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

MySQL死鎖排查與預(yù)防實(shí)戰(zhàn)

 更新時(shí)間:2026年02月11日 08:54:56   作者:花寶寶hua  
死鎖是MySQL高并發(fā)場(chǎng)景下的常見問題,偶爾一兩次可以通過業(yè)務(wù)重試解決,本文就來詳細(xì)的介紹一下MySQL死鎖排查與預(yù)防實(shí)戰(zhàn),感興趣的可以了解一下

前言

線上日志里突然出現(xiàn)大量這個(gè)錯(cuò)誤:

Deadlock found when trying to get lock; try restarting transaction

死鎖是MySQL高并發(fā)場(chǎng)景下的常見問題。偶爾一兩次可以通過業(yè)務(wù)重試解決,但如果頻繁出現(xiàn),就需要從根本上排查和優(yōu)化。

這篇整理MySQL死鎖的排查方法和預(yù)防策略。

一、查看死鎖信息

MySQL有個(gè)命令能看到最近一次死鎖的詳情:

SHOW ENGINE INNODB STATUS\G

輸出很長,找LATEST DETECTED DEADLOCK這部分:

*** (1) TRANSACTION:
UPDATE orders SET status = 'paid' WHERE id = 1001
*** (1) HOLDS THE LOCK(S):  -- 持有orders表的鎖
*** (1) WAITING FOR THIS LOCK:  -- 等inventory表的鎖

*** (2) TRANSACTION:
UPDATE inventory SET quantity = quantity - 1 WHERE product_id = 2001
*** (2) HOLDS THE LOCK(S):  -- 持有inventory表的鎖
*** (2) WAITING FOR THIS LOCK:  -- 等orders表的鎖

*** WE ROLL BACK TRANSACTION (2)

經(jīng)典的死鎖場(chǎng)景:事務(wù)A鎖了orders等inventory,事務(wù)B鎖了inventory等orders,互相等。

二、分析死鎖原因

知道是哪兩個(gè)SQL了,回去翻代碼。

原來下單邏輯里有兩種調(diào)用順序:

// 路徑A:先改訂單再扣庫存
updateOrderStatus(orderId, "paid");
decreaseInventory(productId, 1);

// 路徑B:先扣庫存再改訂單(另一個(gè)接口)
decreaseInventory(productId, 1);
updateOrderStatus(orderId, "paid");

兩個(gè)接口都在事務(wù)里,剛好并發(fā)了就死鎖。

三、解決方案

最直接的辦法:統(tǒng)一加鎖順序

不管哪個(gè)接口,都先操作orders再操作inventory(或者反過來,總之要一致)。

// 統(tǒng)一順序:先orders后inventory
@Transactional
public void processOrder(long orderId, long productId) {
    updateOrderStatus(orderId, "paid");  // 永遠(yuǎn)先鎖orders
    decreaseInventory(productId, 1);     // 再鎖inventory
}

如果涉及多條記錄,按ID排序:

List<Long> ids = Arrays.asList(id1, id2, id3);
Collections.sort(ids);
for (Long id : ids) {
    lockAndProcess(id);
}

四、間隙鎖導(dǎo)致的死鎖

還有一種更詭異的死鎖,兩個(gè)事務(wù)操作的都不是同一行數(shù)據(jù)。

這通常是間隙鎖的問題。RR隔離級(jí)別下,SELECT ... FOR UPDATE如果沒命中數(shù)據(jù),會(huì)鎖一個(gè)"間隙"。

比如user_id有1、5、10三條記錄:

-- 事務(wù)A
SELECT * FROM orders WHERE user_id = 3 FOR UPDATE;
-- 沒有user_id=3的數(shù)據(jù),但會(huì)鎖住(1,5)這個(gè)間隙

-- 事務(wù)B
SELECT * FROM orders WHERE user_id = 7 FOR UPDATE;
-- 鎖住(5,10)這個(gè)間隙

-- 然后兩邊各自INSERT
-- 事務(wù)A想插入user_id=6,要等(5,10)的間隙鎖
-- 事務(wù)B想插入user_id=4,要等(1,5)的間隙鎖
-- 死鎖

解決辦法:

  1. 改用RC隔離級(jí)別(間隙鎖少很多,但要注意幻讀)
  2. 用唯一索引精確查詢,避免范圍鎖
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

五、縮小事務(wù)范圍

還有個(gè)常見問題是事務(wù)太長。事務(wù)越長,持有鎖的時(shí)間越久,死鎖概率越高。

// 這種寫法不好
@Transactional
public void process() {
    queryData();        // 查數(shù)據(jù)
    callExternalApi();  // 調(diào)外部接口,可能很慢
    updateDatabase();   // 更新數(shù)據(jù)庫
}

// 改成這樣
public void process() {
    queryData();
    callExternalApi();  // 外部調(diào)用放事務(wù)外面
    updateInTransaction();
}

@Transactional
public void updateInTransaction() {
    updateDatabase();   // 只有真正需要事務(wù)的操作
}

六、監(jiān)控與告警

建議加上監(jiān)控:

# 簡單腳本,每分鐘檢查死鎖次數(shù)
DEADLOCKS=$(mysql -e "SHOW GLOBAL STATUS LIKE 'Innodb_deadlocks'" | awk 'NR==2{print $2}')
echo "$(date) deadlocks: $DEADLOCKS" >> /var/log/deadlock.log

配合Prometheus的話:

- alert: MySQLDeadlock
  expr: increase(mysql_global_status_innodb_deadlocks[5m]) > 0
  for: 1m

死鎖次數(shù)漲了就告警,別等業(yè)務(wù)反饋才知道。

七、業(yè)務(wù)層重試

有些場(chǎng)景死鎖確實(shí)很難完全避免,那就在業(yè)務(wù)層做重試:

int retry = 3;
while (retry-- > 0) {
    try {
        doTransaction();
        break;
    } catch (DeadlockException e) {
        if (retry == 0) throw e;
        Thread.sleep(100);  // 等一下再試
    }
}

MySQL檢測(cè)到死鎖會(huì)立即回滾一個(gè)事務(wù),不會(huì)一直卡著,所以重試通常能成功。

總結(jié)

死鎖本質(zhì)是資源競(jìng)爭問題,預(yù)防比解決更重要:

方法效果
統(tǒng)一加鎖順序最有效,從根本上避免死鎖
縮小事務(wù)范圍減少鎖持有時(shí)間
合理使用索引減少鎖的范圍
降低隔離級(jí)別減少間隙鎖(RC級(jí)別)
業(yè)務(wù)層重試兜底方案

記住兩點(diǎn):統(tǒng)一加鎖順序縮小事務(wù)范圍,能解決大部分死鎖問題。

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

相關(guān)文章

  • MYSQL數(shù)據(jù)庫中cmd命令操作詳解

    MYSQL數(shù)據(jù)庫中cmd命令操作詳解

    今天我們就來看一下數(shù)據(jù)庫的各種命令,以下命令全部是從CMD命令窗口下的命令行輸入指令,首先如果如果輸入mysql,系統(tǒng)提示“mysql不是內(nèi)部命 令或外部命令
    2016-04-04
  • 允許遠(yuǎn)程用戶訪問mysql服務(wù)sql語句

    允許遠(yuǎn)程用戶訪問mysql服務(wù)sql語句

    本節(jié)主要介紹了如何允許遠(yuǎn)程用戶訪問mysql服務(wù),本例授權(quán)192.168.14.1 主機(jī)的cakephp用戶訪問cakephp數(shù)據(jù)庫
    2014-07-07
  • MySQL高可用解決方案MMM(mysql多主復(fù)制管理器)

    MySQL高可用解決方案MMM(mysql多主復(fù)制管理器)

    MySQL本身沒有提供replication failover的解決方案,通過MMM方案能實(shí)現(xiàn)服務(wù)器的故障轉(zhuǎn)移,從而實(shí)現(xiàn)mysql的高可用。MMM不僅能提供浮動(dòng)IP的功能,如果當(dāng)前的主服務(wù)器掛掉后,會(huì)將你后端的從服務(wù)器自動(dòng)轉(zhuǎn)向新的主服務(wù)器進(jìn)行同步復(fù)制,不用手工更改同步配置
    2017-09-09
  • MySQL性能優(yōu)化之table_cache配置參數(shù)淺析

    MySQL性能優(yōu)化之table_cache配置參數(shù)淺析

    這篇文章主要介紹了MySQL性能優(yōu)化之table_cache配置參數(shù)淺析,本文介紹了它的緩存機(jī)制、參數(shù)優(yōu)化及清空緩存的命令等,需要的朋友可以參考下
    2014-07-07
  • MySQL數(shù)據(jù)庫備份與恢復(fù)方法

    MySQL數(shù)據(jù)庫備份與恢復(fù)方法

    網(wǎng)站數(shù)據(jù)對(duì)我們對(duì)站長來說都是最寶貴的,我們平時(shí)應(yīng)該養(yǎng)成良好的備份數(shù)據(jù)的習(xí)慣。
    2010-12-12
  • MySQL驗(yàn)證用戶權(quán)限的方法

    MySQL驗(yàn)證用戶權(quán)限的方法

    這篇文章主要介紹了MySQL驗(yàn)證用戶權(quán)限的方法,需要的朋友可以參考下
    2015-11-11
  • MySQL8.4一主一從環(huán)境搭建實(shí)現(xiàn)

    MySQL8.4一主一從環(huán)境搭建實(shí)現(xiàn)

    本文主要介紹了MySQL8.4一主一從環(huán)境搭建實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • 淺析如何保證MySQL與Redis數(shù)據(jù)一致性

    淺析如何保證MySQL與Redis數(shù)據(jù)一致性

    在互聯(lián)網(wǎng)應(yīng)用中,MySQL作為持久化存儲(chǔ)引擎,Redis作為高性能緩存層,兩者的組合能有效提升系統(tǒng)性能,下面我們來看看如何保證兩者的數(shù)據(jù)一致性吧
    2025-06-06
  • Mysql存儲(chǔ)引擎特性總結(jié)

    Mysql存儲(chǔ)引擎特性總結(jié)

    這篇文章主要介紹了Mysql存儲(chǔ)引擎特性總結(jié),同時(shí)總結(jié)了最常用的2種存儲(chǔ)引擎的特性,以及各引擎的適用環(huán)境,需要的朋友可以參考下
    2014-07-07
  • MySql 5.6.36 64位綠色版安裝圖文教程

    MySql 5.6.36 64位綠色版安裝圖文教程

    這篇文章主要介紹了MySql 5.6.36 64位綠色版安裝圖文教程,需要的朋友可以參考下
    2017-05-05

最新評(píng)論

舞阳县| 阿克| 朝阳市| 建水县| 化德县| 上饶县| 麦盖提县| 东宁县| 额济纳旗| 阿拉善盟| 德清县| 南郑县| 区。| 广河县| 永善县| 武隆县| 武城县| 辉南县| 北川| 全州县| 淳安县| 锡林郭勒盟| 贵港市| 安泽县| 中山市| 呼伦贝尔市| 白朗县| 杨浦区| 敦煌市| 兴宁市| 西丰县| 海南省| 大名县| 仪征市| 金昌市| 武城县| 南溪县| 吴忠市| 高清| 龙岩市| 万源市|