MySQL锁详解
一、锁概述
锁是保证数据一致性和并发控制的重要机制。
锁的作用:
- 保证数据完整性
- 实现事务隔离
- 控制并发访问
二、锁的类型
全局锁
锁定整个数据库实例。
使用场景:
- 全库逻辑备份
- 数据迁移
1 | -- 加全局读锁 |
特点:
- 整个库只读
- 阻塞所有写操作
表级锁
锁定整张表。
表锁
1 | -- 加读锁 |
读锁:可以读,不能写
写锁:独占表,其他会话不能读写
元数据锁
DDL操作自动加MDL锁。
- MDL读锁:DML操作
- MDL写锁:DDL操作
意向锁
InnoDB支持,表示事务意图在表中的行上加锁。
- 意向共享锁(IS)
- 意向排他锁(IX)
作用:表锁和行锁兼容性判断。
行级锁
InnoDB支持的细粒度锁。
Record Lock
锁定单条记录。
1 | SELECT * FROM user WHERE id = 1 FOR UPDATE; |
Gap Lock
锁定记录之间的间隙,防止幻读。
1 | -- 假设id为1,5,10 |
Next-Key Lock
Record Lock + Gap Lock,锁定记录和前面的间隙。
1 | SELECT * FROM user WHERE id >= 5 FOR UPDATE; |
三、共享锁和排他锁
共享锁(S锁)
读锁,多个事务可以同时持有。
1 | -- 加共享锁 |
排他锁(X锁)
写锁,独占资源。
1 | -- 加排他锁 |
兼容性
| S锁 | X锁 | |
|---|---|---|
| S锁 | 兼容 | 冲突 |
| X锁 | 冲突 | 冲突 |
四、行锁的实现
索引条件
行锁必须通过索引实现。
1 | -- 命中索引,行锁 |
锁的加锁范围
唯一索引等值查询:
- 存在记录:加Record Lock
- 不存在记录:加Gap Lock
唯一索引范围查询:
- 加Next-Key Lock
非唯一索引等值查询:
- 加Next-Key Lock
- 加Gap Lock
锁查看
1 | -- 查看当前锁 |
五、锁算法
当前读
读取最新数据,加锁。
1 | SELECT * FROM table WHERE condition FOR UPDATE; |
快照读
读取历史版本,不加锁,通过MVCC实现。
1 | SELECT * FROM table WHERE condition; |
在READ COMMITTED和REPEATABLE READ下,快照读实现不同。
六、死锁
死锁产生
两个事务相互等待对方释放锁。
1 | -- 事务1 |
死锁检测
1 | -- 查看死锁检测配置 |
死锁避免
- 按相同顺序访问资源
- 减少长事务
- 合理设计索引
- 降低隔离级别
七、锁优化
减少锁持有时间
- 事务尽量简短
- 避免在事务中做耗时操作
减少锁范围
- 使用索引,避免表锁
- 避免全表扫描
合理使用锁
- 只读查询不加锁
- 必要时使用FOR UPDATE
监控锁状态
1 | -- 锁等待超时 |
八、总结
MySQL锁要点:
- 全局锁、表锁、行锁
- 共享锁和排他锁
- Record Lock、Gap Lock、Next-Key Lock
- 行锁依赖索引实现
- 死锁检测和避免
合理使用锁机制可以保证数据一致性和并发性能。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 夏天的风吹向哪里!
