MySQL innoDB 間隙鎖產生的死鎖問題

来源:https://www.cnblogs.com/jingdongkeji/archive/2023/10/07/17745643.html
-Advertisement-
Play Games

線上經常偶發死鎖問題,當時處理一張表,也沒有聯表處理,但是有兩個mq入口,並且消息體存在一樣的情況,頻率還不是很低,這麼一個背景,我非常容易懷疑到,兩個消息同時近到這一個事務裡面導致的,但是是偶發的,又模擬不出來什麼場景會導致死鎖,只能進行代碼分析,問題還原的方式去排查問題。 ...


背景

線上經常偶發死鎖問題,當時處理一張表,也沒有聯表處理,但是有兩個mq入口,並且消息體存在一樣的情況,頻率還不是很低,這麼一個背景,我非常容易懷疑到,兩個消息同時近到這一個事務裡面導致的,但是是偶發的,又模擬不出來什麼場景會導致死鎖,只能進行代碼分析,問題還原的方式去排查問題。

業務代碼簡化成下麵

begin


update test set yn = 0 where dm_code = "3";
SELECT  * from test where dm_code = '3'
INSERT INTO demand_flow_followers (dm_code, erp )
values
('3', 'a')
,
('3', 'b')
,
('3', 'c')





也就是說先update ,select , insert 這麼一個順序

表中存在dm_code ,erp 唯一索引
如果不存在索引 第一行update 會導致行鎖升級為表鎖,反而不會導致問題出現,但是併發太差

結論

先說結論:

session1 session2
開啟事務
update
開啟事務
update
insert
insert出現死鎖

重點: 無論哪個事務insert,兩個事務必須都update 完成,只要滿足這個條件,兩個insert執行的時候就會報死鎖

原因:我先按照自己的理解解釋下:

innodb的行鎖,存在間隙鎖,為啥要去有索引,如果沒有索引,第一個update 就直接進行了表鎖,這樣導致另外一個事務無法進入,就只能進行等待了。

有索引的情況下:

兩個事務都執行update,都拿到了[當前值,+∞) 的鎖(記錄鎖+間隙鎖),(update的時候,無數據命中)
第一個insert時,希望等待另外一個事務釋放鎖。第二個事務希望第一個事務釋放鎖,因此出現了死鎖問題

相關知識梳理

InnoDB有三種行鎖的演算法:

1.Record Lock:是加在索引記錄上的。

2.Gap Lock(間隙鎖):對索引記錄間的範圍加鎖,或者加在最後一個索引記錄的前面或者後面

3.Next-Key Lock:前兩種鎖的結合,鎖定一個範圍,並且鎖定記錄本身,主要目的是解決幻讀的問題。

間隙鎖主要是防止幻象讀,用在Repeated-Read(簡稱RR)隔離級別下。在Read-Commited(簡稱RC)下,一般沒有間隙鎖(有外鍵情況下例外,此處不考慮)。間隙鎖還用於statement based replication

間隙鎖有些副作用,如果要關閉,一是將會話隔離級別改到RC下,或者開啟 innodb_locks_unsafe_for_binlog(預設是OFF)。

間隙鎖(無論是S還是X)只會阻塞insert操作。

CREATE TABLE `test` (


  `id` bigint(20) NOT NULL,


  `k` bigint(20) DEFAULT '0',


  PRIMARY KEY (`id`),


  KEY `idx_k` (`k`)


) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT into test values(2,2),(5,5),(10,10)



select @@global.tx_isolation, @@tx_isolation;



RR隔離級別

delete from test where k=5;



session2

insert into test (id,k) values (3,3)
insert into test (id,k) values (4,4)
insert into test (id,k) values (6,6)
insert into test (id,k) values (7,7)
insert into test (id,k) values (8,8)
insert into test (id,k) values (9,9)



上面都報錯:ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
這個證明id (3,5)都被間隙鎖鎖住了

insert into test (id,k) values (1,1)
insert into test (id,k) values (11,11)
delete from test where id in (1,11)



(3,5) 區間之外都可以執行insert,delete操作

可以看到,delete k=5的記錄阻塞了k=3、4、5、6、7、8、9記錄的插入操作,事實上,除了對於k=5這條記錄上record lock之外,innoDB對於delete和update在輔助索引(非主鍵索引)上的條件時會對掃過的記錄上間隙鎖,為了防止幻讀,會鎖住k=5這條記錄的前面一條記錄(id=2,k=2)到後面一條記錄(id=10,k=10)之間的區間,即鎖住k在區間(2,10)的範圍(如果沒有後一條記錄,一直鎖到正無窮),至於在邊界k=2及k=10上,由於索引內是按照主鍵排序的,不會鎖住(id<2,k=2)但是會鎖住(id>2,k=2),同理不會鎖住(id>10,k=10)但是會鎖住(id<10,k=10).

insert into test (id,k) values (1,2) ok
insert into test (id,k) values (11,2) no
insert into test (id,k) values (11,9) no
insert into test (id,k) values (11,10) ok
insert into test (id,k) values (1,10) no
insert into test (id,k) values (11,10) ok



由於索引內是按照主鍵排序的,不會鎖住(id<2,k=2)但是會鎖住(id>2,k=2),同理不會鎖住(id>10,k=10)但是會鎖住(id<10,k=10).

值得註意的是,delete和update在唯一索引(primary key/unique key)上更新存在的記錄時只會上行級記錄鎖(record key),而在唯一索引上更新不存在的記錄時同輔助索引一樣會上間隙鎖;在上例中,delete id=5只會在(id=5,k=5)這條記錄上上X鎖,而delete id=7卻會鎖住(id>5&&id<10)這個區間。

線上問題還原

session1 session2
begin
begin
update test set k = 20 where id = 20
update test set k = 20 where id = 20
INSERT into test values(25,25)
INSERT into test values(25,25)

重點: insert 之前兩個回話都執行完update

SQL 錯誤 [1213] [40001]: Deadlock found when trying to get lock; try restarting transaction



解決辦法:

避免更新或者刪除不存在的記錄,雖然更新存在的記錄也會產生間隙鎖,但是間隙鎖鎖住的範圍會更小;

更新不存在的記錄會鎖住意想不到的區間範圍,極其容易導致死鎖問題

這些僅僅是解決問題的一個小的技巧,不能從根本上解決問題,如果想從根本上解決就從代碼級別上加鎖,這樣避免了這種問題,但是同時併發就小了,根據自己的實際情況進行定奪方案

作者:京東零售 吳法剛

來源:京東雲開發者社區 轉載請註明來源


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Seata 簡介 傳統的單體應用中,業務操作使用同一條連接操作不同的數據表,一旦出現異常就可以整體回滾。隨著公司的快速發展、業務需求的變化,單體應用被拆分成微服務應用,原來的單體應用被拆分成多個獨立的微服務,分別使用獨立的數據源,業務操作需要調用三個服務來完成。此時每個服務內部的數據一致性由本地事務 ...
  • Css實現瀏覽滾動條效果 前言 也是有大半個月沒有更新文章了,大部分時間都在玩,然後就是入職的事。今天就更新一個小知識,刷抖音的時候看到的,感覺還不錯。 屬性介紹 關鍵屬性animation-timeline:動畫名稱; 用於控制動畫的時間軸。它可以讓你在一個元素上同時播放多個動畫,控制它們的開始時 ...
  • 一、官網下載JDK1.8 https://www.oracle.com/java/technologies/oracle-java-archive-downloads.html JDK1.8 因為1.8是目前項目中用到最多的 基本都是基於JDK1.8 可以直接在虛擬機中的瀏覽器訪問下載,但是嘗試過的 ...
  • 一、許可權介紹 在Linux中分別有讀、寫、執行許可權: 讀許可權: 對於文件夾來說,讀許可權影響用戶是否能夠列出目錄結構 對於文件來說,讀許可權影響用戶是否可以查看文件內容 寫許可權: 對文件夾來說,寫許可權影響用戶是否可以在文件夾下“創建/刪除/複製到/移動到”文檔 對於文件來說,寫許可權影響用戶是否可以編輯文 ...
  • 一、概念 資料庫:DataBase,簡稱DB。按照一定格式存儲數據的一些文件的組合顧名思義: 存儲數據的倉庫,實際上就是一堆文件。這些文件中存儲了具有特定格式的數據。 資料庫管理系統:DataBaseManagement,簡稱DBMS。資料庫管理系統是專門用來管理資料庫中數據的,資料庫管理系統可以對 ...
  • 目錄一.準備備份腳本並拷貝進容器二,在宿主機寫定時任務去執行容器內的備份腳本 一.準備備份腳本並拷貝進容器 vi backup.sh #內容如下 #!/bin/bash # PostgreSQL database credentials DB_NAME="<要備份的資料庫名>" DB_USER="< ...
  • 本文分享自華為雲社區《GaussDB(DWS)性能調優:MERGE場景下語句不下推引起的性能瓶頸問題案例》,作者:O泡果奶~。 1、【問題描述】 語句執行時間過長,且該語句performance執行計劃中SQL Diagnostic Information顯示SQL語句不下推,理由為:Type of ...
  • 數據泵(impdb)導入Oracle資料庫 一.sqlplus登錄目標資料庫,創建導入的目錄路徑 #該目錄要在導入的資料庫本機建立,如果是docker就在容器內部創建 create directory data_dir as '/home/oracle/prd_imp/prd_dump'; data ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...