MySQL的index merge(索引合併)導致資料庫死鎖分析與解決方案

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

在DBS-集群列表-更多-連接查詢-死鎖中,看到9月22日有資料庫死鎖日誌,後排查發現是因為mysql的優化-index merge(索引合併)導致資料庫死鎖。 ...


背景

在DBS-集群列表-更多-連接查詢-死鎖中,看到9月22日有資料庫死鎖日誌,後排查發現是因為mysql的優化-index merge(索引合併)導致資料庫死鎖。

定義

index merge(索引合併):該資料庫查詢優化的一種技術,在mysql 5.1之後進行引入,它可以在多個索引上進行查詢,並將結果合併返回。

mysql資料庫的鎖機制

在排查問題之前,首先講一下mysql資料庫的鎖機制:

1 加鎖的基本單位是 next-key lock(記錄鎖+間隙鎖),當記錄鎖或者間隙鎖能夠解決幻讀的問題,就會退化為記錄鎖(行鎖),間隙鎖。

2 加鎖是將鎖加在了索引之上,而不是數據之上。

3 對於當前讀,索引進行加鎖,當前讀語句包括了(select ... from. ... for update,select...from ..... lock in share mode,update...,delete....)。

4 加鎖根據唯一性索引、非唯一性索引進行了區分,根據查詢條件分為了等值查詢、範圍查詢,根據是否能夠查到數據又分為了記錄存在和不存在的情況。

本次死鎖問題使用的索引是非唯一性索引的等值查詢中記錄存在的情況,因此本文僅僅詳細介紹這種情況,其它情況可以查看最下麵的參考文檔1:

加鎖情況是:會依次掃描,首先掃描到條件匹配的數據,加一個next-key lock,然後接下來掃描到第一個記錄不匹配的數據,增加一個間隙鎖,最後對查到記錄的主鍵增加一個記錄鎖,

針對以上情況加了三種鎖,加鎖的目的是為了防止幻讀的發生。

針對二級索引的鎖進行分析:

表結構:

CREATE TABLE `jdi_roster_apply_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `apply_id` varchar(100) NOT NULL COMMENT '申請單號',
  `status` tinyint(10) NOT NULL COMMENT '狀態',
  PRIMARY KEY (`id`),
  KEY `idx_status` (`status`),
  KEY `idx_apply_id` (`apply_id`)
) ENGINE=InnoDB AUTO_INCREMENT=984483 DEFAULT CHARSET=utf8 COMMENT='黑白名單申請單明細'


表數據:

id apply_id status
959651 1695369220522068998 1
960738 1695369227576173690 1
961319 1695373047673903326 1
961365 1695373122447865228 1

通過 idx_apply_id建立的b+樹:

因為索引是二級索引,所以葉子節點存儲的數據是主鍵值。

執行sql:

select * from jdi_roster_apply_detail where apply_id='1695369227576173690' for update


執行數據掃描過程

1 查到符合條件的記錄,增加next-key 鎖,因此鎖是(1695369220522068998,1695369227576173690]

2 找到第一個不符合記錄的數據增加間隙鎖,因此鎖是 (1695369227576173690,1695373047673903326)

3 對符合條件的主鍵索引增加記錄鎖,因此對 id=960738,增加記錄鎖。

針對三種鎖解決的幻讀:

1 如果沒有第一條的next-key鎖, 另一個事務增加一個apply_id=1695369227576173690, id<960738 時,該事務在進行查詢時,會多一條記錄,因此會造成幻讀。

2 如果沒有第二條的 間隙鎖,另一個事務增加一個apply_id=1695369227576173690, id>960738是,該事務在進行查詢時,會多一條記錄,因此會造成幻讀。

3 如果沒有第三條的記錄鎖,另一條事務刪除一條 id=960738的記錄,該事務進行查詢時,會少一條數據,因此會造成幻讀。

實際問題分析

資料庫死鎖日誌

以上日誌兩個事務分別執行了update語句:

#事務1
update jdi_roster_apply_detail set `status` = 10 where `status` = 1 and apply_id = '1695369220522068998'
#事務2
update jdi_roster_apply_detail set `status` = 10 where `status` = 1 and apply_id = '1695369227576173690' 


這個sql是用於將某個申請單id待審批的數據改為已審批。

因為在泰山裡不能執行update語句 ,因此執行了select語句查看用的索引情況:

explain select * from  jdi_roster_apply_detail  where `status` = 1 and apply_id = '1695369220522068998'


執行的結果:

通過結果可以看出兩個update語句都使用了兩個索引,分別是idx_status,idx_apply_id,然後將查到的結果進行合併,因此在模擬的過程中,可以將其拆成兩個查詢語句。

死鎖模擬

事務1 事務2 鎖的範圍
begin begin
select * from jdi_roster_apply_detail where apply_id = '1695369220522068998' for update idx_apply_id所以鎖住了(-∞,1695369220522068998],(1695369220522068998,1695369227576173690) 主鍵id索引鎖住了 id=959651
select * from jdi_roster_apply_detail where apply_id = '1695369227576173690' for update idx_apply_id所以鎖住了(1695369220522068998,1695369227576173690],(1695369227576173690,1695373047673903326) 主鍵id索引鎖住了 id=960738
select * from jdi_roster_apply_detail where status = 1 for update 會對idx_status上加next-key鎖和間隙鎖,但是在對主鍵959651,960738,961319,961365進行加記錄鎖時,其中事務2 對960738已經加了記錄鎖,所以該事務1進行了阻塞。
select * from jdi_roster_apply_detail where status = 1 for update 會對idx_status上加next-key鎖和間隙鎖,但是在對主鍵959651,960738,961319,961365進行加記錄鎖時,其中事務1對959651已經加了記錄鎖,所以該事務2進行了阻塞。
deadlock

兩個事務分別想要兩個主鍵id的記錄鎖,造成相互等待,形成了死鎖。

以上是先執行idx_apply_id的索引查詢再執行idx_status索引查詢,如果先執行idx_status索引查詢,再執行idx_apply_id的索引查詢,也會因為主鍵的記錄鎖造成死鎖。

解決方案

1 利用force index(idx_apply_id)強制走某個索引,這樣InnoDB就會忽略index merge,避免多個索引同時加鎖的情況。

2 禁用Index Merge,用命令禁用Index Merge:SET GLOBAL optimizer_switch='index_merge=off,index_merge_union=off,index_merge_sort_union=off,index_merge_intersection=off';

3 Index Merge同時使用了2個獨立索引,因此新建一個包含這兩個索引所有欄位的聯合索引,這樣InnoDB就只會走這個單獨的聯合索引。

第三種方案相較於第一種查詢性能更好,相對於第二種僅僅作用於該表,影響範圍小,因此本次也是採用了該方案。

總結

該死鎖問題是因為優化器使用了合併索引問題導致的,最終通過新建一個聯合索引來解決這個問題。

參考文檔:

1 https://www.xiaolincoding.com/mysql/lock/how_to_lock.html

作者:京東工業 李小輝

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


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

-Advertisement-
Play Games
更多相關文章
  • C#核心 面向對象--封裝 用程式來抽象現實世界,(萬物皆對象)來編程實現功能。 三大特性:封裝、繼承、多態。 類與對象 聲明位置:namespace中 樣式:class 類名{} 命名:帕斯卡命名法(首字母大寫) 實例化對象:根據類來新建一個對象。Person p=new Person(); 成員 ...
  • <工作記錄——Report> 報表前的數據獲取操作是高重覆性的,今天差不多完成了腳本,下述是代碼: 1 // See https://aka.ms/new-console-template for more information 2 using System.IO; 3 using System. ...
  • 目錄一、微處理器與匯流排1.微處理器的概述1.1 運算器1.2 控制器1.2.1 指令控制1.2.2 時序控制1.2.3 操作控制二、8086/8088微處理器1.8086/8088CPU的指令特點1.1 指令流水線1.2 記憶體的分段管理技術1.3 支持多處理器系統2.8088/8086的外部引腳及其 ...
  • 本腳本通過域管下發預設為root許可權執行 #!/bin/bash cd /home #快捷方式圖標名稱 string_imgName="temp1.png" #快捷方式文件名稱 string_fileName="/changePassword.desktop" #快捷方式內容 function in ...
  • Tcpdump 抓包工具使用以及Wireshark解析pacp包 參考鏈接(比較詳細):https://blog.csdn.net/weixin_42866036/article/details/128004750 1.項目上數據在共用時,由於介面有Token鑒權動作,有時調用介面會出現鑒權失效問題 ...
  • 前言 存儲引擎都是把數據存儲在文件系統上,通過通過查詢命令,可以查看數據目錄所在的本機路徑。 mysql> SHOW VARIABLES LIKE 'datadir'; + + + | Variable_name | Value | + + + | datadir | /var/lib/mysql/ ...
  • Trino是一款開源的高性能、分散式SQL查詢引擎,專門用於對各種異構數據源運行互動式分析查詢,支持從GB到PB的數據量範圍。 ...
  • 解決SUM函數返回為NULL SUM函數的作用:計算某一欄位中所有行的數值和, 使用SUM函數進行對符合條件的結果行數進行求和。 問題產生: sum 求和時會對 null 進行過濾,不計算,但如果沒有返回結果,則sum 函數的返回值為 null,不是 0: 解決方式: 1. IFNULL 使用IFN ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...