Skip to content

锁详解

一、按锁的粒度划分(最核心分类)

锁的粒度决定了锁定的范围,从大到小依次为:全局锁表级锁行级锁页级锁(几乎不用)。

1. 全局锁(Global Lock)

定义:锁定整个MySQL实例,所有数据库的所有表均变为只读状态(无法执行INSERT、UPDATE、DELETE、ALTER TABLE等写操作)。
作用:用于全库逻辑备份(如mysqldump),确保备份期间数据一致性(避免备份过程中数据被修改)。
常见方式

  • FLUSH TABLES WITH READ LOCK (FTWRL):强制关闭所有打开的表,并对所有表加读锁,直到执行UNLOCK TABLES释放。
    • 优点:比SET GLOBAL read_only=1更安全(read_only对超级用户无效,FTWRL对所有用户有效)。
  • SET GLOBAL read_only = 1:设置全局只读,但超级用户仍可写,一般不用于备份。
    注意:全局锁会阻塞所有写操作,对业务影响大,建议用InnoDB的--single-transaction选项(通过MVCC实现一致性备份,无需全局锁)。

2. 表级锁(Table-Level Lock)

定义:锁定整个表,是MyISAM存储引擎的默认锁机制(InnoDB也支持,但更常用行级锁)。
分类

(1)表共享读锁(Table Shared Read Lock, S锁

  • 作用:允许多个会话同时持有,允许读禁止写(包括当前会话)。
  • 语法LOCK TABLES t READ;
  • 兼容性:与其他S锁兼容,与X锁冲突。

(2)表排他写锁(Table Exclusive Write Lock, X锁

  • 作用:仅允许一个会话持有,允许读写禁止其他会话读或写
  • 语法LOCK TABLES t WRITE;
  • 兼容性:与所有锁冲突(包括自身)。

(3)意向锁(Intention Lock)

  • 定义:InnoDB为协调表级锁与行级锁而引入的“标记锁”,自动添加(无需手动操作)。
  • 作用:当需要加表级锁时,无需扫描全表检查每行的锁状态,只需检查意向锁即可,减少锁检查的开销。
  • 分类
    • 意向共享锁(Intention Shared Lock, IS锁):加行S锁前自动添加(如SELECT ... FOR SHARE)。
    • 意向排他锁(Intention Exclusive Lock, IX锁):加行X锁前自动添加(如INSERT/UPDATE/DELETE)。
  • 兼容性(关键):
    请求锁\持有锁IS锁IX锁S锁X锁
    IS锁兼容兼容兼容冲突
    IX锁兼容兼容兼容冲突
    S锁兼容兼容兼容冲突
    X锁冲突冲突冲突冲突
    例如:若会话A持有表的IX锁(行X锁的意向),会话B要加表S锁(读全表),允许(IX与S兼容);但会话B要加表X锁(写全表),禁止(IX与X冲突)。

(4)元数据锁(Metadata Lock, MDL

  • 定义:保护表的元数据(如表结构、列定义),自动添加(无需手动操作)。
  • 作用:防止并发修改表结构与数据操作冲突(如一个会话在SELECT,另一个会话在ALTER TABLE,会阻塞直到SELECT完成)。
  • 分类
    • MDL读锁(共享):执行SELECTSHOW TABLES等操作时添加,允许多个会话持有。
    • MDL写锁(排他):执行ALTER TABLEDROP TABLERENAME TABLE等操作时添加,仅允许一个会话持有。
  • 注意:MDL锁的释放时机取决于事务提交(如BEGIN; SELECT * FROM t;会持有MDL读锁直到COMMIT),长时间未提交的事务会阻塞表结构修改。

(5)自增锁(AUTO-INC Lock)

  • 定义:用于自增列(AUTO_INCREMENT)的生成,确保自增值的唯一性连续性
  • 作用:插入数据时,对自增列加锁,防止多个会话生成重复的自增值。
  • 模式(由innodb_autoinc_lock_mode参数控制):
    • 0(传统模式):表级锁,锁定整个表直到插入完成,自增值连续,但性能低(适合批量插入)。
    • 1(连续模式,默认):轻量级锁,对于简单插入(如INSERT INTO t VALUES (NULL))用行级锁,自增值连续;对于批量插入(如INSERT INTO t SELECT ...)用表级锁。
    • 2(交错模式):行级锁,允许并发插入,自增值可能不连续(如会话A插入id=1,会话B插入id=3),但性能最高(适合高并发插入)。

(6)表空间锁(Tablespace Lock)

  • 定义:锁定表空间文件(如.ibd文件),用于ALTER TABLE ... DISCARD TABLESPACEIMPORT TABLESPACE等操作,防止其他会话访问该表空间。

3. 行级锁(Row-Level Lock)

定义:锁定单行记录,是InnoDB存储引擎的核心特性(MyISAM不支持),基于索引实现(无索引时会升级为表级锁)。
作用:最小化锁冲突,提高并发性能(如多个会话可同时修改不同行)。
分类

(1)记录锁(Record Lock)

  • 定义:锁定单行具体记录,基于主键或唯一索引(若用非唯一索引,会升级为临键锁)。
  • 语法SELECT * FROM t WHERE id=1 FOR UPDATE;(加行X锁);SELECT * FROM t WHERE id=1 FOR SHARE;(加行S锁)。
  • 作用:防止其他会话修改或删除该记录。

(2)间隙锁(Gap Lock)

  • 定义:锁定索引之间的间隙(不包括记录本身),用于防止幻读(Repeatable Read(RR)隔离级别下有效)。
  • 示例:若表t的id索引值为1、3、5,间隙为(-∞,1)(1,3)(3,5)(5,+∞)。执行SELECT * FROM t WHERE id BETWEEN 1 AND 5 FOR UPDATE;会锁定这些间隙,防止插入id=2、4等数据。
  • 注意:间隙锁仅在RR级别下生效(Read Committed(RC)级别下无间隙锁)。

(3)临键锁(Next-Key Lock)

  • 定义记录锁+间隙锁(锁定当前记录及前面的间隙),是InnoDBRR级别下的默认行锁方式
  • 示例:若id索引值为1、3、5,临键锁为(-∞,1](1,3](3,5](5,+∞)]表示包含当前记录)。执行SELECT * FROM t WHERE id=3 FOR UPDATE;会锁定(1,3]间隙,防止插入id=2的数据。
  • 作用:彻底解决幻读(通过锁定记录及间隙,避免插入新数据)。

(4)插入意向锁(Insert Intention Lock)

  • 定义:插入数据时,对目标间隙加的意向锁(属于间隙锁的子类)。
  • 作用:允许并发插入不同位置的记录,减少锁冲突。
  • 示例:会话A要插入id=2(间隙(1,3)),会话B要插入id=4(间隙(3,5)),两者的插入意向锁兼容,可同时执行;但如果两者都插入id=2,会因记录锁冲突而阻塞。

(5)谓词锁(Predicate Lock)

  • 定义:在Serializable隔离级别下,锁定满足某个条件的所有行(如SELECT * FROM t WHERE name LIKE 'a%' FOR UPDATE;),用于防止幻读。
  • 注意:InnoDB并未直接实现谓词锁,而是用临键锁覆盖谓词条件的范围(如name LIKE 'a%'会锁定所有name以a开头的行及间隙)。

4. 页级锁(Page-Level Lock)

定义:锁定一页数据(MySQL默认页大小为16KB),是BDB存储引擎的默认锁机制(几乎不用)。
特点:粒度介于表级锁与行级锁之间,并发性能优于表级锁,但低于行级锁。

二、按锁的类型划分

1. 共享锁(Shared Lock, S锁

  • 定义:允许多个会话同时持有,允许读禁止写
  • 语法SELECT ... FOR SHARE;(InnoDB);LOCK TABLES t READ;(表级)。
  • 兼容性:与S锁兼容,与X锁冲突。

2. 排他锁(Exclusive Lock, X锁

  • 定义:仅允许一个会话持有,允许读写禁止其他会话读或写
  • 语法SELECT ... FOR UPDATE;(InnoDB);LOCK TABLES t WRITE;(表级);INSERT/UPDATE/DELETE(自动加X锁)。
  • 兼容性:与所有锁冲突。

三、按锁的实现方式划分

1. 悲观锁(Pessimistic Lock)

  • 定义:假设并发冲突一定会发生,提前加锁(如SELECT ... FOR UPDATE),阻塞其他会话的操作。
  • 适用场景:并发冲突频繁(如秒杀、库存扣减)。
  • InnoDB支持:默认使用悲观锁(如INSERT/UPDATE/DELETE自动加X锁)。

2. 乐观锁(Optimistic Lock)

  • 定义:假设并发冲突不会发生,不提前加锁,而是通过版本控制(如version列)或时间戳判断数据是否被修改,若修改则回滚。
  • 语法SELECT * FROM t WHERE id=1;(获取版本号);UPDATE t SET ..., version=version+1 WHERE id=1 AND version=old_version;(验证版本号)。
  • 适用场景:并发冲突少(如用户信息修改)。
  • InnoDB支持:通过**MVCC(多版本并发控制)**实现(普通SELECT语句用MVCC,无需加锁)。

四、其他重要概念

1. 锁兼容性矩阵(核心参考)

请求锁\持有锁S锁(表/行)X锁(表/行)IS锁IX锁
S锁(表/行)兼容冲突兼容兼容
X锁(表/行)冲突冲突冲突冲突
IS锁兼容冲突兼容兼容
IX锁兼容冲突兼容兼容

2. 锁升级(Lock Escalation)

  • 定义:当行级锁的数量超过阈值(如innodb_max_row_lock_count),InnoDB会将行级锁升级为表级锁,减少锁结构的内存占用。
  • 影响:升级后并发性能下降(建议优化索引,减少行锁数量)。

3. 死锁(Deadlock)

  • 定义:两个或多个会话互相等待对方的锁,导致无法继续(如会话A持有id=1的X锁,要获取id=2的X锁;会话B持有id=2的X锁,要获取id=1的X锁)。
  • InnoDB处理:自动检测死锁(通过innodb_deadlock_detect参数控制),回滚事务较小的会话(减少损失)。
  • 避免方式:统一锁顺序(如先锁id=1,再锁id=2)、减少事务范围、使用SELECT ... FOR UPDATE明确加锁。

五、不同存储引擎的锁支持

存储引擎全局锁表级锁行级锁页级锁意向锁MDL锁自增锁
InnoDB支持支持支持不支持支持支持支持
MyISAM支持支持不支持不支持不支持支持支持
BDB支持支持不支持支持不支持支持支持

总结

MySQL的锁机制是并发控制的核心,不同粒度的锁适用于不同场景:

  • 全局锁:用于全库备份(尽量用InnoDB的--single-transaction替代)。
  • 表级锁:适用于MyISAM存储引擎(并发性能低),或需要锁定全表的操作(如ALTER TABLE)。
  • 行级锁:InnoDB的核心优势(并发性能高),适用于高并发场景(如电商订单、用户信息修改)。
  • 意向锁:协调表级锁与行级锁的关键,避免全表扫描。
  • MDL锁:保护表结构,防止并发修改冲突。

理解锁的类型、兼容性和使用场景,是优化MySQL并发性能、避免死锁的关键。