Skip to content

MySQL

MySQL 8.4 Reference Manual

索引

存储引擎

事物的隔离级别

事务隔离级别影响锁的行为。常见的隔离级别包括:

  • READ UNCOMMITTED:允许脏读(Dirty Read),不加锁。
  • READ COMMITTED:允许不可重复读(Non-Repeatable Read),读时不加锁,写时锁定当前读到的行。
  • REPEATABLE READ:防止不可重复读,但允许幻读(Phantom Read),读时使用共享锁,写时使用排他锁。
  • SERIALIZABLE:完全隔离,防止所有并发问题,读写操作都使用锁。

InnoDB

InnoDB 是 MySQL 的默认存储引擎,提供了许多高级特性:

  • 行级锁定:提高并发性能。
  • MVCC(多版本并发控制):通过保存多个数据版本来实现并发控制,提高读性能。
  • 事务支持:通过使用 undo 日志和 redo 日志确保事务的 ACID 特性。
  • WAL(Write-Ahead Logging):在修改数据之前先记录日志。

MySQL 连接池调优

客户端连接池端

MaxIdleConnections 最大空闲连接数

连接池里最多能“躺着歇着”的连接数,这些连接暂时不用,但留着不关,等下次请求再复用,性能蹭蹭蹭

  • 太小 → 经常得新建连接(慢);太大 → 占内存 + 数据库资源浪费
  • 流量不稳定,有突发高并发 MaxIdleConns 稍大点(10~50),避免抖动

MaxOpenConnections 最大打开连接数

数据库允许同时处理多少个连接,包括正在用的 + 躺着歇着的

  • 太小 → 高并发请求会排队甚至阻塞
  • 太大 → DB 扛不住,连接过多会卡死甚至崩掉

MaxConnectionLifeTime 单连接生命周期限制

一个连接最多能活多久,不管它有没有在用,避免连接“陈年老爹”堆积在池子里,可能被 DB 端 kill,导致后续操作报错

  • 推荐设置在 小于数据库 wait_timeout(非交互式连接超时时间,如应用连接池) 之前(MySQL 默认 8 小时)
  • MaxConnectionLifeTime 会 等连接“归还”到连接池时,才判断是否“超龄”,不会暴力中断正在执行的 SQL 操作

MaxConnectionIdleTime 空闲连接最多能闲多久

如果连接长期不用,还留在池子里,会浪费资源,设置这个时间能主动清理多余连接,防止连接池过大

go
sqlDB.SetConnMaxIdleTime(10 * time.Minute) // 闲置超过 10 分钟的连接会被关闭
  • 服务长时间闲置、夜间无流量 设置 MaxConnectionIdleTime 为 10~15 分钟,释放资源

MySQL 服务端相关参数

wait_timeout

一个非交互连接(如后端服务)在 MySQL 那边闲多久就被踢掉

要点:如果你的应用连接池里连着老连接,但 MySQL 早就把它踢了,你一用就报错 invalid connection;所以要让你的连接池里的 ConnMaxLifetime(活多久)比这个时间更短,否则你以为有连接,其实是尸体

查看 MySQL 的 wait_timeout:

sql
SHOW GLOBAL VARIABLES LIKE 'wait_timeout';

interactive_timeout

wait_timeout 类似,只不过它是给“交互式连接”用的,比如你用 MySQL 客户端手动连上去那种

要点:一般不影响后端程序,属于 CLI/GUI 工具连接;大多数情况下,只需关注 wait_timeout 即可


max_connections

MySQL 同时最多允许多少个连接连进来(不管是活跃还是空闲),即最大连接数上限

要点:

  • 一定要结合业务并发量 + 应用连接池上限来设定,不能比实际需求少;
  • 超出这个值,程序会报 too many connections;
  • 但也不能一味加大,连接是占资源的,每个连接消耗内存、线程上下文等。

查看 MySQL 的 max_connections:

sql
SHOW VARIABLES LIKE 'max_connections';

磁盘寻道和磁盘带宽

  • 磁盘寻道” = 读一小块数据却到处跳来跳去,效率低
  • 磁盘带宽” = 扫一大片数据的时候读得不够快,拖慢整体速度

OLTP(高并发、小事务)更怕“磁盘寻道”;OLAP(大查询、批处理)更怕“磁盘带宽”


磁盘寻道(seek time)

磁盘寻道就像是你用机械硬盘(HDD)看小说,但小说不是按页码排好,而是:

“第一页在书头,第二页在书尾,第三页又跑回中间”

那你的磁头就要不停地 “转动 + 跳位置” —— 这就是“寻道”

在 OLTP 场景(比如用户下订单、读一条用户记录)时,你读取的数据小、随机访问多,寻道成本就成了主要性能杀手

举例说明:

  • 查询订单详情(几十字节的数据) -> 如果在盘上到处飞,性能拉胯
  • 用户频繁访问账户信息、点赞状态(小且频繁)

磁盘带宽(sequential throughput)

磁盘带宽就像是你要把整本小说复印出来,一页页扫过去——这个时候:

“寻不寻道无所谓,只要扫得够快就行”

所以 OLAP(大数据报表分析、大字段聚合)更关心的是:

  • 一次性读取多少数据?
  • 单位时间内能吞多少 MB/s?

举例说明:

  • 一次查 1 年销售数据总和(大表聚合)
  • 数据仓库中做 COUNT、SUM、JOIN 的全表扫描

总结

项目OLTP(事务型)OLAP(分析型)
特征高频、小事务、随机访问低频、大查询、批量顺序访问
性能瓶颈磁盘寻道时间磁盘带宽/吞吐
举例用户下单、查订单、账户状态报表生成、销售总和、批量统计分析
更适合的存储SSD(低寻道延迟)顺序 IO 强的硬盘或列式存储引擎
  • 如果你是搞 OLTP 的,尽量避免磁盘跳来跳去,让热点数据进内存(InnoDB Buffer Pool)
  • 如果你搞 OLAP,优化的是吞吐:数据仓库用列式存储、配 SSD、压缩、并发 scan

InnoDB 事务隔离级别

MVCC 是 InnoDB 实现事务隔离的秘密武器,用版本控制代替锁

事务隔离级别

SQL 标准规定了 4 个隔离级别(由低到高):

隔离级别可能出现的问题性能并发安全性
Read Uncommitted(读未提交)脏读
Read Committed(读已提交)不可重复读中(Oracle 默认)
Repeatable Read(可重复读)幻读中下高(MySQL 默认)
Serializable(可串行化)无(最严格)最强

可能出现的问题:

  • 脏读:我读到了别人还没提交的数据。
  • 不可重复读:我前后两次查询结果不一样,中间被别人改了。
  • 幻读:我查的是“符合某条件的所有记录”,下一秒别人插了一条新记录也符合条件——你一查,“哎,怎么多了?幻觉?”

隔离级别和显式锁的区别

隔离级别 = 隐式的规则,是数据库系统帮你在后台搞定的,例如用 MVCC、加锁等手段

显式锁 = 手动加的锁,是你作为开发者主动要求“我锁定这几行,别人别碰”。比如:

sql
SELECT * FROM orders WHERE id = 123 FOR UPDATE;

这就等于告诉数据库:

“这行我暂时要用,谁也别改,谁也别读(在某些隔离级别下)。”