MySQL 死锁及解决方案

06-01 1229阅读

🔥 MySQL 死锁及解决方案

在 MySQL 事务中,多个事务同时竞争相同资源(如表、行)时,可能会 相互等待锁释放,最终形成 死锁(Deadlock)。MySQL 需要检测和解决死锁,否则数据库可能会陷入死循环,影响系统可用性。


1️⃣ 什么是 MySQL 死锁?

死锁(Deadlock) 指两个或多个事务在持有某些资源的同时,又等待对方释放资源,导致事务无法继续执行的情况。

💡 死锁的本质:事务之间形成循环等待。

🔹 典型死锁示例

场景:两个事务交叉锁定同一批数据

-- 事务 A
BEGIN;
UPDATE users SET balance = balance - 100 WHERE id = 1;  -- 锁住 id=1
UPDATE users SET balance = balance + 100 WHERE id = 2;  -- 等待 id=2 的锁(被事务 B 占用)
-- 事务 B
BEGIN;
UPDATE users SET balance = balance - 100 WHERE id = 2;  -- 锁住 id=2
UPDATE users SET balance = balance + 100 WHERE id = 1;  -- 等待 id=1 的锁(被事务 A 占用)

🔥 结果:

  • 事务 A 持有 id=1 的锁,等待 id=2。
  • 事务 B 持有 id=2 的锁,等待 id=1。
  • 循环等待,产生死锁。

    2️⃣ MySQL 如何检测死锁?

    MySQL InnoDB 引擎可以自动检测死锁,并选择回滚某个事务来解除死锁。

    🔹 如何查看 MySQL 死锁?

    SHOW ENGINE INNODB STATUS;
    

    示例输出(简化版):

    ------------------------
    LATEST DETECTED DEADLOCK
    ------------------------
    Transaction A: 
      WAITING for lock on row (users.id=2)
    Transaction B: 
      WAITING for lock on row (users.id=1)
    

    💡 MySQL 自动检测死锁,回滚 其中一个事务,另一个事务可以继续执行。


    3️⃣ 如何解决 MySQL 死锁?(最佳实践)

    ✅ 方法 1:保证事务访问顺序一致

    💡 规则:总是按照固定顺序访问资源,避免交叉锁定

    -- ✅ 事务 A 和 B 都按 `id` 递增顺序访问
    BEGIN;
    UPDATE users SET balance = balance - 100 WHERE id = 1;
    UPDATE users SET balance = balance + 100 WHERE id = 2;
    COMMIT;
    

    📌 事务访问顺序相同,避免循环等待,减少死锁风险。


    ✅ 方法 2:使用短事务,减少锁持有时间

    💡 规则:事务越短,死锁概率越低

    -- ❌ 不推荐:事务时间过长,增加死锁概率
    BEGIN;
    UPDATE users SET balance = balance - 100 WHERE id = 1;
    -- 业务逻辑处理(等待 10 秒)
    SLEEP(10);
    UPDATE users SET balance = balance + 100 WHERE id = 2;
    COMMIT;
    -- ✅ 推荐:立即提交,避免长时间持有锁
    BEGIN;
    UPDATE users SET balance = balance - 100 WHERE id = 1;
    UPDATE users SET balance = balance + 100 WHERE id = 2;
    COMMIT;
    

    📌 事务执行越快,锁的持有时间越短,死锁概率越低。


    ✅ 方法 3:使用 SELECT ... FOR UPDATE 显式加锁

    💡 规则:手动锁定数据,避免隐式锁冲突

    -- ✅ 在更新前先显式加锁,避免死锁
    BEGIN;
    SELECT balance FROM users WHERE id = 1 FOR UPDATE;
    SELECT balance FROM users WHERE id = 2 FOR UPDATE;
    UPDATE users SET balance = balance - 100 WHERE id = 1;
    UPDATE users SET balance = balance + 100 WHERE id = 2;
    COMMIT;
    

    📌 手动加锁可减少 MySQL 自动加锁导致的死锁问题。


    ✅ 方法 4:合理设置索引,避免行锁升级为表锁

    💡 规则:索引命中减少锁范围,避免锁冲突

    MySQL 死锁及解决方案
    (图片来源网络,侵删)
    -- ❌ 没有索引,导致全表锁
    UPDATE users SET balance = balance - 100 WHERE name = 'Alice';
    -- ✅ 使用索引减少锁的范围
    UPDATE users SET balance = balance - 100 WHERE id = 1;
    

    📌 索引可以让 InnoDB 只锁定必要的行,减少锁冲突。


    ✅ 方法 5:使用 NOWAIT 或 SKIP LOCKED 避免等待

    💡 规则:遇到锁竞争时直接失败或跳过

    MySQL 死锁及解决方案
    (图片来源网络,侵删)
    -- ✅ `NOWAIT` 避免长时间等待锁(适用于高并发场景)
    SELECT * FROM orders WHERE status = 'pending' FOR UPDATE NOWAIT;
    -- ✅ `SKIP LOCKED` 直接跳过被锁行(适用于队列消费)
    SELECT * FROM orders WHERE status = 'pending' FOR UPDATE SKIP LOCKED;
    

    📌 适用于队列型业务(如订单、任务调度)。


    4️⃣ MySQL 死锁处理流程

    📌 MySQL 处理死锁的机制:

    1. 发现死锁(InnoDB 死锁检测)。
    2. 回滚其中一个事务,让另一个事务继续执行。
    3. 事务重试机制:应用层检测死锁并重试事务。

    🔹 事务重试代码示例(Java)

    int retryCount = 3;
    while (retryCount > 0) {
        try {
            // 开启事务
            connection.setAutoCommit(false);
            
            // 业务逻辑
            statement.executeUpdate("UPDATE users SET balance = balance - 100 WHERE id = 1");
            statement.executeUpdate("UPDATE users SET balance = balance + 100 WHERE id = 2");
            
            // 提交事务
            connection.commit();
            break;
        } catch (SQLException e) {
            if (e.getMessage().contains("Deadlock found")) {
                retryCount--;
                System.out.println("死锁发生,重试中... 剩余次数: " + retryCount);
            } else {
                throw e; // 非死锁错误,抛出异常
            }
        }
    }
    

    📌 思路:

    • 检测 SQL 异常信息,如果发现是死锁,则自动重试。
    • 避免无限重试(如 3~5 次)。
    • 业务层面可使用乐观锁(版本号机制),避免竞争冲突。

      5️⃣ 总结

      死锁原因解决方案
      交叉锁定资源固定访问顺序(如按 ID 递增访问)
      事务执行过慢减少事务时间,尽快提交
      隐式锁冲突使用 SELECT ... FOR UPDATE 显式加锁
      锁范围过大合理使用索引,减少锁的范围
      高并发竞争使用 NOWAIT / SKIP LOCKED 处理锁等待
      死锁不可避免应用层事务重试机制

      📌 一句话总结:死锁是 MySQL 并发访问的常见问题,优化事务逻辑、减少锁等待是关键。🚀

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码