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

MySQL死鎖排查指南

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

MySQL死鎖排查指南

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

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

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

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

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

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

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

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

當(dāng)兩個(gè)事務(wù)同時(shí)執(zhí)行時(shí):

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

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

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

步驟1:查看死鎖日志

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

SHOW ENGINE INNODB STATUS;

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

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

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

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

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

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

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

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

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

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

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

時(shí)間線事務(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)一加鎖順序 → 無(wú)死鎖(執(zhí)行流程)
當(dāng)兩個(gè)事務(wù)都按“ID從小到大”的順序加鎖時(shí):

時(shí)間線事務(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提交,無(wú)死鎖

這樣是不是更清楚了?需要我把這個(gè)流程做成更簡(jiǎn)潔的對(duì)比表格方便你保存嗎?

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

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

// 壞例子:事務(wù)包含RPC調(diào)用(加長(zhǎng)鎖持有時(shí)間)
@Transactional
public void badTransfer(String fromId, String toId, int amount) {
    accountMapper.updateBalance(fromId, -amount);
    rpcClient.notifyThirdParty(fromId, toId, amount); // 非DB操作,加長(zhǎng)事務(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ù)庫(kù)層面(按需)

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

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

相關(guān)文章

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

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

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

    MySQL 如何處理隱式默認(rèn)值

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論

泰兴市| 孝昌县| 治县。| 石楼县| 义乌市| 雷山县| 河间市| 邢台县| 泸西县| 长沙县| 公安县| 独山县| 寿阳县| 孟津县| 宜丰县| 抚顺市| 寿宁县| 金昌市| 新竹市| 山东省| 海口市| 天长市| 涿鹿县| 达日县| 修武县| 微山县| 阳西县| 巢湖市| 旬邑县| 黎川县| 罗田县| 宜川县| 镇宁| 顺昌县| 常州市| 临武县| 湟源县| 大悟县| 巴彦县| 滨海县| 甘南县|