MySQL - 锁
[TOC]
锁机制
计算机协调 多个进程或线程 并发访问某一资源的机制。
锁的类型及其特点
开销、加锁、颗粒度、冲突、并发等角度分析
表级锁(table-level locking) 开销大,加锁快,不会出现死锁,颗粒度大,锁冲突概率高,并发度小;
页面锁(page-level locking) 开销、加锁效率、颗粒度介于表锁和行锁之间,会出现死锁,并发度一般;
行级锁(row-level locking) 开销大,加速慢,会出现死锁,颗粒度小,锁冲突概率小,并发度高;
锁的类型应用场景
表锁 - 适合 以查询为主,只有少量按索引条件更新数据的应用,如web应用;
行锁 - 适合 大量按索引条件并发 更新少量不同数据,同时又有并发查询的应用,如在线事务处理系统;
锁的支持度及情况
MyISAM 只支持 表锁
InnoDB 默认支持 行锁,也支持表锁
1 | -- 查询表锁争用情况 |
锁的维度划分
共享锁(S) - 读锁
排他锁(X) - 写锁
意向共享锁(IS)
意向排他锁(IX)
间隙锁
死锁
死锁的产生原因
- 系统资源不足
- 进程运行 推进的顺序/速度 不同
- 资源分配不当
死锁的必要条件
- 互斥 一个资源每次只能被一个进程使用
- 请求与保持 一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不可剥夺 进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待 若干进程之间形成一种头尾相接的循环等待资源关系
避免死锁
合理的设计索引,区分度高的列放到组合索引前面,使业务 SQL 尽可能通过索引定位更少的行,减少锁竞争。
调整业务逻辑 SQL 执行顺序, 避免 update/delete 长时间持有锁的 SQL 在事务前面。
避免大事务,将大事务拆成多个小事务
以固定的顺序访问表和行。
比如两个更新数据的事务,
事务 A 更新数据的顺序为 1,2;
事务 B 更新数据的顺序为 2,1。
这样更可能会造成死锁。
在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。
如 select … for update 语句,
如果是在事务里(运行了 start transaction 或设置了autocommit 等于0),
那么就会锁定所查找到的记录。
尽量用主键/索引去查找记录
优化 SQL 和表设计,减少同时占用太多资源的情况。
比如说,避免多个表join,将复杂 SQL 分解为多个简单的 SQL。
死锁的解除与预防
打破必要条件之一即可解锁,预防必要条件即可预防死锁。
- 按同一顺序访问对象
- 避免事务中的用户交互
- 保持事务简短并在一个批处理中
- 使用低隔离级别
- 使用绑定连接
总结
| 维度|锁类型 | 表级锁 | 页面锁 | 行级锁 |
|---|---|---|---|
| 内存开销 | 大 | 中 | 大 |
| 加锁效率 | 快 | 中 | 慢 |
| 颗粒度 | 大 | 中 | 小 |
| 锁冲突概率 | 高 | 中 | 低 |
| 是否会死锁 | 否 | 是 | 是 |
| 并发度 | 小 | 一般 | 高 |
| 应用特点 | 查询&少量索引条件的更新 | - | 并发查询&大量索引条件的不同数据更新 |
| 应用场景 | Web | - | 在线事务处理系统 |
| 存储引擎 | MyISAM、InnoDB | - | InnoDB |
| 锁争用参数 | show status like 'table%'Table_locks_waited | - | show status like 'InnoDB_row_lock%';InnoDB_row_lock_waits InnoDB_row_lock_time_avg |





