全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-15 7 分钟 ✍️ juanwangdev

MySQL 死锁与优化

死锁是并发事务相互等待对方持有资源导致的循环阻塞,影响系统可用性。

死锁产生条件

条件说明
互斥资源只能被一个事务持有
持有并等待持有资源同时等待其他资源
不可剥夺不能强制释放他人持有的锁
循环等待形成等待环路

死锁示例

SQL
-- 创建示例表
CREATE TABLE accounts (
    id INT PRIMARY KEY,
    balance DECIMAL(10, 2)
);
INSERT INTO accounts VALUES (1, 1000), (2, 1000);

-- 事务A                          事务B
START TRANSACTION;               START TRANSACTION;
UPDATE accounts SET              UPDATE accounts SET
  balance=balance-100            balance=balance-100
WHERE id=1;  -- 锁住id=1         WHERE id=2;  -- 锁住id=2

UPDATE accounts SET              UPDATE accounts SET
  balance=balance+100            balance=balance+100
WHERE id=2;  -- 等待B释放id=2    WHERE id=1;  -- 等待A释放id=1
-- 死锁!

死锁检测与处理

SQL
-- MySQL 自动检测死锁
-- innodb_deadlock_detect = ON(默认开启)

-- 检测到死锁后,回滚代价最小的事务
-- 事务B被回滚,报错:
-- ERROR 1213 (40001): Deadlock found when attempting to get lock...

-- 查看死锁信息
SHOW ENGINE INNODB STATUS;
-- 找到 LATEST DETECTED DEADLOCK 部分

手动处理死锁

SQL
-- 被回滚的事务需重新执行
START TRANSACTION;
UPDATE accounts SET balance=balance+100 WHERE id=1;
UPDATE accounts SET balance=balance-100 WHERE id=2;
COMMIT;

死锁预防策略

1. 固定加锁顺序

SQL
-- 所有事务按相同顺序加锁
-- 如:按 id 升序加锁

-- 事务A                          事务B
START TRANSACTION;               START TRANSACTION;
UPDATE accounts                  UPDATE accounts
WHERE id=1;                      WHERE id=1;  -- 等待A释放
UPDATE accounts
WHERE id=2;
COMMIT;                          -- B获得锁后继续
                                 UPDATE accounts
                                 WHERE id=2;
                                 COMMIT;
-- 无死锁

2. 减小事务范围

SQL
-- 事务尽快提交,减少锁持有时间
START TRANSACTION;
UPDATE accounts SET balance=balance-100 WHERE id=1;
COMMIT;  -- 立即提交

START TRANSACTION;
UPDATE accounts SET balance=balance+100 WHERE id=2;
COMMIT;

3. 降低隔离级别

SQL
-- RC 比 RR 锁更少,死锁概率更低
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

4. 使用乐观锁

SQL
-- 使用版本号代替悲观锁
UPDATE products
SET stock=stock-1, version=version+1
WHERE id=1 AND version=5;

-- 检查影响行数
-- 若=0,说明版本变化,需重试

5. 添加合适索引

SQL
-- 无索引时锁定全表扫描的行
CREATE INDEX idx_user_id ON orders(user_id);

-- 有索引只锁定匹配行,减少锁冲突
UPDATE orders SET status='paid' WHERE user_id=1;

锁等待超时设置

SQL
-- 设置锁等待超时时间(秒)
SET innodb_lock_wait_timeout = 50;  -- 默认50秒

-- 超时后报错:
-- ERROR 1205 (HY000): Lock wait timeout exceeded

监控死锁

SQL
-- 查看 InnoDB 状态
SHOW ENGINE INNODB STATUS;

-- performance_schema
SELECT * FROM performance_schema.data_lock_waits;
SELECT * FROM performance_schema.data_locks;

-- 开启死锁日志
SET GLOBAL innodb_print_all_deadlocks = ON;

要点总结

  • 死锁是循环等待资源导致的阻塞
  • MySQL 自动检测死锁并回滚代价最小的事务
  • 预防策略:固定加锁顺序、减小事务范围、降低隔离级别
  • 使用乐观锁和合适索引减少锁冲突
  • innodb_lock_wait_timeout 控制锁等待超时

📝 发现内容有误?点击此处直接编辑

← 上一篇 MySQL 事务隔离级别
下一篇 → MySQL 锁的分类
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库