国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > MySQL > 正文

如何修復MySQL死鎖問題

2024-07-24 12:33:16
字體:
來源:轉載
供稿:網友
  這篇文章主要介紹“如何解決MySQL死鎖問題”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“如何解決MySQL死鎖問題”文章能幫助大家解決問題。
 
  1、什么是死鎖
 
  死鎖是并發系統中常見的問題,同樣也會出現在數據庫MySQL的并發讀寫請求場景中。當兩個及以上的事務,雙方都在等待對方釋放已經持有的鎖或因為加鎖順序不一致造成循環等待鎖資源,就會出現“死鎖”。常見的報錯信息為 Deadlock found when trying to get lock...。
 
  舉例來說 A 事務持有 X1 鎖 ,申請 X2 鎖,B事務持有 X2 鎖,申請 X1 鎖。A 和 B 事務持有鎖并且申請對方持有的鎖進入循環等待,就造成了死鎖。
 
  是右側的四輛汽車資源請求產生了回路現象,即死循環,導致了死鎖。
 
  從死鎖的定義來看,MySQL 出現死鎖的幾個要素為:
 
  兩個或者兩個以上事務
 
  每個事務都已經持有鎖并且申請新的鎖
 
  鎖資源同時只能被同一個事務持有或者不兼容
 
  事務之間因為持有鎖和申請鎖導致彼此循環等待
 
  2、InnoDB 鎖類型
 
  為了分析死鎖,我們有必要對 InnoDB 的鎖類型有一個了解。
 
  MySQL InnoDB 引擎實現了標準的行級別鎖:共享鎖( S lock ) 和排他鎖 ( X lock )
 
  不同事務可以同時對同一行記錄加 S 鎖。
 
  如果一個事務對某一行記錄加 X 鎖,其他事務就不能加 S 鎖或者 X 鎖,從而導致鎖等待。
 
  如果事務 T1 持有行 r 的 S 鎖,那么另一個事務 T2 請求 r 的鎖時,會做如下處理:
 
  T2 請求 S 鎖立即被允許,結果 T1 T2 都持有 r 行的 S 鎖
 
  T2 請求 X 鎖不能被立即允許
 
  如果 T1 持有 r 的 X 鎖,那么 T2 請求 r 的 X、S 鎖都不能被立即允許,T2 必須等待 T1 釋放 X 鎖才可以,因為 X 鎖與任何的鎖都不兼容。共享鎖和排他鎖的兼容性如下所示:
 
  2.1、間隙鎖( gap lock )
 
  間隙鎖鎖住一個間隙以防止插入。假設索引列有2, 4, 8 三個值,如果對 4 加鎖,那么也會同時對(2,4)和(4,8)這兩個間隙加鎖。其他事務無法插入索引值在這兩個間隙之間的記錄。但是,間隙鎖有個例外:
 
  如果索引列是唯一索引,那么只會鎖住這條記錄(只加行鎖),而不會鎖住間隙。
 
  對于聯合索引且是唯一索引,如果 where 條件只包括聯合索引的一部分,那么依然會加間隙鎖。
 
  2.2、next-key lock
 
  next-key lock 實際上就是 行鎖+這條記錄前面的 gap lock 的組合。假設有索引值10,11,13和 20,那么可能的 next-key lock 包括:
 
  (負無窮,10],(10,11],(11,13],(13,20],(20,正無窮)
 
  在 RR 隔離級別下,InnoDB 使用 next-key lock 主要是防止幻讀問題產生。
 
  2.3、意向鎖( Intention lock )
 
  InnoDB 為了支持多粒度的加鎖,允許行鎖和表鎖同時存在。為了支持在不同粒度上的加鎖操作,InnoDB 支持了額外的一種鎖方式,稱之為意向鎖( Intention Lock )。意向鎖是將鎖定的對象分為多個層次,意向鎖意味著事務希望在更細粒度上進行加鎖。意向鎖分為兩種:
 
  意向共享鎖( IS ):事務有意向對表中的某些行加共享鎖
 
  意向排他鎖( IX ):事務有意向對表中的某些行加排他鎖
 
  由于 InnoDB 存儲引擎支持的是行級別的鎖,因此意向鎖其實不會阻塞除全表掃描以外的任何請求。表級意向鎖與行級鎖的兼容性如下所示:
 
  2.4、插入意向鎖( Insert Intention lock )
 
  插入意向鎖是在插入一行記錄操作之前設置的一種間隙鎖,這個鎖釋放了一種插入方式的信號,即多個事務在相同的索引間隙插入時如果不是插入間隙中相同的位置就不需要互相等待。假設某列有索引值2,6,只要兩個事務插入位置不同(如事務 A 插入3,事務 B 插入4),那么就可以同時插入。
 
  2.5、鎖模式兼容矩陣
 
  橫向是已持有鎖,縱向是正在請求的鎖:
 
  3、閱讀死鎖日志
 
  在進行具體案例分析之前,咱們先了解下如何去讀懂死鎖日志,盡可能地使用死鎖日志里面的信息來幫助我們來解決死鎖問題。
 
  后面測試用例的數據庫場景如下:MySQL 5.7 事務隔離級別為 RR

  通過執行show engine innodb status 可以查看到最近一次死鎖的日志。
 
  3.1、日志分析如下:
 
  1.***** (1) TRANSACTION: TRANSACTION 2322, ACTIVE 6 sec starting index read
 
  事務號為2322,活躍 6秒,starting index read 表示事務狀態為根據索引讀取數據。常見的其他狀態有:
 
  mysql tables in use 1 說明當前的事務使用一個表。
 
  locked 1 表示表上有一個表鎖,對于 DML 語句為 LOCK_IX
 
  LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
 
  LOCK WAIT 表示正在等待鎖,2 lock struct(s) 表示 trx->trx_locks 鎖鏈表的長度為2,每個鏈表節點代表該事務持有的一個鎖結構,包括表鎖,記錄鎖以及自增鎖等。本用例中 2locks 表示 IX 鎖和lock_mode X (Next-key lock)
 
  1 row lock(s) 表示當前事務持有的行記錄鎖/ gap 鎖的個數。
 
  MySQL thread id 37, OS thread handle 140445500716800, query id 1234 127.0.0.1 root updating
 
  MySQL thread id 37 表示執行該事務的線程 ID 為 37 (即 show processlist; 展示的 ID )
 
  delete from student where stuno=5 表示事務1正在執行的 sql,比較難受的事情是 show engine innodb status 是查看不到完整的 sql 的,通常顯示當前正在等待鎖的 sql。
 
   ***** (1) WAITING FOR THIS LOCK TO BE GRANTED:
 
  RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2322 lock_mode X waiting
 
  RECORD LOCKS 表示記錄鎖, 此條內容表示事務 1 正在等待表 student 上的 idx_stuno 的 X 鎖,本案例中其實是 Next-Key Lock 。
 
  事務2的 log 和上面分析類似:
 
  2.***** (2) HOLDS THE LOCK(S):
 
  RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X
 
  顯示事務 2 的 insert into student(stuno,score) values(2,10) 持有了 a=5 的 Lock mode X
 
  LOCK_gap,不過我們從日志里面看不到事務2執行的 delete from student where stuno=5;
 
  這點也是造成 DBA 僅僅根據日志難以分析死鎖的問題的根本原因。
 
  3.***** (2) WAITING FOR THIS LOCK TO BE GRANTED:
 
  RECORD LOCKS space id 11 page no 5 n bits 72 index idx_stuno of table cw****.****student trx id 2321 lock_mode X locks gap before rec insert intention waiting
 
  表示事務 2 的 insert 語句正在等待插入意向鎖 lock_mode X locks gap before rec insert intention waiting ( LOCK_X + LOCK_REC_gap )
 
  4、經典案例分析
 
  4.1、事務并發 insert 唯一鍵沖突
 
  日志分析如下:
 
  事務 T2 insert into t7(id,a) values (26,10) 語句 insert 成功,持有 a=10 的 排他行鎖( Xlocks rec but no gap )
 
  事務 T1 insert into t7(id,a) values (30,10), 因為T2的第一條 insert 已經插入 a=10 的記錄,事務 T1 insert a=10 則發生唯一鍵沖突,需要申請對沖突的唯一索引加上S Next-key Lock( 即 lock mode S waiting ) 這是一個間隙鎖會申請鎖住(,10],(10,20]之間的 gap 區域。
 
  事務 T2 insert into t7(id,a) values (40,9)該語句插入的 a=9 的值在事務 T1 申請的 gap 鎖4-10之間, 故需事務 T2 的第二條 insert 語句要等待事務 T1 的 S-Next-key Lock 鎖釋放,在日志中顯示 lock_mode X locks gap before rec insert intention waiting 。
 
  4.2、先 update 再 insert 的并發死鎖問題
 
  可以看到兩個事務 update 不存在的記錄,先后獲得間隙鎖( gap 鎖),gap 鎖之間是兼容的所以在update環節不會阻塞。兩者都持有 gap 鎖,然后去競爭插入意向鎖。當存在其他會話持有 gap 鎖的時候,當前會話申請不了插入意向鎖,導致死鎖。
 
  5、如何盡可能避免死鎖
 
  合理的設計索引,區分度高的列放到組合索引前面,使業務 SQL 盡可能通過索引定位更少的行,減少鎖競爭。
 
  調整業務邏輯 SQL 執行順序, 避免 update/delete 長時間持有鎖的 SQL 在事務前面。
 
  避免大事務,盡量將大事務拆成多個小事務來處理,小事務發生鎖沖突的幾率也更小。
 
  以固定的順序訪問表和行。比如兩個更新數據的事務,事務 A 更新數據的順序為 1,2;事務 B 更新數據的順序為 2,1。這樣更可能會造成死鎖。
 
  在并發比較高的系統中,不要顯式加鎖,特別是是在事務里顯式加鎖。如 select … for update 語句,如果是在事務里(運行了 start transaction 或設置了autocommit 等于0),那么就會鎖定所查找到的記錄。
 
  盡量按主鍵/索引去查找記錄,范圍查找增加了鎖沖突的可能性,也不要利用數據庫做一些額外額度計算工作。比如有的程序會用到 “select … where … order by rand();”這樣的語句,由于類似這樣的語句用不到索引,因此將導致整個表的數據都被鎖住。
 
  優化 SQL 和表設計,減少同時占用太多資源的情況。比如說,減少連接的表,將復雜 SQL 分解為多個簡單的 SQL。
 
  關于“如何解決MySQL死鎖問題”的內容就介紹到這里了,感謝大家的閱讀。
 

(編輯:武林網)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 卢湾区| 麻城市| 贵德县| 明星| 汉寿县| 连州市| 丹凤县| 盈江县| 舒兰市| 淮南市| 青田县| 安龙县| 祁东县| 舟山市| 大丰市| 搜索| 富宁县| 碌曲县| 普宁市| 福海县| 上思县| 大同市| 仁寿县| 彭泽县| 玉林市| 乐昌市| 阿拉善右旗| 盱眙县| 镶黄旗| 台州市| 容城县| 海原县| 图木舒克市| 郧西县| 海城市| 禹城市| 寿光市| 新津县| 天等县| 青阳县| 枞阳县|