# MySQL 中有哪些锁类型?

8 min read
Table of Contents

MySQL 中有哪些锁类型?

MySQL 锁类型详解

【核心定义】

MySQL 中的锁是数据库管理系统用于协调多个会话并发访问同一数据资源,保证数据一致性和完整性的核心机制。

【关键要点】

  1. 按锁的粒度划分

    • 表级锁:锁定整张表,开销小,加锁快,但并发度最低。MyISAM 引擎主要使用表锁。
    • 行级锁:锁定数据行,开销大,加锁慢,但并发度高。InnoDB 引擎支持。
    • 页级锁:锁定数据页(一组行),开销和并发度介于表锁和行锁之间。Berkeley DB 引擎使用。
  2. 按锁的兼容性划分

    • 共享锁(S 锁):允许其他会话读取被锁资源,但禁止写入。SELECT ... LOCK IN SHARE MODE 语句会加 S 锁。
    • 排他锁(X 锁):禁止其他会话对锁定的资源进行任何读写操作。SELECT ... FOR UPDATE、UPDATE、DELETE、INSERT 语句通常会加 X 锁。
  3. 按锁的实现方式划分(InnoDB 特有)

    • 记录锁(Record Lock):锁定索引中的一条具体记录。
    • 间隙锁(Gap Lock):锁定一个索引范围,但不包括记录本身。用于防止幻读。
    • 临键锁(Next-Key Lock):记录锁和间隙锁的组合,锁定一个左开右闭的区间。是 InnoDB 默认的行锁算法。
    • 插入意向锁(Insert Intention Lock):一种特殊的间隙锁,表示有事务想在某个间隙中插入新记录,多个事务只要插入位置不冲突,可以同时持有。
  4. 按锁的意图划分

    • 意向共享锁(IS 锁):事务打算给数据行加共享锁前,必须先取得该表的 IS 锁。
    • 意向排他锁(IX 锁):事务打算给数据行加排他锁前,必须先取得该表的 IX 锁。
    • 作用:意向锁是表级锁,用于快速判断表中是否有行被锁定,避免为了检查每行是否有锁而遍历所有行,从而提升效率。

【深度推导/细节】

1. InnoDB 行锁的加锁规则与幻读解决

核心矛盾:在可重复读(RR)隔离级别下,如何防止幻读?

  • Step 1:对于等值查询(如 WHERE id = 5),如果使用唯一索引,InnoDB 只会对 id=5 这条记录加记录锁。
  • Step 2:对于等值查询,如果使用非唯一索引或没有命中记录,InnoDB 会对扫描到的间隙加间隙锁。例如,表中有 id=3 和 id=7 的记录,执行 SELECT * FROM t WHERE id = 5 FOR UPDATE,由于 id=5 不存在,会锁定 (3, 7) 这个间隙,防止其他事务插入 id=5 的记录。
  • Step 3:对于范围查询(如 WHERE id > 5 AND id < 10),InnoDB 会使用临键锁,锁定满足条件的所有记录及其周围的间隙。这是防止幻读的关键。

2. 锁的冲突矩阵与兼容性

请求锁类型当前持有 S当前持有 X当前持有 IS当前持有 IX
请求 S兼容冲突兼容冲突
请求 X冲突冲突冲突冲突
请求 IS兼容冲突兼容兼容
请求 IX冲突冲突兼容兼容
表锁与行锁的意向锁通过此矩阵高效协同。

【关联/对比】

  1. MyISAM vs InnoDB 锁机制

    • MyISAM:仅支持表级锁。读操作加共享表锁,写操作加排他表锁。并发写性能差,适合读多写少场景。
    • InnoDB:支持行级锁和表级意向锁。通过 MVCC 支持非锁定读,大大提升了并发性能,是现代 OLTP 应用的首选。
  2. 乐观锁 vs 悲观锁(应用层概念)

    • 悲观锁:上述数据库锁机制即是悲观锁,认为并发冲突总会发生,故先加锁再操作。SELECT ... FOR UPDATE 是典型实现。
    • 乐观锁:认为冲突不常发生,在更新时通过版本号或时间戳判断数据是否被修改。通常通过应用逻辑实现(如 UPDATE ... SET ... WHERE version = old_version)。

【面试官追问】

  1. 什么时候行锁会升级为表锁?

    • 当 SQL 语句无法有效使用索引时,InnoDB 会进行全表扫描,此时行锁会升级为表锁,严重影响并发。
    • 这也是为什么强调查询必须走索引的重要原因之一。
  2. 如何查看 MySQL 中的锁信息?

    • 使用 SHOW ENGINE INNODB STATUS\G 命令,查看 LATEST DETECTED DEADLOCK 和 TRANSACTIONS 部分。
    • 查询 information_schema 库中的 INNODB_LOCKS、INNODB_LOCK_WAITS、INNODB_TRX 表(MySQL 5.7及之前)。
    • MySQL 8.0+:使用 performance_schema 库中的 data_locks 和 data_lock_waits 表,提供了更清晰、更强大的锁监控能力。
  3. 什么是死锁?MySQL 如何检测和处理死锁?

    • 死锁:两个或以上事务互相等待对方释放锁,形成循环等待。
    • 处理机制:InnoDB 有死锁检测机制(默认开启)。当检测到死锁时,会选择一个回滚代价最小的事务(通常涉及行数最少的事务)进行回滚,并释放其锁,让其他事务得以继续。
  4. 自增锁(AUTO-INC Lock)是什么?

    • 这是一种特殊的表级锁,用于在事务中为 AUTO_INCREMENT 列生成连续的自增值。
    • 模式:
      • innodb_autoinc_lock_mode = 0(传统模式):所有 INSERT 语句都会获得特殊的表级 AUTO-INC 锁,语句结束后释放,保证主从复制安全性,但并发性差。
      • innodb_autoinc_lock_mode = 1(连续模式,默认):对于能预先确定插入行数的语句(如单条 INSERT),使用轻量级的互斥量,避免表锁;对于不确定的语句(如 INSERT ... SELECT),仍使用 AUTO-INC 锁。
      • innodb_autoinc_lock_mode = 2(交错模式):所有 INSERT 语句都使用轻量级互斥量,自增值不保证连续,只保证递增,性能最高,但可能对基于语句的复制(SBR)不安全。

【版本差异与最佳实践】

  • MySQL 5.7 及之前:information_schema 中的锁信息表是监控锁的主要手段。
  • MySQL 8.0+:performance_schema 全面替代,提供了更精细、更强大的可观测性。
  • 最佳实践:
    1. 尽量使用 InnoDB 引擎,并利用其行锁特性。
    2. 确保事务中的 SQL 语句都使用索引,避免锁升级。
    3. 保持事务简短,尽快提交或回滚,减少锁的持有时间。
    4. 在业务设计上,可以通过“尝试-回退”机制或队列来减少高并发下的锁竞争。
My avatar

文章看完了,不打算去别的篇章转转吗?👀 感谢你的耐心阅读!如果你有问题想探讨,或者发现了文中的 Bug,请务必通过页脚的链接联系我


More Posts

# Netty 是如何解决粘包和拆包问题的?

8 min read

Netty 通过内置的多种解码器(Decoder),基于 TCP 流式传输的特性,在应用层按照预设的协议规则对字节流进行边界识别和消息重组,从而解决粘包(多个消息粘在一起)和拆包(一个消息被拆成多个包)的问题。

Read

Comments