色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

在Repeatable Read的隔離級別下使用select for update可能引發的死鎖問題

數據分析與開發 ? 來源:數據分析與開發 ? 作者:數據分析與開發 ? 2020-09-24 15:47 ? 次閱讀

本文針對MySQL InnoDB中在Repeatable Read的隔離級別下使用select for update可能引發的死鎖問題進行分析。

1. 業務案例

業務中需要對各種類型的實體進行編號,例如對于x類實體的編號可能是x201712120001,x201712120002,x201712120003類似于這樣。可以觀察到這類編號有兩個部分組成:x+日期作為前綴,以及流水號(這里是四位的流水號)。

如果用數據庫表實現一個能夠分配流水號的需求,無外乎就可以建立一個類似于下面的表:

CREATETABLEnumber ( prefix VARCHAR(20) NOTNULLDEFAULT''COMMENT'前綴碼', valueBIGINTNOTNULLDEFAULT0COMMENT'流水號', UNIQUEKEY uk_prefix(prefix) );

那么在業務層,根據業務規則得到編號的前綴比如x20171212,接下去就可以在代碼中起事務,用select for update進行如下的控制。

@Transactional long acquire(String prefix) { SerialNumber current = dao.selectAndLock(prefix); if (current == null) { dao.insert(new Record(prefix, 1)); return1; } else { current.number++; dao.update(current); return current.number; } }

這段代碼做的事情其實就是加鎖篩選,有則更新,無則插入,然而在Repeatable Read的隔離級別下這段代碼是有潛在死鎖問題的。(另一處與事務傳播行為相關的問題也會在下文提及)。

2. 分析與解決

當可以通過select for update的where條件篩出記錄時,上面的代碼是不會有deadlock問題的。然而當select for update中的where條件無法篩選出記錄時,這時在有多個線程執行上面的acquire方法時是可能會出現死鎖的。

2.1 一個簡單的復現場景

下面通過一個比較簡單的例子復現一下這個場景首先給表里初始化3條數據。

insertintonumberselect'bbb',2; insertintonumberselect'hhh',8; insertintonumberselect'yyy',25;

接著按照如下的時序進行操作:

session 1 session 2
begin;
begin;
select * from number where prefix='ddd' for update;
select * from number where prefix='fff' for update
insert into number select 'ddd',1
鎖等待中 insert into number select 'fff',1
鎖等待解除 死鎖,session 2的事務被回滾

2.2 分析下這個死鎖

通過查看show engine innodb status的信息,我們慢慢地觀察每一步的情況:

2.2.1 session1做了select for update

------------TRANSACTIONS------------Trx id counter 238435Purge done for trx's n:o < 238430 undo n:o < 0 state: running but idleHistory list length 13LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 281479459589696, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 281479459588792, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 238434, ACTIVE 3 sec2 lock struct(s), heap size 1136, 1 row lock(s)MySQL thread id 160, OS thread handle 123145573965824, query id 69153 localhost rootTABLE LOCK table?test.number?trx id 238434 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238434 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;

事務238434拿到了hhh前的gap鎖,也就是('bbb', 'hhh')的gap鎖。

2.2.2 session2做了select for update

------------TRANSACTIONS------------Trx id counter 238436Purge done for trx's n:o < 238430 undo n:o < 0 state: running but idleHistory list length 13LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 281479459589696, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 238435, ACTIVE 3 sec2 lock struct(s), heap size 1136, 1 row lock(s)MySQL thread id 161, OS thread handle 123145573408768, query id 69155 localhost rootTABLE LOCK table?test.number?trx id 238435 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238435 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;---TRANSACTION 238434, ACTIVE 30 sec2 lock struct(s), heap size 1136, 1 row lock(s)MySQL thread id 160, OS thread handle 123145573965824, query id 69153 localhost rootTABLE LOCK table?test.number?trx id 238434 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238434 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;

事務238435也拿到了hhh前的gap鎖。

截自InnoDB的lock_rec_has_to_wait方法實現,可以看到的LOCK_GAP類型的鎖只要不帶有插入意向標識,不必等待其它鎖(表鎖除外)

2.2.3 session1嘗試insert

------------TRANSACTIONS------------Trx id counter 238436Purge done for trx's n:o < 238430 undo n:o < 0 state: running but idleHistory list length 13LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 281479459589696, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 238435, ACTIVE 28 sec2 lock struct(s), heap size 1136, 1 row lock(s)MySQL thread id 161, OS thread handle 123145573408768, query id 69155 localhost rootTABLE LOCK table?test.number?trx id 238435 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238435 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;---TRANSACTION 238434, ACTIVE 55 sec insertingmysql tables in use 1, locked 1LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)MySQL thread id 160, OS thread handle 123145573965824, query id 69157 localhost root executinginsert into number select 'ddd',1------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238434 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;

TABLE LOCK tabletest.numbertrx id 238434 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of tabletest.numbertrx id 238434 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of tabletest.numbertrx id 238434 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;

可以看到,這時候事務238434在嘗試插入'ddd',1時,由于發現其他事務(238435)已經有這個區間的gap鎖,因此innodb給事務238434上了插入意向鎖,鎖的模式為LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION,等待事務238435釋放掉gap鎖。

截取自InnoDB的lock_rec_insert_check_and_lock方法實現

2.2.4 session2嘗試insert

------------------------LATEST DETECTED DEADLOCK------------------------2017-12-21 2240 0x70001028a000*** (1) TRANSACTION:TRANSACTION 238434, ACTIVE 81 sec insertingmysql tables in use 1, locked 1LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)MySQL thread id 160, OS thread handle 123145573965824, query id 69157 localhost root executinginsert into number select 'ddd',1*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of tabletest.numbertrx id 238434 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;*** (2) TRANSACTION:TRANSACTION 238435, ACTIVE 54 sec insertingmysql tables in use 1, locked 13 lock struct(s), heap size 1136, 2 row lock(s)MySQL thread id 161, OS thread handle 123145573408768, query id 69159 localhost root executinginsert into number select 'fff',1*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of tabletest.numbertrx id 238435 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of tabletest.numbertrx id 238435 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;*** WE ROLL BACK TRANSACTION (2)

TRANSACTIONS

Trx id counter 238436Purge done for trx's n:o < 238430 undo n:o < 0 state: running but idleHistory list length 13LIST OF TRANSACTIONS FOR EACH SESSION:---TRANSACTION 281479459589696, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 281479459588792, not started0 lock struct(s), heap size 1136, 0 row lock(s)---TRANSACTION 238434, ACTIVE 84 sec3 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1MySQL thread id 160, OS thread handle 123145573965824, query id 69157 localhost rootTABLE LOCK table?test.number?trx id 238434 lock mode IXRECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238434 lock_mode X locks gap before recRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;Record lock, heap no 7 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 646464; asc ddd;;1: len 6; hex 00000003a362; asc b;;2: len 7; hex de000001e60110; asc ;;3: len 8; hex 8000000000000001; asc ;;RECORD LOCKS space id 1506 page no 3 n bits 80 index uk_prefix of table?test.number?trx id 238434 lock_mode X locks gap before rec insert intentionRecord lock, heap no 3 PHYSICAL RECORD: n_fields 4; compact format; info bits 00: len 3; hex 686868; asc hhh;;1: len 6; hex 00000003a350; asc P;;2: len 7; hex d2000001ff0110; asc ;;3: len 8; hex 8000000000000008; asc ;;

到了這里,我們可以從死鎖信息中看出,由于事務238435在插入時也發現了事務238434的gap鎖,同樣加上了插入意向鎖,等待事務238434釋放掉gap鎖。因此出現死鎖的情況。

2.3 debug it!

接下來通過debug MySQL的源碼來重新復現上面的場景。

這里session2的事務4445加鎖的type_mode為515,也即(LOCK_X | LOCK_GAP),與session1事務的鎖4444的gap鎖lock2->type_mode=547(LOCK_X | LOCK_REC | LOCK_GAP)的lock_mode是不兼容的(兩者皆為LOCK_X)。然而由于type_mode滿足LOCK_GAP且不帶有LCK_INSERT_INTENTION的標識位,這里會判定為不需要等待。因此,第二個session執行select for update也同樣成功加上gap鎖了。

這里sesion1事務4444執行insert時type_mode為2563(LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION),由于帶有LOCK_INSERT_INTENTION標識位,因此需要等待session2事務釋放4445的gap鎖。后續session1事務4444獲得了一個插入意向鎖,并且在等待session2事務4445釋放gap鎖。

這里session2事務4445同樣執行了insert操作,插入意向鎖需要等待session1的事務4444的gap鎖釋放。在死鎖檢測時,被探測到形成等待環。因此InnoDB會選擇一個事務作為victim進行回滾。其過程大致如下:

session2嘗試獲取插入意向鎖,需要等待session1的gap鎖

session1事務的插入意向鎖處于等待中

session1事務插入意向鎖在等待session2的gap鎖

形成環路,檢測到死鎖

2.4 如何避免這個死鎖

我們已經知道,這種情況出現的原因是:兩個session同時通過select for update,并且未命中任何記錄的情況下,是有可能得到相同gap的鎖的(要看where篩選條件是否落在同一個區間。如果上面的案例如果一個session準備插入'ddd'另一個準備插入'kkk'則不會出現沖突,因為不是同一個gap)。此時再進行并發插入,其中一個會進入鎖等待,待第二個session進行插入時,會出現死鎖。MySQL會根據事務權重選擇一個事務進行回滾。

那么如何避免這個情況呢?一種解決辦法是將事務隔離級別降低到Read Committed,這時不會有gap鎖,對于上述場景,如果where中條件不同即最終要插入的鍵不同,則不會有問題。如果業務代碼中可能不同線程會嘗試對相同鍵進行select for update,則可在業務代碼中捕獲索引沖突異常進行重試。此外,上面代碼示例中的代碼還有一處值得注意的地方是事務注解@Transactional的傳播機制,對于這類與主流程事務關系不大的方法,應當將事務傳播行為改為REQUIRES_NEW。原因有兩點:

因為這里的解決方案是對隔離級別降級,如果傳播行為仍然是默認的話,在外層事務隔離級別不是RC的情況下,會拋出IllegalTransactionStateException異常(在你的TransactionManager開啟了validateExistingTransaction校驗的情況下)。

如果加入外層事務的話,某個線程在執行獲取流水號的時候可能會因為另一個線程的與流水號不相關的事務代碼還沒執行完畢而阻塞。

責任編輯:xj

原文標題:select for update 引發的死鎖分析,太驚險了

文章出處:【微信公眾號:數據分析與開發】歡迎添加關注!文章轉載請注明出處。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 死鎖
    +關注

    關注

    0

    文章

    25

    瀏覽量

    8078
  • MySQL
    +關注

    關注

    1

    文章

    816

    瀏覽量

    26606

原文標題:select for update 引發的死鎖分析,太驚險了

文章出處:【微信號:DBDevs,微信公眾號:數據分析與開發】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    Linux--IO多路復用(select,poll,epoll)

    顯式調用讀寫操作。這意味著異步IO模型中,讀寫操作由操作系統在后臺完成,從而進一步提高了應用程序的效率和響應性。select概述系統提供了select函數來實現多路復用輸入/輸出模型sele
    的頭像 發表于 11-06 16:13 ?312次閱讀

    電廠衛星時鐘加裝授時安全隔離防護的解決方案

    電廠從發電、輸電到配電的每一個環節,任何一個細微的時間誤差都可能引發調度失誤,甚至導致安全事故。因此,選擇適合的電廠衛星授時安全隔離防護裝置,是確保電力系統高效、安全運行的關鍵步驟。
    的頭像 發表于 10-18 14:50 ?406次閱讀
    電廠衛星時鐘加裝授時安全<b class='flag-5'>隔離</b>防護的解決方案

    使用TAS3251EVM進行PBTL的調試,為什么選擇Select Audio Mode后會報錯?

    請教一下,我使用TAS3251EVM進行PBTL的調試,連接好PPC3和硬件,為什么選擇 Select Audio Mode后會報錯。已經將J31&amp;J32短接,J14開路
    發表于 10-15 07:30

    MOS管溫度過高會引發什么故障

    MOS管(金屬氧化物半導體場效應晶體管)溫度過高會引發一系列故障,這些故障不僅影響MOS管本身的性能,還可能對整個電路系統造成損害。以下是對MOS管溫度過高可能引發的故障及其原因的詳細
    的頭像 發表于 10-09 14:27 ?1166次閱讀

    隔離電源的地波動大,隔離電源的地怎么處理

     隔離電源電氣系統中起到重要作用,它通過將電氣系統的某部分與電源的地線或其他部分進行電氣隔離,以減少干擾和確保安全。然而,如果你發現隔離電源的地波動大,這
    的頭像 發表于 10-01 16:15 ?499次閱讀

    高壓隔離開關的常見缺陷

    缺陷的詳細描述。 一、接觸部分過熱 高壓隔離開關的運行過程中,接觸部分過熱是一個常見的問題。這通常是由于擰緊部件松動或刀口合得不嚴所導致的。當接觸部分過熱時,可能引發一系列問題,如
    的頭像 發表于 09-19 16:32 ?452次閱讀

    隔離變壓器輸入輸出可以隨便接嗎

    的額定電壓一致。如果電壓不匹配,可能會造成變壓器損壞或電器無法正常工作,甚至引發安全事故。 相位正確 :對于三相隔離變壓器,輸入和輸出的相位必須正確對應,以確保電流的正常流通和設備的正常工作。 電氣
    的頭像 發表于 09-06 11:07 ?890次閱讀

    控制回路斷線可能原因及如何處理

    回路斷線的常見原因之一。電氣系統中,接線錯誤可能導致電路短路、斷路或接觸不良,從而引發控制回路斷線。 接觸不良 接觸不良是控制回路斷線的另一個常見原因。電氣系統中,接觸不良
    的頭像 發表于 08-23 16:36 ?2212次閱讀

    ROOT NODE用了mlink_httpd_read之后可以使用mwifi_root_read嗎?

    我想問一下,如果我ROOT NODE用了mlink_httpd_read 之后可以使用mwifi_root_read 嗎? 我root node create 了這兩個task
    發表于 06-28 09:35

    淺談MySQL常見死鎖場景

    這里問題的原因是這個 table 里面只有record 2, 所以這里認真看, 死鎖的時候是等待在 supremum 上的, 因為supremum 的特殊性, supremum 沒有gap lock, 只有 next-key lock
    的頭像 發表于 03-21 14:10 ?789次閱讀
    淺談MySQL常見<b class='flag-5'>死鎖</b>場景

    STM32L5 boot_lock與rdp level配置導致死鎖如何解決?

    STM32L5 boot_lock 與 rdp level配置導致死鎖,應該如何解決
    發表于 03-20 06:22

    TIA V17 Update 5的密碼PLC和安全程序設置

    隨著切換到TIA V17 Update 5,密碼強度要求已進行了調整。
    的頭像 發表于 01-25 10:27 ?1169次閱讀
    TIA V17 <b class='flag-5'>Update</b> 5的密碼PLC和安全程序設置

    ADUCM360如何解除死鎖問題?

    如何解除死鎖問題? 芯片里面的程序將UCLK設置成了片外的32.768晶振,但晶振沒有啟動,就再也無法使用JLINK進入,請問怎樣能夠擦除程序? 芯片上電后就開始執行內部程序了,然后就卡死了(忘打開
    發表于 01-15 06:01

    VisionFive2上按官方的文檔燒錄edk2到QSPI flash失敗了的原因?

    on given SPI bus and chip select sf read addr offset|partition len - read len\' bytes starting at offset
    發表于 01-10 06:51

    隔離探頭的基本原理 光隔離探頭的作用

    )時,可能會發生不同的功率損耗、干涉和反射等現象,從而影響光信號的傳輸質量和系統的性能表現。而光隔離探頭的基本原理就是通過特殊的設計和組裝結構,使得光信號只能在一個方向上傳輸,從而有效地隔離光信號,防止其
    的頭像 發表于 01-08 16:34 ?1438次閱讀
    主站蜘蛛池模板: 久久国产精品麻豆AV影视| 全部老头和老太XXXXX| 性欧美videosex18嫩| 国产喷水1区2区3区咪咪爱AV| 羞羞漫画在线播放| 恋老视频 国产国佬| A级韩国乱理伦片在线观看| 日韩视频中文在线一区| 国产中文在线| 799是什么意思网络用语| 日韩一区二区三区视频在线观看| 国产亚洲精品线观看不卡| 最新果冻传媒在线观看免费版 | 国产亚洲一区在线| 在线 国产 欧美 亚洲 天堂| 青春草国产成人精品久久| 国产中文字幕免费观看| 99国产精品偷窥熟女精品视频| 色欲狠狠躁天天躁无码中文字幕 | 精品国产品在线18年| 99久久香蕉国产线看观看| 文中字幕一区二区三区视频播放 | 国产亚洲精品久久777777| 97综合久久| 亚洲精品视频区| 欧美性猛交AAA片| 久久91精品国产91久久户| 电影果冻传媒在线播放| 在线欧美精品一区二区三区| 无码国产伦一区二区三区视频| 美女夫妻内射潮视频| 国产午夜精品美女免费大片| 99在线免费视频| 亚洲午夜精品A片久久WWW软件| 日韩欧美中文字幕在线| 乐乐亚洲精品综合影院| 国产色婷婷亚洲99精品| yellow日本高清在线| 佐山爱巨大肥臀在线| 亚洲欧美成人无码久久久| 双性h浪荡受bl|