最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL死鎖排查指南

 更新時間:2025年12月26日 15:54:51   作者:不如打代碼KK  
本文詳細介紹了MySQL死鎖的排查與解決方法,從死鎖的定義、Java業(yè)務(wù)中的死鎖場景、死鎖排查步驟和命令到根治死鎖的方案,包括約定統(tǒng)一的加鎖順序、縮短事務(wù)范圍和優(yōu)化數(shù)據(jù)庫層面,提供了全面的指導(dǎo),感興趣的朋友跟隨小編一起看看吧

MySQL死鎖排查指南

作為一名10年經(jīng)驗的Java工程師,我會從場景、排查、解決三個維度,帶你搞定MySQL死鎖問題。

一、先搞懂:死鎖是什么?

死鎖是多個事務(wù)互相持有對方需要的資源,陷入無限等待的僵局。

它必須同時滿足4個“缺一不可”的條件(破壞任意一個就能避免死鎖):

  1. 資源獨占:一個資源(如一行數(shù)據(jù))只能被一個事務(wù)持有;
  2. 請求并持有:事務(wù)持有資源的同時,又請求其他資源且不釋放已有資源;
  3. 不可剝奪:事務(wù)已獲得的資源不能被強行搶占;
  4. 循環(huán)等待:事務(wù)之間形成“事務(wù)A等B,B等A”的閉環(huán)。

二、經(jīng)典場景:Java業(yè)務(wù)里的死鎖長啥樣?

用戶互轉(zhuǎn)余額為例(Java+MySQL事務(wù)):

// 事務(wù)A:用戶A給B轉(zhuǎn)賬10元
@Transactional
public void transferAtoB(String aId, String bId, int amount) {
    // 1. 鎖定A的賬戶(更新操作會加行鎖)
    accountMapper.updateBalance(aId, -amount);
    // 2. 嘗試鎖定B的賬戶(若B此時正在操作A,就會等待)
    accountMapper.updateBalance(bId, +amount);
}
// 事務(wù)B:用戶B給A轉(zhuǎn)賬20元
@Transactional
public void transferBtoA(String bId, String aId, int amount) {
    // 1. 鎖定B的賬戶
    accountMapper.updateBalance(bId, -amount);
    // 2. 嘗試鎖定A的賬戶(此時A已被事務(wù)A鎖定,陷入等待)
    accountMapper.updateBalance(aId, +amount);
}

當兩個事務(wù)同時執(zhí)行時:

  • 事務(wù)A持有A的鎖,等待B的鎖;
  • 事務(wù)B持有B的鎖,等待A的鎖;
    → 死鎖產(chǎn)生。

三、死鎖排查:核心步驟+命令

當業(yè)務(wù)出現(xiàn)“接口超時、事務(wù)卡住”時,優(yōu)先排查死鎖。

步驟1:查看死鎖日志

MySQL(InnoDB引擎)最核心的排查命令:

SHOW ENGINE INNODB STATUS;

執(zhí)行后,找到 LATEST DETECTED DEADLOCK 模塊,關(guān)鍵信息包括:

  • TRANSACTION (1)/(2):沖突的兩個事務(wù);
  • WAITING FOR THIS LOCK:事務(wù)等待的鎖及對應(yīng)的SQL;
  • HOLDS THE LOCK(S):事務(wù)持有的鎖及對應(yīng)的SQL;
  • WE ROLL BACK TRANSACTION (X):MySQL自動回滾的事務(wù)(解決死鎖)。

步驟2:結(jié)合Java業(yè)務(wù)定位代碼

根據(jù)死鎖日志里的SQL語句,找到對應(yīng)的Java代碼(比如上述transferAtoB方法),分析事務(wù)的加鎖順序是否不一致。

四、根治死鎖:Java業(yè)務(wù)里的落地方案

針對Java業(yè)務(wù),從代碼、數(shù)據(jù)庫兩個層面解決:

方案1:約定統(tǒng)一的加鎖順序(最有效)

我們約定一個全局規(guī)則:無論轉(zhuǎn)賬方向如何,都先鎖定 ID 字典序更小的賬戶,再鎖定 ID 更大的賬戶,這就是 “統(tǒng)一的加鎖順序”:

@Service
public class TransferService {
    @Autowired
    private AccountMapper accountMapper;
    // 統(tǒng)一的轉(zhuǎn)賬方法(無論誰轉(zhuǎn)誰,都按ID大小順序加鎖)
    @Transactional
    public void transfer(String fromId, String toId, int amount) {
        // 步驟1:確定加鎖順序(全局統(tǒng)一規(guī)則)
        String lockFirstId; // 先鎖這個ID
        String lockSecondId; // 后鎖這個ID
        if (fromId.compareTo(toId) < 0) {
            lockFirstId = fromId;
            lockSecondId = toId;
        } else {
            lockFirstId = toId;
            lockSecondId = fromId;
        }
        // 步驟2:按統(tǒng)一順序加鎖(先鎖小ID,再鎖大ID)
        // 先鎖定第一個賬戶(無論它是轉(zhuǎn)出方還是轉(zhuǎn)入方)
        if (lockFirstId.equals(fromId)) {
            accountMapper.deductBalance(lockFirstId, amount); // 轉(zhuǎn)出
        } else {
            accountMapper.addBalance(lockFirstId, amount); // 轉(zhuǎn)入
        }
        // 再鎖定第二個賬戶
        if (lockSecondId.equals(fromId)) {
            accountMapper.deductBalance(lockSecondId, amount); // 轉(zhuǎn)出
        } else {
            accountMapper.addBalance(lockSecondId, amount); // 轉(zhuǎn)入
        }
    }
}

假設(shè):A 的 ID 是user_001,B 的 ID 是user_002(user_001 < user_002)。

  • 當調(diào)用transfer(“user_001”, “user_002”, 10)(A 轉(zhuǎn) B):先鎖user_001,再鎖user_002;
  • 當調(diào)用transfer(“user_002”, “user_001”, 20)(B 轉(zhuǎn) A):依然先鎖user_001,再鎖user_002;
    兩個事務(wù)的加鎖順序完全一致,不會出現(xiàn) “你等我、我等你” 的循環(huán)等待,從根源上杜絕死鎖。
流程展示
  • 用戶A:ID為 user_001
  • 用戶B:ID為 user_002
  • 規(guī)則:user_001 的字典序 < user_002

無統(tǒng)一加鎖順序 → 死鎖(執(zhí)行流程)
當兩個事務(wù)各自按“轉(zhuǎn)出方→轉(zhuǎn)入方”的順序加鎖時:

時間線事務(wù)1(A轉(zhuǎn)B:先鎖A,再鎖B)事務(wù)2(B轉(zhuǎn)A:先鎖B,再鎖A)狀態(tài)
T1執(zhí)行 deductBalance("user_001", 10),成功鎖定 user_001-事務(wù)1持有A的鎖
T2-執(zhí)行 deductBalance("user_002", 20),成功鎖定 user_002事務(wù)2持有B的鎖
T3嘗試執(zhí)行 addBalance("user_002", 10),需要鎖B → 等待-事務(wù)1等待B的鎖
T4-嘗試執(zhí)行 addBalance("user_001", 20),需要鎖A → 等待事務(wù)2等待A的鎖
T5持續(xù)等待B的鎖持續(xù)等待A的鎖死鎖

有統(tǒng)一加鎖順序 → 無死鎖(執(zhí)行流程)
當兩個事務(wù)都按“ID從小到大”的順序加鎖時:

時間線事務(wù)1(A轉(zhuǎn)B:先鎖A,再鎖B)事務(wù)2(B轉(zhuǎn)A:先鎖A,再鎖B)狀態(tài)
T1執(zhí)行 deductBalance("user_001", 10),成功鎖定 user_001-事務(wù)1持有A的鎖
T2-嘗試執(zhí)行 addBalance("user_001", 20),需要鎖A → 等待事務(wù)2等待A的鎖
T3執(zhí)行 addBalance("user_002", 10),成功鎖定 user_002-事務(wù)1持有A、B的鎖
T4事務(wù)執(zhí)行完成,釋放A、B的鎖-事務(wù)1提交,鎖釋放
T5-獲得A的鎖,執(zhí)行 addBalance("user_001", 20)事務(wù)2持有A的鎖
T6-執(zhí)行 deductBalance("user_002", 20),成功鎖定 user_002事務(wù)2持有A、B的鎖
T7-事務(wù)執(zhí)行完成,釋放A、B的鎖事務(wù)2提交,無死鎖

這樣是不是更清楚了?需要我把這個流程做成更簡潔的對比表格方便你保存嗎?

方案2:縮短事務(wù)范圍

避免事務(wù)中包含非數(shù)據(jù)庫操作(如RPC調(diào)用、日志打印),減少鎖的持有時間:

// 壞例子:事務(wù)包含RPC調(diào)用(加長鎖持有時間)
@Transactional
public void badTransfer(String fromId, String toId, int amount) {
    accountMapper.updateBalance(fromId, -amount);
    rpcClient.notifyThirdParty(fromId, toId, amount); // 非DB操作,加長事務(wù)
    accountMapper.updateBalance(toId, +amount);
}
// 好例子:事務(wù)僅包含DB操作
@Transactional
public void goodTransfer(String fromId, String toId, int amount) {
    accountMapper.updateBalance(fromId, -amount);
    accountMapper.updateBalance(toId, +amount);
}
// 非DB操作放在事務(wù)外
public void transferWithNotify(String fromId, String toId, int amount) {
    goodTransfer(fromId, toId, amount);
    rpcClient.notifyThirdParty(fromId, toId, amount);
}

方案3:優(yōu)化數(shù)據(jù)庫層面(按需)

  • 加索引:確保更新/查詢的WHERE條件走索引,減少鎖的范圍(避免表鎖);
  • 降低隔離級別:業(yè)務(wù)允許的話,將隔離級別從REPEATABLE-READ(默認)降為READ-COMMITTED,減少間隙鎖;
  • 顯式加鎖優(yōu)化:使用SELECT ... FOR UPDATE顯式加鎖時,確保WHERE條件走索引。

到此這篇關(guān)于MySQL死鎖排查指南的文章就介紹到這了,更多相關(guān)mysql死鎖排查內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Mysql創(chuàng)建子賬號及權(quán)限設(shè)置全過程

    Mysql創(chuàng)建子賬號及權(quán)限設(shè)置全過程

    本文介紹通過Xshell6連接MySQL,創(chuàng)建test子賬號并設(shè)置密碼,測試登錄,分配本地及遠程操作權(quán)限,最后刪除子賬號及權(quán)限的完整流程,涵蓋賬號管理與權(quán)限配置關(guān)鍵步驟
    2025-10-10
  • MySQL 如何處理隱式默認值

    MySQL 如何處理隱式默認值

    這篇文章主要介紹了MySQL 處理隱式默認值的相關(guān)資料,幫助大家更好的理解和使用MySQL,感興趣的朋友可以了解下
    2020-12-12
  • mysql?dblink跨庫關(guān)聯(lián)查詢的實現(xiàn)

    mysql?dblink跨庫關(guān)聯(lián)查詢的實現(xiàn)

    本文主要介紹了mysql?dblink跨庫關(guān)聯(lián)查詢的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • SQL刪除重復(fù)數(shù)據(jù)的實例教程

    SQL刪除重復(fù)數(shù)據(jù)的實例教程

    在使用SQL提數(shù)的時候,常會遇到表內(nèi)有重復(fù)值的時候,下面這篇文章主要給大家介紹了關(guān)于SQL刪除重復(fù)數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-07-07
  • linux下mysql5.7.17最新穩(wěn)定版本安裝教程

    linux下mysql5.7.17最新穩(wěn)定版本安裝教程

    這篇文章主要為大家詳細介紹了linux上mysql5.7.17最新穩(wěn)定版本安裝教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • mybatis 模糊查詢的實現(xiàn)方法

    mybatis 模糊查詢的實現(xiàn)方法

    這篇文章主要介紹了mybatis 模糊查詢的實現(xiàn)方法的相關(guān)資料,希望通過本文能幫助到大家,讓大家掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • MySQL不使用子查詢的原因及優(yōu)化案例

    MySQL不使用子查詢的原因及優(yōu)化案例

    對于mysql,不推薦使用子查詢,效率太差,執(zhí)行子查詢時,MYSQL需要創(chuàng)建臨時表,查詢完畢后再刪除這些臨時表,所以,子查詢的速度會受到一定的影響,本文給大家詳細介紹了MySQL不使用子查詢的原因及優(yōu)化案例,需要的朋友可以參考下
    2025-01-01
  • MySQL系列數(shù)據(jù)庫設(shè)計三范式教程示例

    MySQL系列數(shù)據(jù)庫設(shè)計三范式教程示例

    這篇文章主要為大家介紹了MySQL系列之數(shù)據(jù)庫設(shè)計三范式的教程示例講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2021-10-10
  • 基于proxysql實現(xiàn)MySQL讀寫分離的實現(xiàn)實例

    基于proxysql實現(xiàn)MySQL讀寫分離的實現(xiàn)實例

    這篇文章主要介紹了基于proxysql實現(xiàn)MySQL讀寫分離,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-11-11
  • MySQL授予用戶權(quán)限命令詳解

    MySQL授予用戶權(quán)限命令詳解

    這篇文章主要給大家介紹了關(guān)于MySQL授予用戶權(quán)限命令的相關(guān)資料,授權(quán)就是為某個用戶賦予某些權(quán)限,例如可以為新建的用戶賦予查詢所有數(shù)據(jù)庫和表的權(quán)限,需要的朋友可以參考下
    2023-11-11

最新評論

金溪县| 淮南市| 榆林市| 犍为县| 娱乐| 湘潭县| 张家港市| 卓资县| 绵阳市| 营口市| 新乐市| 四会市| 新巴尔虎左旗| 遂平县| 鄂托克前旗| 商城县| 苗栗市| 桑植县| 镇安县| 九龙城区| 太湖县| 恭城| 麦盖提县| 嘉祥县| 修武县| 台东县| 南投市| 县级市| 乐山市| 张家界市| 新和县| 六盘水市| 库尔勒市| 溧阳市| 青川县| 奉节县| 桦川县| 武安市| 桑日县| 兴山县| 苏尼特右旗|