精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深入剖析SQL死鎖-兩條SQL之間的死鎖原因

數據庫 其他數據庫
在業務實現中,若數據存在則修改,不存在則插入,通常大家會選用 INSERT INTO... ON DUPLICATE KEY UPDATE 語句讓數據庫實現此功能。近期在進行開發批量取消預約物流的場景中,由于一個物流對應多個訂單,同時取消多個物流時,接口可能會變慢。

1.問題背景

在業務實現中,若數據存在則修改,不存在則插入,通常大家會選用 INSERT INTO... ON DUPLICATE KEY UPDATE 語句讓數據庫實現此功能。近期在進行開發批量取消預約物流的場景中,由于一個物流對應多個訂單,同時取消多個物流時,接口可能會變慢。故采用了多線程,每個物流取消任務對應一個線程。然而,在測試過程中出現了意外問題,當同時取消兩個物流單時發生了數據庫死鎖。

2.問題復現

2.1 SHOW ENGINE INNODB STATUS尋找死鎖語句

通過SHOW ENGINE INNODB STATUS命令獲取MySQL的死鎖日志。

MySQL版本5.7

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-12-05 21:18:45 0x7fecb4759700
*** (1) TRANSACTION:
TRANSACTION 1366772472, ACTIVE 1 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 10 lock struct(s), heap size 1136, 6 row lock(s), undo log entries 3
MySQL thread id 7821432, OS thread handle 140654140307200, query id 1454780802 192.168.26.25 vault-0iPqpD update
insert into recycle_order_extend (id, recycle_order_id, param_id,
    value_ids, es_type) values
      
      (
      376510847366725657, 376473627618443479, 100031,
      '[]', 1
      )
     , 
      (
      376510847369871385, 376473627618443479, 100030,
      '[]', 1
      )
     
    on duplicate key update value_ids=values(`value_ids`), update_time=values(`update_time`)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 6314 page no 4758 n bits 192 index PRIMARY of table `dbzz_hunter_partner`.`recycle_order_extend` trx id 1366772472 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 000019d6cf63; asc      c;;
 2: len 7; hex aa000340390eea; asc    @9  ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;

*** (2) TRANSACTION:
TRANSACTION 1366772473, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
11 lock struct(s), heap size 1136, 10 row lock(s), undo log entries 8
MySQL thread id 7821433, OS thread handle 140654616614656, query id 1454780841 192.168.26.25 vault-0iPqpD update
insert into recycle_order_extend (id, recycle_order_id, param_id,
    value_ids, es_type) values
      
      (
      376510847508283417, 376473608578400471, 100031,
      '[]', 1
      )
     , 
      (
      376510847509331993, 376473608578400471, 100030,
      '[]', 1
      )
     
    on duplicate key update value_ids=values(`value_ids`), update_time=values(`update_time`)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 6314 page no 4758 n bits 192 index PRIMARY of table `dbzz_hunter_partner`.`recycle_order_extend` trx id 1366772473 lock_mode X locks gap before rec
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 000019d6cf63; asc      c;;
 2: len 7; hex aa000340390eea; asc    @9  ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 6314 page no 4758 n bits 192 index PRIMARY of table `dbzz_hunter_partner`.`recycle_order_extend` trx id 1366772473 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 000019d6cf63; asc      c;;
 2: len 7; hex aa000340390eea; asc    @9  ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)

通過日志很容易的找到產生死鎖的SQL語句。

在事務A執行回收單號為376473627618443479對應的數據修改時,會先處理單號376473601396703447對應的數據(通過SQL語句打印得知執行順序),所以是如下執行流程。

事務A:
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473601396703447, 100030, '[]', 1 ),
 ( 376510847369871385, 376473601396703447, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE  value_ids=values(`value_ids`), update_time = now();
----------------------------------------------------------------------------------------------------
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847508283417, 376473608578400471, 100030, '[]', 1 ),
 ( 376510847509331993, 376473608578400471, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE value_ids=values(`value_ids`), update_time = now();
事務B:
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473627618443479, 100030, '[]', 1 ),
 ( 376510847369871385, 376473627618443479, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE value_ids=values(`value_ids`), update_time = now();

2.2 問題復現

通過上面的日志找到了產生死鎖的SQL語句,那么我們就將測試數據導入到本地(本地數據庫8.0版本),嘗試復現問題。

步驟

事務A

事務B

1

begin;

begin;

2

INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )  VALUES ( 376510847366725657, 376473601396703447, 100030, '[]', 1 ), ( 376510847369871385, 376473601396703447, 100031, '[]', 1 ) ON DUPLICATE KEY UPDATE  value_ids=values(value_ids), update_time = now();

-

3

-

INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )     VALUES ( 376510847366725657, 376473627618443479, 100030, '[]', 1 ), ( 376510847369871385, 376473627618443479, 100031, '[]', 1 ) ON DUPLICATE KEY UPDATE value_ids=values(value_ids), update_time = now();


INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )    VALUES ( 376510847508283417, 376473608578400471, 100030, '[]', 1 ), ( 376510847509331993, 376473608578400471, 100031, '[]', 1 ) ON DUPLICATE KEY UPDATE value_ids=values(value_ids), update_time = now();

-

現象是事務A會把事務B進行阻塞,并不會死鎖。

獲取當前鎖的占用情況;

SELECT engine_transaction_id,index_name,lock_type,lock_mode,lock_status,lock_data  FROM performance_schema.data_locks;

圖片圖片

發現事務B會因為事務A持有的間隙鎖導致自己需要執行的插入意向鎖獲取失敗,進而進行阻塞。

3.問題思考

通過上述的問題驗證,發現根本無法復現問題,因為事務A一定會阻塞事務B,但是為什么又會出現死鎖的問題呢?

于是編寫本地測試用例,同時啟動兩個線程模仿修改表的任務,發現確實會死鎖,并且這次的死鎖現象更奇怪,僅僅A線程和B線程各執行了一條SQL就產生了死鎖,并且持有鎖和等待鎖都是RECORD LOCKS space id 98 page no 5231 n bits 152 index PRIMARY。

MySQL8.0版本

------------------------
LATEST DETECTED DEADLOCK
------------------------
2024-12-25 15:09:06 0x16c60f000
*** (1) TRANSACTION:
TRANSACTION 195596, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1128, 2 row lock(s)
MySQL thread id 51, OS thread handle 6136246272, query id 403 localhost 127.0.0.1 root update
insert into recycle_order_extend (id, recycle_order_id, param_id,
    value_ids, es_type) values
      
      (
      376510847366725657, 376473601396703447, 100031,
      '[]', 1
      )
     , 
      (
      376510847369871385, 376473601396703447, 100030,
      '[]', 1
      )
     
    on duplicate key update value_ids=values(`value_ids`), update_time=values(`update_time`)

*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 98 page no 5231 n bits 152 index PRIMARY of table `tishu`.`recycle_order_extend` trx id 195596 lock_mode X locks gap before rec
Record lock, heap no 83 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 00000002efac; asc       ;;
 2: len 7; hex 8100008cbb0ec4; asc        ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 98 page no 5231 n bits 152 index PRIMARY of table `tishu`.`recycle_order_extend` trx id 195596 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 83 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 00000002efac; asc       ;;
 2: len 7; hex 8100008cbb0ec4; asc        ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;


*** (2) TRANSACTION:
TRANSACTION 195597, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 6 lock struct(s), heap size 1128, 4 row lock(s), undo log entries 1
MySQL thread id 50, OS thread handle 6135132160, query id 402 localhost 127.0.0.1 root update
insert into recycle_order_extend (id, recycle_order_id, param_id,
    value_ids, es_type) values
      
      (
      376510847366725657, 376473627618443479, 100031,
      '[]', 1
      )
     , 
      (
      376510847369871385, 376473627618443479, 100030,
      '[]', 1
      )
     
    on duplicate key update value_ids=values(`value_ids`), update_time=values(`update_time`)

*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 98 page no 5231 n bits 152 index PRIMARY of table `tishu`.`recycle_order_extend` trx id 195597 lock_mode X locks gap before rec
Record lock, heap no 83 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 00000002efac; asc       ;;
 2: len 7; hex 8100008cbb0ec4; asc        ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 98 page no 5231 n bits 152 index PRIMARY of table `tishu`.`recycle_order_extend` trx id 195597 lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 83 PHYSICAL RECORD: n_fields 9; compact format; info bits 0
 0: len 8; hex a6a1360e0ef42300; asc   6   # ;;
 1: len 6; hex 00000002efac; asc       ;;
 2: len 7; hex 8100008cbb0ec4; asc        ;;
 3: len 8; hex 83dcebce32400001; asc     2@  ;;
 4: len 8; hex 80000000000186a8; asc         ;;
 5: len 5; hex 5b2232225d; asc ["2"];;
 6: len 5; hex 99ab64b48a; asc   d  ;;
 7: len 5; hex 99ab7b17aa; asc   {  ;;
 8: len 4; hex 80000001; asc     ;;

*** WE ROLL BACK TRANSACTION (1)
------------
TRANSACTIONS
------------

這時候就產生了一個疑問,有沒有可能我們傳過去是一個SQL語句,但是對于MySQL處理是一個記錄一個記錄處理的呢?

既然有疑問,那么我們就下載MySQL源碼,通過源碼來證明自己的猜想。

4.MySQL8.0源碼

4.1 準備工作

  • MySQL8.0版本的源碼,CLion開發工具
  • 配置工具鏈

圖片

-DHOMEBREW_HOME=/opt/homebrew/opt -DWITH_DEBUG=1 -DDOWNLOAD_BOOST=1 -DDOWNLOAD_BOOST_TIMEOUT=60000 -DWITH_BOOST=boost -DCMAKE_INSTALL_PREFIX=build_out -DMYSQL_DATADIR=build_out/data -DSYSCONFDIR=build_out/etc -DMYSQL_TCP_PORT=3310 -DMYSQL_UNIX_ADDR=mysql-debug.sock
  • CMake編譯項目
  • 初始化MySQL
--initialize-insecure
  • 啟動MySQL

修改程序實參

--defaults-file=/Users/chenkai/student/Mysql/mysql-8.0.33/cmake-build-debug/build_out/etc/my.cnf

4.2 源碼閱讀

通過參考資料和自己Debug代碼得到調用鏈路

精簡版本

調用鏈路
-sql_insert.cc#Sql_cmd_insert_values::execute_inner->遍歷values執行
  -handler.cc#ha_write_row->操作一行記錄
   -ha_innodb.cc#ha_innobase::write_row->在InnoDB database中存儲一行數據,針對這張表對應的handle
    -row0mysql.cc#row_insert_for_mysql->執行insert操作
     -row0ins.cc#x->向一個table中插入一行
      -row0ins.cc#row_ins_index_entry_step->向表中插入index,每個索引需要單獨插入,一個索引執行一次
       -row0ins.cc#row_ins_clust_index_entry->向表中插入聚集索引
       -row0ins.cc#row_ins_sec_index_multi_value_entry->向表中插入多個value的二級索引
       -row0ins.cc#row_ins_sec_index_entry->向表中插入二級索引
     -row0mysql.cc#row_mysql_handle_errors->處理存在重復key的情況
      -que0que.cc#que_run_threads_low->真正選擇線程去執行操作(牽扯到對剛插入的索引回滾,所以需要加間隙鎖,防止幻讀)

  -handler.cc#handler::ha_index_read_idx_map 檢索原有記錄主鍵并加鎖,防止被修改

  -handler.cc#ha_update_row->修改一行記錄         
   -row0upd.cc#row_upd->修改table中的一行
    -row0upd.cc#row_upd_clust_step->修改聚集索引
    -row0upd.cc#row_upd_sec_step->修改二級索引
     -row0upd.cc#row_upd_sec_index_entry->更新單個二級索引

詳細版本

調用鏈路
sql_parse.cc#mysql_execute_command->按照不同的SQL分類,進入不同的執行流程
 -sql_select.cc#Sql_cmd_dml::execute->執行sql
  -sql_insert.cc#Sql_cmd_insert_values::execute_inner->遍歷values執行
   -sql_insert.cc#write_record->執行sql中每一條記錄

    -handler.cc#ha_write_row->操作一行記錄
     -ha_innodb.cc#ha_innobase::write_row->在InnoDB database中存儲一行數據,針對這張表對應的handle
      -row0mysql.cc#row_insert_for_mysql->執行insert操作
       -row0mysql.cc#row_insert_for_mysql_using_ins_graph->使用graph結構存儲insert的信息
        -row0mysql.cc#row_mysql_convert_row_to_innobase->將一行數據從MySQL格式轉換為innodb格式
        -row0ins.cc#x->向一個table中插入一行
         -row0ins.cc#row_ins->向表中插入一行
          -row0ins.cc#row_ins_index_entry_step->向表中插入index,每個索引需要單獨插入,一個索引執行一次
           -row0ins.cc#row_ins_index_entry->向表中插入index
            -row0ins.cc#row_ins_clust_index_entry->向表中插入聚集索引
             -row0mysql.cc#row_ins_clust_index_entry_low->嘗試將entry插入到聚集索引中
              開始事務
              -btr0cur.cc#btr_cur_optimistic_insert->插入一個page到一個索引tree
               -btr0cur.cc#btr_cur_ins_lock_and_undo->檢查lock和寫undolog和加lock
                -lock0lock.cc#lock_rec_insert_check_and_lock->檢查lock和加lock
              結束事務  
            -row0ins.cc#row_ins_sec_index_multi_value_entry->向表中插入多個value的二級索引
            -row0ins.cc#row_ins_sec_index_entry->向表中插入二級索引
             -row0ins.cc#row_ins_sec_index_entry_low->嘗試將entry插入到二級索引中
              -row0ins.cc#row_ins_scan_sec_index_for_duplicate->掃描給定索引項處的唯一非聚集索引,以確定該項的鍵值是否發生唯一性沖突。對可能重復的記錄設置共享鎖
        -row0mysql.cc#row_mysql_handle_errors->處理存在重復key的情況
         -trx0roll.cc#trx_rollback_to_savepoint->回滾事務到某個保存點(插入時主鍵或者唯一索引沖突之前的保存點)
          -trx0roll.cc#trx_rollback_to_savepoint_low->執行真正的回滾操作
           -que0que.cc#que_run_threads->調度一個線程執行操作(在處理重復key的情況下,是調用undo線程執行回滾操作)
            -que0que.cc#que_run_threads_low->真正選擇線程去執行操作(牽扯到對剛插入的索引回滾,所以需要加間隙鎖,防止幻讀)
    
    -handler.cc#handler::ha_index_read_idx_map 檢索原有記錄主鍵并加鎖,防止被修改
              
    -handler.cc#ha_update_row->修改一行記錄         
     -ha_innodb.cc#ha_innobase::update_row->在InnoDB database中修改一行數據,針對這張表對應的handle
      -row0mysql.cc#row_update_for_mysql->執行update操作
       -row0mysql.cc#row_update_for_mysql_using_upd_graph->使用graph結構存儲update的信息
        -row0upd.cc#row_upd_step->修改table中的一行
         -row0upd.cc#row_upd->修改table中的一行
          -row0upd.cc#row_upd_clust_step->修改聚集索引
           #開始事務
           -row0upd.cc#row_upd_del_mark_clust_rec->標記刪除舊聚集索引
            -btr0cur.cc#btr_cur_del_mark_set_clust_rec->設置二級索引記錄的刪除標記
             -lock0lock.cc#ock_clust_rec_modify_check_and_lock->檢查lock和加lock
           -row0upd.cc#row_upd_clust_rec_by_insert->插入新的聚集索引,標記刪除舊聚集索引
            -btr0cur.cc#btr_cur_del_mark_set_clust_rec->設置二級索引記錄的刪除標記
             -lock0lock.cc#lock_clust_rec_modify_check_and_lock->檢查lock和加lock
            -row0ins.cc#row_ins_clust_index_entry->向表中插入聚集索引 
           #提交事務
          -row0upd.cc#row_upd_sec_step->修改二級索引
           -rrow0upd.cc#row_upd_del_multi_sec_index_entry->刪除多個二級索引
           -rrow0upd.cc#row_upd_multi_sec_index_entry->更新多個二級索引
           -row0upd.cc#row_upd_sec_index_entry->更新單個二級索引
            -row0upd.cc#row_upd_sec_index_entry_low->嘗試修改entry中的二級索引
             #開始事務 
             -btr0cur.cc#btr_cur_del_mark_set_sec_rec->設置二級索引記錄的刪除標記
              -lock0lock.cc#lock_sec_rec_modify_check_and_lock->檢查lock和加lock
             -row0ins.cc#row_ins_sec_index_entry->向表中插入二級索引
              -row0ins.cc#row_ins_sec_index_entry_low->嘗試將entry插入到二級索引中
               -row0ins.cc#row_ins_scan_sec_index_for_duplicate->掃描給定索引項處的唯一非聚集索引,以確定該項的鍵值是否發生唯一性沖突。對可能重復的記錄設置共享鎖 
             #提交事務

4.3 Debug代碼后得到執行SQL加鎖的過程

執行SQL語句

INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473601396703447, 100030, '[]', 1 ),
 ( 376510847369871385, 376473601396703447, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE  value_ids=values(`value_ids`), update_time = now();

加鎖過程

類型      模式              索引                  數據
TABLE  | IX            | NULL               | null
執行第一條value的插入主鍵索引前 row0ins.cc#row_ins_clust_index_entry

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
執行第一條value的插入二級索引后 row0ins.cc#row_ins_sec_index_entry

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
執行第一條value處理存在重復key的情況 row0mysql.cc#row_mysql_handle_errors

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
執行第一條value檢索原有記錄操作后 handler.cc#handler::ha_index_read_idx_map

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X,GAP         | PRIMARY            | 376510847369871385
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
執行第二條value的插入主鍵索引后 row0ins.cc#row_ins_clust_index_entry

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X             | uniq_recycle_param | 376473601396703447,100031,376501984042156217
RECORD | X,GAP         | PRIMARY            | 376510847369871385
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
執行第二條value的插入二級索引后 row0ins.cc#row_ins_sec_index_entry

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X             | uniq_recycle_param | 376473601396703447,100031,376501984042156217
RECORD | X,GAP         | PRIMARY            | 376510847369871385
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
執行第er條value處理存在重復key的情況 row0mysql.cc#row_mysql_handle_errors

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X             | uniq_recycle_param | 376473601396703447,100031,376501984042156217
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984042156217
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
執行第二條value檢索原有記錄操作后 handler.cc#handler::ha_index_read_idx_map

TABLE  | IX            | NULL               | null
RECORD | X             | uniq_recycle_param | 376473601396703447,100030,376501984043204793
RECORD | X             | uniq_recycle_param | 376473601396703447,100031,376501984042156217
RECORD | X,GAP         | PRIMARY            | 2783565478700000000
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984042156217
RECORD | X,REC_NOT_GAP | PRIMARY            | 376501984043204793
完成

4.4 結論

1.在MySQL執行過程中會遍歷每一個value執行。

2.執行過程中加鎖是分批加鎖的,比如handler.cc#handler::ha_index_read_idx_map會檢查原有記錄加行鎖防止修改,row0mysql.cc#row_mysql_handle_errors處理存在重復key的情況會加間隙鎖。

3.處理SQL過程中并不會加鎖,保證同時只能處理一條SQL。

4.內部有迷你事務,本質是對記錄所在的頁加一個 RW-X-LATCH 鎖保證共享資源(如頁、行或元數據)的訪問,以避免數據競爭和不一致,從而保證對同一記錄加鎖不是并發的。

5 問題驗證

通過上述結論以及SQL語句的調用鏈路,我們可以在執行row0mysql.cc#row_mysql_handle_errors睡眠1s,這樣我們可以模仿事務A和事務B都拿到間隙鎖進而產生死鎖的現象。

圖片圖片

5.1 復現A事務執行兩條,B事務執行一條產生死鎖的過程

過程:

步驟1:先在A事務中執行
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473601396703447, 100030, '[]', 1 )
ON DUPLICATE KEY UPDATE  value_ids=values(`value_ids`), update_time = now();
到斷點

步驟2:當到斷點后在事務B中執行
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473627618443479, 100030, '[]', 1 ),
 ( 376510847369871385, 376473627618443479, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE value_ids=values(`value_ids`), update_time = now();

步驟3:
然后再到事務A中執行
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847508283417, 376473608578400471, 100030, '[]', 1 ),
 ( 376510847509331993, 376473608578400471, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE value_ids=values(`value_ids`), update_time = now();

當執行完步驟3后就會發生死鎖,且死鎖原因也是因為事務A和B都獲取到了間隙鎖。

5.2 復現A事務執行一條,B事務執行一條產生死鎖的過程

過程:

步驟1:先在A事務中執行
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473601396703447, 100030, '[]', 1 ),
 ( 376510847369871385, 376473601396703447, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE  value_ids=values(`value_ids`), update_time = now();
到斷點

步驟2:當到斷點后在事務B中執行
INSERT INTO recycle_order_extend ( id, recycle_order_id, param_id, value_ids, es_type )
VALUES
 ( 376510847366725657, 376473627618443479, 100030, '[]', 1 ),
 ( 376510847369871385, 376473627618443479, 100031, '[]', 1 ) 
 ON DUPLICATE KEY UPDATE value_ids=values(`value_ids`), update_time = now();

當執行完步驟2后就會發生死鎖,且死鎖原因也是因為事務A和B都獲取到了間隙鎖。

6 參考

MAC 下編譯調試 MySQL8.0 源碼(https://blog.csdn.net/qq_40161813/article/details/127823025)

mysql 8.0 一條insert語句的具體執行流程分析(二)(https://blog.csdn.net/zgaoq/article/details/120371555)

關于作者

陳凱   俠客匯Java開發工程師

責任編輯:武曉燕 來源: 轉轉技術
相關推薦

2010-11-09 16:29:39

SQL Server死

2010-07-02 10:53:32

SQL Server死

2010-07-06 10:08:57

SQL Server

2010-07-07 13:58:25

SQL Server死

2010-11-09 17:04:20

SQL Server死

2011-04-02 17:08:44

SQL Server死鎖

2010-11-09 17:02:43

SQL Server死

2010-09-14 15:34:29

sql server死

2014-03-17 10:34:48

SQL Server

2023-08-15 08:26:34

SQL Server查找死鎖

2010-07-20 10:27:57

SQL Server

2010-11-09 16:37:25

Sql server死

2010-11-09 16:20:46

SQL Server死

2010-06-30 14:15:08

SQL Server死

2011-03-08 09:27:34

SQL Server數死鎖

2017-10-18 15:07:21

MySQL執行死鎖

2009-03-30 10:56:58

SQL Server數據庫死鎖數據庫

2011-02-28 13:19:50

SQL Server SQL死鎖

2022-05-11 07:41:55

死鎖運算線程

2010-08-26 10:45:33

死鎖SQL Server
點贊
收藏

51CTO技術棧公眾號

一道本无吗dⅴd在线播放一区 | 青青久在线视频| 亚洲人体偷拍| 亚洲欧美激情一区| av网站在线不卡| 国产在线高清视频| 99久久久免费精品国产一区二区| 国产99久久精品一区二区永久免费 | 精品视频一区二区三区| 亚洲五月六月丁香激情| 日韩高清dvd| 国产www视频| 美女国产精品| 欧美精品制服第一页| 51调教丨国产调教视频| 欧美综合影院| 午夜精品福利在线| 亚洲免费精品视频| 欧美一级淫片免费视频魅影视频| 视频一区欧美精品| 欧美成人免费小视频| 日本高清www| 成人在线啊v| 欧美日韩亚洲精品一区二区三区| 亚洲欧洲一区二区| 亚洲aaa在线观看| 激情综合网最新| 欧美在线视频a| 亚洲av无码一区二区三区在线| 欧美有码在线| 日韩免费观看高清完整版 | 国产精品成人在线视频| 成人性生交大片免费看96| 欧美制服丝袜第一页| 日本人体一区二区| 国产三区在线观看| 欧美—级在线免费片| 国产伦精品一区二区三区四区免费 | 亚洲综合伊人| 色综合天天做天天爱| 国产xxxx振车| 国产在线二区| 中文字幕日韩精品一区| 日本一区视频在线| 四虎影视2018在线播放alocalhost| 国产精品2024| 91亚洲一区精品| 一级黄色短视频| 日本美女一区二区三区视频| 日韩免费在线看| 91在线看视频| 亚洲美女网站| 久久久视频精品| 劲爆欧美第一页| 在线一区电影| 免费99精品国产自在在线| 999福利视频| 日韩黄色大片| 自拍视频国产精品| 制服丨自拍丨欧美丨动漫丨| 成人毛片免费看| 在线成人一区二区| 国产精品无码无卡无需播放器| 九九综合九九| 一区二区三区四区精品| 永久免费毛片在线观看| 日本黄色精品| xxxxxxxxx欧美| 国内毛片毛片毛片毛片毛片| 久久麻豆精品| 久久夜色精品亚洲噜噜国产mv| 久久久久亚洲av片无码| 欧美日韩国产一区二区三区不卡| 在线观看国产成人av片| 亚洲天堂精品一区| 一个色综合网| 欧美黄色小视频| 日韩成人免费在线观看| 亚洲制服少妇| 国产精品视频一区国模私拍| 在线观看亚洲一区二区| 国产老女人精品毛片久久| 国产精品久久国产精品| 深夜福利视频一区| 久久久久久毛片| 亚洲成人在线视频网站| 成人在线观看免费网站| 你懂的视频欧美| 亚洲欧美日韩精品| 影音先锋男人看片资源| 亚洲最大av| 1769国产精品| 波多野结衣小视频| 国产美女一区二区| 精品国产乱码久久久久久108| 青青久在线视频| 一区在线播放视频| 久久久久久免费看| 韩日一区二区| 欧美成人国产一区二区| 国产熟妇搡bbbb搡bbbb| 欧美激情理论| 69视频在线播放| 在线免费观看一区二区| 国产不卡一区视频| 日韩高清三级| 国产丝袜在线观看视频| 欧美午夜精品一区| 国产免费a级片| 精品视频97| 久久久久久噜噜噜久久久精品| 亚洲欧美综合另类| 国产一区二区精品在线观看| 欧美日韩成人一区二区三区 | av影院在线播放| 成人免费网站视频| 日韩欧美综合在线| 国产一区二区三区四区在线| 在线日韩电影| 91久久精品国产| 日韩欧美在线番号| 亚洲综合色网站| 日本肉体xxxx裸体xxx免费| 精品中国亚洲| 欧美激情视频网站| 97国产成人无码精品久久久| 久久综合中文字幕| 人人妻人人澡人人爽欧美一区双 | 国产精品中文字幕在线| 无码精品人妻一区二区| 亚洲激情综合网| 欧美第一页浮力影院| 一本色道久久综合狠狠躁的番外| 欧美成人剧情片在线观看| 在线观看国产成人| 久久精品一级爱片| 久久综合久久久久| 国产精品亚洲综合在线观看| 一本色道久久综合狠狠躁篇的优点| 亚洲国产综合久久| 国产aⅴ精品一区二区三区色成熟| 日韩片电影在线免费观看| 日韩欧美一中文字暮专区 | 头脑特工队2免费完整版在线观看| 日韩一区日韩二区| 99re精彩视频| 成人激情视频| 国产精品视频区1| 成人在线播放视频| 欧美专区日韩专区| av网在线播放| 视频在线观看一区二区三区| 美女主播视频一区| 手机在线观看av网站| 亚洲成人黄色在线| 91久久国产视频| av电影天堂一区二区在线观看| www.国产在线播放| 草莓视频一区二区三区| 久久久久在线观看| 台湾av在线二三区观看| 一本色道久久加勒比精品| 免费在线观看你懂的| 久热国产精品| 亚洲 国产 日韩 综合一区| 香蕉成人av| 中文字幕精品久久| 国产伦精品一区二区三区四区 | 欧美日韩亚洲免费| 三级成人在线| 色老头一区二区三区| 91黄色在线视频| 亚洲免费在线视频| 88av在线播放| 久久亚洲二区| 亚洲精品视频一二三| 91精品国产自产观看在线 | 久久久精品动漫| 亚洲天堂导航| 中文字幕自拍vr一区二区三区| 国产精品一级视频| 一区二区三区丝袜| 800av在线播放| 久久最新视频| 黄色www在线观看| 在线日韩成人| 91wwwcom在线观看| 婷婷成人激情| 欧美变态口味重另类| 国产午夜免费福利| 中文字幕的久久| 国产裸体视频网站| 国产精品一二| 在线不卡日本| 久久人人爽人人爽人人片av不| 日韩av毛片网| www.欧美日本韩国| 亚洲精品日韩丝袜精品| 国产美女精品视频国产| 午夜精品福利在线| 日韩精品123区| 不卡一二三区首页| 中文字幕亚洲欧洲| 国产精品试看| 黄色污污在线观看| 国产99久久精品一区二区300| 成人亲热视频网站| 丁香六月综合| 色与欲影视天天看综合网| 国产黄在线观看| 亚洲精品一区二区三区在线观看 | 午夜亚洲福利在线老司机| 一级黄色免费在线观看| 一区二区三区日本久久久| 91在线无精精品一区二区| 亚洲欧美电影| 欧美另类极品videosbestfree| 激情视频在线观看免费| 欧美va亚洲va| 国产精品系列视频| 欧美丝袜自拍制服另类| 日韩av无码中文字幕| 亚洲精品一二三| 国产三级黄色片| 26uuu亚洲| 欧美午夜精品一区二区| 看电视剧不卡顿的网站| 92看片淫黄大片一级| 国产综合激情| 亚洲免费视频播放| 成人综合久久| 日本午夜精品电影| 亚洲福利网站| 国产日韩三区| 动漫3d精品一区二区三区乱码| 亚洲a成v人在线观看| 国产欧美自拍| 国产精品福利网| 在线人成日本视频| 国产91精品久久久久久| 91九色美女在线视频| 欧美日本啪啪无遮挡网站| 国产在线高潮| 久久天堂电影网| 嫩草在线视频| 中文字幕亚洲天堂| yw视频在线观看| 精品一区二区三区四区五区| 日本美女一区二区| 亚洲成av人片一区二区梦乃| 中文国产在线观看| 美女视频黄 久久| 97视频在线免费播放| 亚洲精品国产日韩| 免费网站在线观看视频 | 欧美变态tickling挠脚心| 国产精品国产一区二区三区四区| 欧美中文字幕一区二区三区亚洲| 麻豆成人免费视频| 色就色 综合激情| 午夜精品三级久久久有码| 五月天国产精品| 羞羞影院体验区| 欧美日韩免费在线观看| 美日韩一二三区 | 91丨九色丨海角社区| 在线中文字幕不卡| 中文字幕一二三四| 欧美精品久久一区| 国产成人毛毛毛片| 精品美女被调教视频大全网站| 国产91免费看| 精品亚洲一区二区三区在线观看 | 久久丫精品久久丫| 亚洲va欧美va天堂v国产综合| 国产成人在线免费视频| 精品久久久中文| 无码人妻丰满熟妇奶水区码| 在线影院国内精品| 国产欧美日韩综合精品一区二区三区| 欧美一区二区三区在线看| 丰满熟女一区二区三区| 亚洲精品在线观看www| a√在线中文网新版址在线| 久久天天躁狠狠躁老女人| www.综合| 国产精品久在线观看| 精品视频在线一区| 国产日韩欧美精品| 日本女优一区| www.av91| 日本欧美在线观看| 亚洲精品成人无码毛片| 久久综合一区二区| 精品自拍偷拍视频| 日韩欧美黄色动漫| 国产视频一区二区三| 亚洲第一精品久久忘忧草社区| 国产在线中文字幕| 欧美另类高清videos| 最新中文字幕在线播放| 国产日韩欧美夫妻视频在线观看| 视频一区日韩精品| 日本精品国语自产拍在线观看| 婷婷综合在线| 欧美 日韩精品| 国产一区在线不卡| a毛片毛片av永久免费| 亚洲人123区| 99re这里只有精品在线| 欧美成人一区二区三区| 黄色在线播放| 久久久久久美女| 亚洲91在线| 欧美极品日韩| 欧美精品一级| 亚洲精品自拍网| 久久人人爽爽爽人久久久| 私库av在线播放| 欧美影视一区在线| 天堂中文网在线| 欧美插天视频在线播放| 欧美123区| 久热国产精品视频一区二区三区| 欧美视频二区| 亚洲男人天堂av在线| 国产女同互慰高潮91漫画| 国产性xxxx高清| 日韩欧美国产综合一区| 日本中文字幕伦在线观看| 国产精品99一区| 夜夜春成人影院| 大j8黑人w巨大888a片| 成人在线视频一区二区| www.xxxx日本| 欧美人伦禁忌dvd放荡欲情| 国产精品99999| 国产精品69久久| 天天久久夜夜| 波多野结衣乳巨码无在线| 成人性生交大片免费看中文网站| 污污的视频在线免费观看| 欧美性一二三区| 国产一级免费在线观看| 人人澡人人澡人人看欧美| 色狠狠久久av综合| 免费高清在线观看免费| 97精品久久久久中文字幕| 国产精品6666| 精品久久久久久无| 不卡的av影片| 国产原创精品| 一本一本久久| 亚洲专区区免费| 欧美性xxxx在线播放| 欧美精品久久久久久久久久丰满| 51午夜精品视频| 亚洲日产av中文字幕| 1024精品视频| 久久久久久久久久美女| 精品久久久久久久久久久国产字幕| 精品视频在线播放免| 97成人资源| 日韩尤物视频| 美女视频一区二区三区| 日本精品在线免费观看| 欧美一级日韩一级| 国产在线xxx| 久久99精品久久久久久久久久| 亚洲综合精品| 国产探花视频在线播放| 欧美人狂配大交3d怪物一区 | 国产精品国产三级国产aⅴ无密码| 在线观看免费视频a| 精品中文字幕在线2019| 国产另类在线| 免费大片在线观看| 国产精品女上位| 国产激情无套内精对白视频| 久久乐国产精品| 久久99国内| 日韩av片专区| 亚洲国产精品久久艾草纯爱| 欧美日韩国产综合视频| 国产日产亚洲精品| 一区在线视频| 日本性高潮视频| 日韩亚洲欧美高清| 大胆人体一区| 91九色国产ts另类人妖| 99热精品国产| 中文字幕制服诱惑| 欧美劲爆第一页| 欧美伦理影院| 扒开伸进免费视频| 欧美性色黄大片手机版| 日本在线视频www鲁啊鲁| 日本成人三级| 国产一区二区三区高清播放| 美日韩一二三区|