阿里云面试官:说说MySQL的锁有那些类型,以及加锁的原理?

阿里云面试官:说说MySQL的锁有那些类型,以及加锁的原理?
最新回答
小糊涂

2026-04-21 20:58:45

MySQL 的锁主要分为表锁和行锁两大类,其加锁原理与存储引擎、隔离级别及索引使用方式密切相关。以下是详细分类及加锁原理说明:

一、表锁

表锁由 MySQL Server 实现,锁定整张表,通常用于 DDL 操作(如 ALTER TABLE)或显式加锁场景。

  • 类型:

    读锁(共享锁,S 锁):多个事务可同时持有,允许读取但禁止修改。

    写锁(排他锁,X 锁):仅允许持有事务修改,其他事务无法读取或修改。

  • 加锁方式:LOCK TABLES user READ; -- 加读锁LOCK TABLES user WRITE; -- 加写锁UNLOCK TABLES; -- 释放锁
  • 特点:

    使用一次性锁技术,加锁后只能操作已锁定的表。

    会话持有其他表锁时执行 LOCK TABLE 或 START TRANSACTION 会释放原有锁。

二、行锁

行锁由存储引擎实现(如 InnoDB),锁定单行或多行数据,支持更高并发。行锁类型与索引使用及隔离级别密切相关。

1. 行锁的模式(兼容性)
  • 读锁(S 锁):共享锁,允许多事务同时读取,但禁止修改。
  • 写锁(X 锁):排他锁,仅允许持有事务读写,其他事务无法操作。
  • 意向锁(表级):

    读意向锁(IS 锁):事务计划加行读锁时,先在表上加 IS 锁。

    写意向锁(IX 锁):事务计划加行写锁时,先在表上加 IX 锁。

    作用:快速判断表是否可加锁,避免遍历所有行锁。

  • 自增锁(AUTO-INC 锁):表锁,用于自增列(AUTO_INCREMENT),确保自增值唯一。MySQL 5.1.22 后引入轻量级锁(innodb_autoinc_lock_mode)优化并发插入。

兼容性矩阵:

  • 意向锁之间互不冲突。
  • S 锁与 S/IS 锁兼容,与其他锁冲突。
  • X 锁与所有锁冲突。
  • AUTO-INC 锁仅与意向锁兼容。
2. 行锁的类型(锁定范围)
  • 记录锁(Record Lock):锁定单条索引记录。

    示例:UPDATE user SET age=10 WHERE id=49 锁定主键 id=49。

    无索引场景:全表扫描时,MySQL 会为所有行加记录锁,过滤后释放不满足条件的锁。

  • 间隙锁(Gap Lock):锁定索引记录之间的间隙(左开右开区间),防止幻读。

    示例:在 RR 隔离级别下,UPDATE user SET age=10 WHERE id=49(记录不存在)会锁定 (30,49) 和 (49,50) 间隙。

    特点:间隙锁之间互不冲突,仅阻止其他事务插入数据。

  • Next-Key Lock:记录锁 + 间隙锁的组合(左开右闭区间),锁定记录及其前间隙。

    示例:索引 15,18,20,30,49,50 的 Next-Key 锁范围为 (-∞,15], (15,18], ..., (50,+∞)。

    作用:解决非唯一索引下的幻读问题。

  • 插入意向锁(Insert Intention Gap Lock):INSERT 操作特有的间隙锁,表示插入意向。

    特点:

    多个事务可在同一间隙加插入意向锁(互不冲突)。

    与间隙锁或 Next-Key 锁冲突,阻止插入。

锁定范围示意图:

三、加锁原理(InnoDB 示例)
  1. 单行更新:

    使用主键索引:直接在主键上加记录锁。

    使用二级索引:先在二级索引上加记录锁,再根据主键回表加记录锁。

    UPDATE user SET age=10 WHERE name='Tom'; -- 在 name='Tom' 和 id=49 上加锁
  2. 范围更新:

    逐行读取满足条件的记录,加记录锁后更新,直至无匹配记录。

    在 RR 隔离级别下,可能加间隙锁或 Next-Key 锁防止幻读。

    UPDATE user SET age=10 WHERE id>49; -- 可能加 Next-Key 锁 (49,50], (50,+∞)
  3. 无索引更新:

    全表扫描并加记录锁,过滤后释放不满足条件的锁,性能极低。

四、死锁场景分析

死锁通常由多个事务竞争不同资源导致,例如:

  • 事务 A:锁定 id=10 的记录锁,尝试锁定 id=20 的间隙锁。
  • 事务 B:锁定 id=20 的间隙锁,尝试锁定 id=10 的记录锁。
  • 结果:两者互相等待对方释放锁,触发 ERROR 1213 (40001)。

规避建议:

  • 保持事务简短,减少锁持有时间。
  • 按固定顺序访问表或行。
  • 合理设计索引,避免全表扫描。
总结

MySQL 的锁机制通过表锁和行锁的组合实现数据一致性与并发性的平衡。理解锁类型、兼容性及加锁原理是优化性能和避免死锁的关键。实际开发中需结合隔离级别、索引使用及事务设计综合考量。