【MySQL】深入理解MySQL锁机制:从行锁、表锁到死锁

目录

前言:为什么需要锁?

🔐 一、MySQL锁的分类:全局视角

📌 1. 按粒度划分(最重要!)

📌 2. 按模式划分(行锁的核心)

🧱 二、InnoDB行锁的实现:记录锁、间隙锁、临键锁

🔹 1. 记录锁(Record Lock)

🔹 2. 间隙锁(Gap Lock)

🔹 3. 临键锁(Next-Key Lock) = 记录锁 + 间隙锁

🔄 三、锁的实践:如何加锁?

✅ 1. 显式加锁(悲观锁)

✅ 2. 隐式加锁(由SQL语句自动触发)

🧪 四、实战案例:转账场景中的锁应用

🏦 需求:实现安全的银行转账

💣 五、死锁(Deadlock):并发的“致命”陷阱

❌ 什么是死锁?

🛠️ MySQL如何解决死锁?

📊 六、查看锁信息(DBA必备技能)

🎯 总结:锁机制核心要点


前言:为什么需要锁?

想象一个银行转账场景:

用户A向用户B转账100元。
事务1:A账户减100元
事务2:B账户加100元

如果这两个操作同时被多个线程执行,而没有锁的保护,可能会出现:

  • A扣了两次钱,但B只收到一次 → 数据不一致!

锁(Lock) 就是MySQL用来保证并发安全数据一致性的核心机制。它确保在并发环境下,多个事务对同一数据的访问是有序、可控的。


🔐 一、MySQL锁的分类:全局视角

MySQL的锁体系非常复杂,我们从几个维度来理解:

📌 1. 按粒度划分(最重要!)

锁类型说明加锁开销并发度使用场景
全局锁锁住整个实例 FLUSH TABLES WITH READ LOCK极低备份(mysqldump)
表级锁锁住整张表 LOCK TABLES ... WRITEMyISAM引擎、DDL操作
✅ 行级锁锁住单行或多行数据InnoDB核心,支持高并发

💡 重点:InnoDB引擎主要使用行级锁,这是它支持高并发的关键。


📌 2. 按模式划分(行锁的核心)

锁模式英文兼容性说明
共享锁(S锁)Shared Lock✅ S锁读锁,多个事务可同时读
排他锁(X锁)Exclusive Lock❌ 任何锁写锁,阻塞其他读写
意向共享锁(IS)Intention Shared-表级锁,表示“打算加S锁”
意向排他锁(IX)Intention Exclusive-表级锁,表示“打算加X锁”

🔑 核心规则

  • S锁与S锁兼容:多个事务可同时读同一行。
  • S锁与X锁互斥:读时不能写,写时不能读。
  • X锁与X锁互斥:写时不能写(避免脏写)。

🧱 二、InnoDB行锁的实现:记录锁、间隙锁、临键锁

InnoDB的行锁不是简单的“锁一行”,而是基于索引B+树结构实现的复杂机制。

🔹 1. 记录锁(Record Lock)

  • 作用:锁定索引记录本身。
  • 示例
    -- 假设id是主键
    SELECT * FROM users WHERE id = 1 FOR UPDATE; -- 对id=1的记录加X锁

🔹 2. 间隙锁(Gap Lock)

  • 作用:锁定索引记录之间的间隙,防止幻读(Phantom Read)。
  • 目的:阻止其他事务在间隙中插入新记录。
  • 示例
    -- users表中id=5, 10, 15
    -- 间隙:(-∞,5), (5,10), (10,15), (15,+∞)
    SELECT * FROM users WHERE id BETWEEN 6 AND 9 FOR UPDATE;
    -- 会锁定间隙 (5,10),阻止插入id=7,8的记录

🔹 3. 临键锁(Next-Key Lock) = 记录锁 + 间隙锁

  • 作用:锁定记录本身 + 记录之前的间隙
  • 范围(gap_left, record]
  • 默认行为:InnoDB在可重复读(REPEATABLE READ) 隔离级别下,默认使用临键锁防止幻读。

🌰 案例:锁定id=10的记录(临键锁)

  • 锁住间隙:(5,10)
  • 锁住记录:id=10
  • 范围:(5,10]

🔄 三、锁的实践:如何加锁?

✅ 1. 显式加锁(悲观锁)

-- 1. 共享锁(S锁):读锁
SELECT * FROM users WHERE id = 1 LOCK IN SHARE MODE; -- MySQL 8.0.1之前
-- 或
SELECT * FROM users WHERE id = 1 FOR SHARE; -- MySQL 8.0.1+

-- 2. 排他锁(X锁):写锁
SELECT * FROM users WHERE id = 1 FOR UPDATE;

⚠️ 注意FOR UPDATE 不仅锁住查询行,还会锁住相关索引(包括二级索引),防止其他事务修改。

✅ 2. 隐式加锁(由SQL语句自动触发)

SQL语句自动加的锁
INSERT在插入行上加X锁
UPDATE在修改行上加X锁
DELETE在删除行上加X锁
SELECT(普通)不加锁(快照读)
SELECT ... FOR UPDATEX锁
SELECT ... LOCK IN SHARE MODES锁

🧪 四、实战案例:转账场景中的锁应用

🏦 需求:实现安全的银行转账

-- 事务1:A转账100给B
START TRANSACTION;
-- 1. 锁住A账户(防止余额被修改)
SELECT balance FROM accounts WHERE user_id = 'A' FOR UPDATE;
-- 2. 检查余额是否足够
-- 3. A账户减100
UPDATE accounts SET balance = balance - 100 WHERE user_id = 'A';
-- 4. 锁住B账户
SELECT balance FROM accounts WHERE user_id = 'B' FOR UPDATE;
-- 5. B账户加100
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'B';
COMMIT;

关键点

  • 使用 FOR UPDATE 确保在事务结束前,A、B账户余额不会被其他事务修改。
  • 避免了“脏读”、“不可重复读”和“幻读”。

💣 五、死锁(Deadlock):并发的“致命”陷阱

❌ 什么是死锁?

两个或多个事务互相等待对方释放锁,导致所有事务都无法继续。

-- 事务1
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 'A'; -- 锁住A
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'B'; -- 等待B(被事务2锁住)

-- 事务2
START TRANSACTION;
UPDATE accounts SET balance = balance - 50 WHERE user_id = 'B'; -- 锁住B
UPDATE accounts SET balance = balance + 50 WHERE user_id = 'A'; -- 等待A(被事务1锁住)

👉 结果:事务1等B,事务2等A → 死锁!

🛠️ MySQL如何解决死锁?

  1. 死锁检测:InnoDB有一个死锁检测器,会定期检查锁等待图。
  2. 自动回滚:一旦发现死锁,会选择一个代价较小的事务进行回滚(ROLLBACK),释放其锁,让其他事务继续。
  3. 报错:被回滚的事务会收到错误:Deadlock found when trying to get lock; try restarting transaction

💡 最佳实践

  • 尽量按相同顺序访问资源(如先A后B)。
  • 减少事务大小,尽快提交。
  • 捕获死锁异常,实现自动重试机制

📊 六、查看锁信息(DBA必备技能)

-- 1. 查看当前正在运行的事务
SELECT * FROM information_schema.INNODB_TRX\G

-- 2. 查看锁等待情况
SELECT * FROM information_schema.INNODB_LOCKS; -- 旧版本
-- MySQL 8.0+ 使用:
SELECT * FROM performance_schema.data_locks;

-- 3. 查看锁等待的详细信息
SELECT * FROM performance_schema.data_lock_waits;

-- 4. 查看死锁日志(需开启)
SHOW ENGINE INNODB STATUS\G
-- 在输出中查找 "LATEST DETECTED DEADLOCK" 部分

🎯 总结:锁机制核心要点

概念关键点
锁粒度行锁 > 表锁 > 全局锁(InnoDB主打行锁)
锁模式S锁(读)、X锁(写)、IS/IX(意向锁)
行锁实现记录锁 + 间隙锁 + 临键锁(防幻读)
加锁方式FOR UPDATE(X锁)、FOR SHARE(S锁)
死锁处理检测 + 回滚 + 重试
最佳实践小事务、同序访问、及时提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值