首页 >> 基础教程

mysql行锁

      MySQL 的行锁(Row-Level Locking)是 InnoDB 存储引擎实现的一种细粒度锁机制,它允许事务只锁定需要修改或访问的特定行,而不是锁定整个表或页。这大大提高了数据库的并发性能,允许多个事务同时读写不同的行(只要它们不冲突)。

  1. 锁的类型:

    1. 记录锁(Record Locks): 最基本的行锁,直接锁定索引中的一条记录。当事务通过索引访问行(例如 SELECT ... FOR UPDATEUPDATEDELETE)且条件精确匹配到唯一索引时,通常加的就是记录锁。

    2. 间隙锁(Gap Locks): 锁定索引记录之间的“间隙”,防止其他事务在这个间隙中插入新记录。这是 InnoDB 解决幻读问题(Phantom Read)的关键机制之一。间隙锁主要作用于可重复读(REPEATABLE READ)隔离级别。

      1. 例如:如果表中有 id 为 5 和 10 的记录,事务 A 执行 SELECT * FROM t WHERE id > 5 AND id < 10 FOR UPDATE,间隙锁会锁定 (5, 10) 这个开区间,阻止其他事务插入 id=6,7,8,9 的记录。

    3. 临键锁(Next-Key Locks): InnoDB 默认的行锁模式。它是 记录锁 + 该记录前面的间隙锁 的组合。临键锁锁定一个索引记录以及该记录之前的间隙。

      1. 例如:如果表中有 id 为 5 和 10 的记录,事务 A 执行 SELECT * FROM t WHERE id = 10 FOR UPDATE,临键锁会锁定 (5, 10] 这个区间(即间隙 (5,10) + 记录 10),阻止其他事务插入 id=6,7,8,9 的记录或修改/删除 id=10 的记录。

    4. 插入意向锁(Insert Intention Locks): 一种特殊的间隙锁,表示一个事务打算在某个间隙中插入一条新记录。多个事务可以在同一个间隙上持有不冲突的插入意向锁(只要它们插入的具体位置不同)。插入意向锁会等待已有的间隙锁(Gap Locks)或临键锁(Next-Key Locks)释放

  2. 锁的兼容性:

    1. 共享锁(S Lock / Read Lock): 允许其他事务读取被锁定的行,但不允许获取独占锁或修改这些行。通常由 SELECT ... LOCK IN SHARE MODE 显式获取,或在某些情况下由隔离级别隐式添加。

    2. 独占锁(X Lock / Write Lock): 阻止其他事务获取任何类型的锁(共享或独占)或修改被锁定的行。由 INSERTUPDATEDELETE 语句以及 SELECT ... FOR UPDATE 隐式或显式获取。

    3. 兼容性矩阵:

      当前持有 / 请求X (独占锁)S (共享锁)
      X (独占锁)冲突冲突
      S (共享锁)冲突兼容
      1. 一个行上只能有一个独占锁。

      2. 一个行上可以有多个共享锁(只要没有独占锁)。

      3. 共享锁与独占锁互斥。

      4. 间隙锁之间通常是兼容的(因为它们锁的是“空”的区域),除非它们意图锁定的范围完全冲突(非常罕见)。间隙锁与插入意向锁是冲突的。

  3. 何时加锁:

    1. 行锁通常在语句执行过程中,当需要访问或修改某一行时,按需加锁(Locking as needed)。

    2. 事务提交(COMMIT)或回滚(ROLLBACK)时,会释放该事务持有的所有锁。

  4. 索引对锁的影响:

    1. InnoDB 的行锁是加在索引记录上的。即使表没有显式定义索引,InnoDB 也会为每行生成一个隐藏的聚簇索引(Clustered Index)。

    2. 锁的范围取决于使用的索引:

      1. 如果语句使用了主键索引(Primary Key) 或 唯一索引(Unique Index) 进行精确匹配(WHERE id = 10),InnoDB 只会锁定找到的那条具体的行(记录锁)。

      2. 如果语句使用了非唯一索引(Non-Unique Index) 或 范围条件(WHERE id > 10,InnoDB 可能不仅锁定满足条件的行,还会锁定这些行周围的间隙(间隙锁或临键锁),以防止幻读。

      3. 如果语句没有使用索引(Full Table Scan),InnoDB 无法使用行锁!它会退而求其次,锁定整个表(表锁)或扫描到的所有行(实际上是锁住索引的所有记录和间隙),这会导致严重的并发性能问题。务必确保 WHERE 条件能有效利用索引!

  5. 隔离级别的影响:

    1. 读已提交(READ COMMITTED):

      1. 通常只使用记录锁。

      2. 语句执行期间会加锁,但语句执行完可能会释放不满足条件的行上的锁(具体实现依赖)。

      3. 不使用间隙锁(除了外键约束检查和重复键检查),因此可能发生幻读

      4. 半一致性读(semi-consistent read)可能减少锁冲突。

    2. 可重复读(REPEATABLE READ) - InnoDB 默认:

      1. 使用记录锁、间隙锁和临键锁(Next-Key Locks)。

      2. 锁会一直持有到事务结束。

      3. 通过间隙锁和临键锁防止幻读(在同一个事务内重复执行相同的查询,保证得到相同的结果集)。

    3. 串行化(SERIALIZABLE):

      1. 所有普通的 SELECT 语句都会自动转换为 SELECT ... LOCK IN SHARE MODE,对读取的行加共享锁。这可能导致更多的锁冲突和性能下降。

      2. 同样使用记录锁、间隙锁、临键锁来保证最高的隔离性。

  6. 死锁(Deadlock):

    1. 行锁机制下,两个或多个事务相互等待对方释放锁时,就可能发生死锁。

    2. 示例:

      1. 事务 A:锁定了行 1,然后尝试锁定行 2。

      2. 事务 B:锁定了行 2,然后尝试锁定行 1。

      3. 此时 A 等待 B 释放行 2,B 等待 A 释放行 1,形成死循环。

    3. InnoDB 的处理:

      1. InnoDB 有死锁检测机制(默认开启)。

      2. 一旦检测到死锁,它会选择一个“代价较小”的事务(通常涉及修改行数较少的)进行回滚(ROLLBACK),并返回 1213 (ER_LOCK_DEADLOCK) 错误。另一个事务则可以继续执行。

    4. 避免死锁策略:

      1. 尽量按相同顺序访问表和行。

      2. 保持事务短小精悍,尽快提交。

      3. 在事务中一次性锁定所有需要的资源(如果可行)。

      4. 合理使用索引,避免锁升级。

      5. 如果业务允许,可以降低隔离级别(如使用 READ COMMITTED)。

      6. 使用 SELECT ... FOR UPDATE NOWAIT 或 SELECT ... FOR UPDATE SKIP LOCKED(MySQL 8.0+)避免等待

  7. 查看锁信息:

    1. SHOW ENGINE INNODB STATUS;: 查看 LATEST DETECTED DEADLOCK 部分(最近死锁信息)和 TRANSACTIONS 部分(当前事务和锁等待信息)。需要 PROCESS 权限。

    2. INFORMATION_SCHEMA 表:

      1. INNODB_TRX: 当前运行的事务信息。

      2. INNODB_LOCKS: 当前事务持有的锁和正在等待的锁信息(MySQL 5.7 及更早)。

      3. INNODB_LOCK_WAITS: 锁等待关系(MySQL 5.7 及更早)。

      4. performance_schema.data_locks: MySQL 8.0+ 替代 INNODB_LOCKS,提供更详细的锁信息。

      5. performance_schema.data_lock_waits: MySQL 8.0+ 替代 INNODB_LOCK_WAITS,提供锁等待信息。

    3. sys.innodb_lock_waits (MySQL 5.6+, sys schema): 提供更易读的锁等待视图。

       MySQL InnoDB 的行锁是支撑其高并发事务处理能力的核心机制。理解不同类型的行锁(记录锁、间隙锁、临键锁、插入意向锁)、它们的兼容性、索引如何影响锁范围、以及不同隔离级别的行为差异,对于设计高性能、高并发的数据库应用至关重要。合理使用索引、优化事务设计、选择合适的隔离级别以及监控锁状态是避免锁竞争和死锁的关键。


最新文章
mysql分页问题2025-08-04
千万数据先insert和先建索引哪个快2025-08-04
MySQL 中大小表关联查询如何优化2025-08-04
sql技巧-每个班年龄排前两名的人2025-08-03
MySQL 导致 cpu 飙升的话,要怎么处理呢?2025-07-29
MySQL 中为千万级大表添加字段2025-07-29
mysql中百万级别以上的数据如何删除2025-07-29
分库分表带来的问题2025-07-29
mysql中常用的分库分表中间件有哪些2025-07-29
mysql不停机扩容2025-07-29
备案号:蜀ICP备2023042032号-1