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

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

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

前言

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

Deadlock found when trying to get lock; try restarting transaction

死鎖是MySQL高并發(fā)場景下的常見問題。偶爾一兩次可以通過業(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)典的死鎖場景:事務(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隔離級別下,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隔離級別(間隙鎖少很多,但要注意幻讀)
  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ù)層重試

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

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

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

總結(jié)

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

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

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

到此這篇關(guān)于MySQL死鎖排查與預(yù)防實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)MySQL死鎖排查內(nèi)容請搜索腳本之家以前的文章或繼續(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ù)對我們對站長來說都是最寶貴的,我們平時(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ì),對大家的學(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

最新評論

东乌珠穆沁旗| 胶州市| 玉溪市| 马尔康县| 奉节县| 南丰县| 乌恰县| 阜平县| 星子县| 贺兰县| 临潭县| 安化县| 射洪县| 丁青县| 衡南县| 平泉县| 河东区| 启东市| 甘洛县| 宁陕县| 广水市| 阆中市| 霍山县| 江华| 射阳县| 南皮县| 同仁县| 海阳市| 大埔区| 舞阳县| 博乐市| 凌源市| 齐齐哈尔市| 柳州市| 隆昌县| 敦煌市| 平乡县| 福海县| 紫金县| 南江县| 福泉市|