Appearance
MySQL
索引
存储引擎
事物的隔离级别
事务隔离级别影响锁的行为。常见的隔离级别包括:
- 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;这就等于告诉数据库:
“这行我暂时要用,谁也别改,谁也别读(在某些隔离级别下)。”