[TOC]

锁机制

计算机协调 多个进程或线程 并发访问某一资源的机制。

锁的类型及其特点

开销、加锁、颗粒度、冲突、并发等角度分析

表级锁(table-level locking) 开销大,加锁快,不会出现死锁,颗粒度大,锁冲突概率高,并发度小;

页面锁(page-level locking) 开销、加锁效率、颗粒度介于表锁和行锁之间,会出现死锁,并发度一般;

行级锁(row-level locking) 开销大,加速慢,会出现死锁,颗粒度小,锁冲突概率小,并发度高;

锁的类型应用场景

表锁 - 适合 以查询为主,只有少量按索引条件更新数据的应用,如web应用;

行锁 - 适合 大量按索引条件并发 更新少量不同数据,同时又有并发查询的应用,如在线事务处理系统;

锁的支持度及情况

MyISAM 只支持 表锁

InnoDB 默认支持 行锁,也支持表锁

1
2
-- 查询表锁争用情况
show status like 'table%'; # Table_locks_waited 大则表锁争用情况严重

锁的维度划分

共享锁(S) - 读锁

排他锁(X) - 写锁

意向共享锁(IS)

意向排他锁(IX)

间隙锁

死锁

死锁的产生原因

  1. 系统资源不足
  2. 进程运行 推进的顺序/速度 不同
  3. 资源分配不当

死锁的必要条件

  1. 互斥 一个资源每次只能被一个进程使用
  2. 请求与保持 一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不可剥夺 进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待 若干进程之间形成一种头尾相接的循环等待资源关系

避免死锁

  • 合理的设计索引,区分度高的列放到组合索引前面,使业务 SQL 尽可能通过索引定位更少的行,减少锁竞争。

  • 调整业务逻辑 SQL 执行顺序, 避免 update/delete 长时间持有锁的 SQL 在事务前面。

  • 避免大事务,将大事务拆成多个小事务

  • 以固定的顺序访问表和行。

    比如两个更新数据的事务,

    事务 A 更新数据的顺序为 1,2;

    事务 B 更新数据的顺序为 2,1。

    这样更可能会造成死锁。

  • 在并发比较高的系统中,不要显式加锁,特别是是在事务里显式加锁。

    如 select … for update 语句,

    如果是在事务里(运行了 start transaction 或设置了autocommit 等于0),

    那么就会锁定所查找到的记录。

  • 尽量用主键/索引去查找记录

  • 优化 SQL 和表设计,减少同时占用太多资源的情况。

    比如说,避免多个表join,将复杂 SQL 分解为多个简单的 SQL。

死锁的解除与预防

打破必要条件之一即可解锁,预防必要条件即可预防死锁。

  1. 按同一顺序访问对象
  2. 避免事务中的用户交互
  3. 保持事务简短并在一个批处理中
  4. 使用低隔离级别
  5. 使用绑定连接

总结

维度|锁类型表级锁页面锁行级锁
内存开销
加锁效率
颗粒度
锁冲突概率
是否会死锁
并发度一般
应用特点查询&少量索引条件的更新-并发查询&大量索引条件的不同数据更新
应用场景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