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

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

3天內不再提示

MySQL并發Replace into導致死鎖場景簡析

數據庫和存儲 ? 來源:MySQL內核剖析 ? 2023-06-13 10:56 ? 次閱讀

在之前的文章 #issue 68021 MySQL unique check 問題中, 我們已經介紹了在 MySQL 里面, 由于唯一鍵的檢查(unique check), 導致 MySQL 在 Read Commit 隔離級別也需要添加 GAP lock, 導致有些比較奇怪情況下有一些鎖等待.

另外一類問題是由于唯一鍵檢查導致的死鎖問題, 這類問題也非常多, 也是我們處理線上經常收到用戶反饋的問題, 這里我們就分析幾個這樣死鎖的 Case.

Replace into 操作是非常常用的操作, 很多時候在插入數據的時候, 不確定表中是否已經存在數據, 有沒有唯一性的沖突, 所以會使用 replace into 或者 insert .. on duplicate update 這樣的操作, 如果沖突就把對應的行給自動更新.

但是這樣的操作在并發場景, 當存在唯一鍵的時候容易有死鎖問題場景, 那么為什么會這樣, 我們來看一個簡單的 case:

通過GDB 和腳本可以復現以下死鎖場景.

create table t(a int AUTO_INCREMENT, b int, PRIMARY KEY (a), UNIQUE KEY (b));

insert into t(a, b) values (100, 8);

session1:
replace into t(a, b) values (10, 8);

session2:
replace into t(a, b) values (11, 8);
(40001): Deadlock found when trying to get lock; try restarting transaction

當然也可以通過這個腳本, 不需要 GDB 就可以隨機復現:

#! /bin/bash

MYSQL="mysql -h127.0.0.1 -P2255 -uroot test"

$MYSQL -e "create table t(a int AUTO_INCREMENT, b int, PRIMARY KEY (a), UNIQUE KEY (b))"

while true
do

  $MYSQL -e "replace into t(b) values (8)" &
  $MYSQL -e "replace into t(b) values (8)" &
  $MYSQL -e "replace into t(b) values (8)" &

  wait;
done

這里在并發session1 和 session2 插入的時候, 就容易出現 Deadlock Lock 的問題, 類似用戶并發插入數據的場景.

dc8932ee-098e-11ee-962d-dac502259ad0.png

上面的死鎖信息 Trx HOLDS THE LOCK 和 WAITING FOR THIS LOCK TO BE GRANTED 是一個錯誤的誤導信息, 官方版本在新的版本中已經修復, 這里 HOLDS THE LOCK 是不對的, 其實還未持有 X lock.

這里看到 Trx 1 waiting 在 8, 100 next-key X lock 上.

然后 Trx2 持有 8, 100 next-key X lock, 但是 WAITING FOR 8, 100 insert_intention lock.

那么為什么會有死鎖呢?

我們先看一下單個 replace into 的流程

整體而言, 如果replace into 第1遍insert 操作的時候, 遇到unique index 沖牧, 那么需要重新執行update 操作或者delete + 重新insert 操作, 但是第1遍insert 操作失敗添加的事務鎖并不會釋放, 而是等到整個事務提交才會釋放, 原因當然是現在MySQL 2Phase Lock 機制要做的保證

replace into 大概代碼如下:

所有replace into/on duplicate key update 這里execute_inner 執行的是Sql_cmd_insert_values => execute_inner() 方法

這里replace into/on duplicate key update 執行在這個循環里面  if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE) {
    DBUG_ASSERT(duplicate_handling != DUP_UPDATE || update != NULL);    while ((error = table->file->ha_write_row(table->record[0]))) {        // ...
      if (duplicate_handling == DUP_UPDATE) {

    這里 branch 就是處理 on duplicate key update 的duplicate key 場景
    判斷如果是 on duplicate key update 邏輯, 那么遇到error 以后, 就是用 table->file->ha_update_row 通過 update 進行更新
          } else /* DUP_REPLACE */ {
    duplicate_handling == DUP_REPLACE 就是處理 replace into 錯誤場景
    在replace into場景中, 如果插入的key 遇到沖突的, 是如何處理的, 其實是分2種場景的:
    如果是 replace into 邏輯, 遇到 error 以后, 如果是沖突的是最后一個 unique index, 并且沒有外鍵約束, 并且沒有delete trigger 的時候, 那么和 on duplicate key update 一樣, 使用 ha_update_row 通過 update 進行更新

    否則通過 delete + 重新 insert 來進行更新, 操作更多, 消耗也就更多.

    具體代碼:
    如果ha_write_row() 失敗, 那么會執行delete_row() 操作, 等這個操作執行完成以后, 又跳到這個while 循環進行重新insert    if ((error = table->file->ha_delete_row(table->record[1]))) goto err;    /* Let us attempt do write_row() once more */
    }

接下來是2個replace into 操作的時候, 如果Thread 1 停在replace into 第一個階段, 也就是insert 遇到unique index 沖突, 此時持有8, 100 next-key lock.

這個時候第2個Thread 2也進行replace into 操作, 在進行唯一鍵沖突檢測, 執行row_ins_scan_sec_index_for_duplicate() 的時候需要申請8, 100 next-key lock. 該lock 被thread 1持有, 那么只能進行等待.

接下來Thread 1 繼續執行, 執行update 操作, 在InnoDB 里面, 對于二級索引而言需要執行delete, 然后再insert 操作, 在insert 的時候需要持有8, 100 insert intention lock. 目前 InnoDB insert intention lock 判斷是否沖突的時候, 對應的 record 不論是有事務等待或者已經持有 next-key lock, 都算沖突. 此時Thread 已經等在8, 100 next-key lock 上, 那么 Thread 1 就無法獲得 insert intention lock, 只能進行等待.

這里有一個問題: 為什么申請insert_intention 的時候, 如果有其他事務提前等待在這個 lock 的 next-key lock 上面, 那么這個 insert_intention 會申請失敗?

dcb2378e-098e-11ee-962d-dac502259ad0.png

在函數rec_lock_check_conflict() 解釋了這個問題, 因為如果申請 intention lock 成功, 那么接下來的 insert 操作也就會成功, 那么原來等待這個 record 上面的trx 就變成需要等待 2 個 record 了.

比如如果之前 trx2 wait 在(4, 10] 這個 next-key lock 上, 如果允許 trx1 插入了 7,這個 record, 那么根據鎖繼承機制, 7 會繼承 10 這個 record 上面的 next-key lock, 那么 trx2 就變成 wait 在兩個 record 上, 也就變成 2 個 waiting lock 了, 那么現有這套鎖等待喚醒機制就也要改了, 現在這套鎖等待喚醒機制因此一個 trx 只會等待一個 lock, 在一個 lock 釋放以后, 相應等待在這個 Lock 上面的 trx 就可以喚醒了.

因此為了規避這樣的問題, MySQL InnoDB 里面如果申請 insert_intention lock 的時候, 如果有其他事務提前等待在這個 lock 的 next-key lock 上, 那么 insert_intention lock 是無法申請成功的.

那么現在的就過就是 Thread 2 等待 Thread 1 next-key lock 釋放, Thread 1 等待 Thread 2 next-key lock 獲得并釋放, 出現了 Thread1 <=> Thread2 互相等待的情況 因此出現的死鎖.




審核編輯:劉清

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

    關注

    0

    文章

    95

    瀏覽量

    9286
  • GAP
    GAP
    +關注

    關注

    0

    文章

    15

    瀏覽量

    8261
  • Thread
    +關注

    關注

    2

    文章

    83

    瀏覽量

    25731
  • gdb調試器
    +關注

    關注

    0

    文章

    10

    瀏覽量

    1086

原文標題:MySQL 常見死鎖場景 -- 并發Replace into導致死鎖

文章出處:【微信號:inf_storage,微信公眾號:數據庫和存儲】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

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

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

    HAL UART發送接收死鎖

    huart->State 盡然不是上面的2個中的一個,然后返回HAL_BUSY狀態,導致死鎖 請問哪位遇到過這種情況嗎?請指點下,系統使用了FREERTOS. 現象: 串口發送(沒用中斷發送),---->有接收,又觸發發送,導致死鎖
    發表于 01-02 10:05

    MySQL存儲引擎

    MySQL存儲引擎InnoDB??InnoDB 的存儲文件有兩個,后綴名分別是.frm和.idb,其中.frm是表的定義文件,而.idb是數據文件。InnoDB 中存在表鎖和行鎖,不過行鎖是在命中
    發表于 09-06 06:07

    搞錯熔絲位,導致芯片死鎖的恢復辦法

    搞錯熔絲位,導致芯片死鎖的恢復辦法 當你改動了AVR的熔絲位配置,重新加電后,想再用ISP下載,提示:“進入編程模式失敗”等,極有可能是你搞錯了熔絲位,導致
    發表于 01-20 17:01 ?2603次閱讀
    搞錯熔絲位,<b class='flag-5'>導致</b>芯片<b class='flag-5'>死鎖</b>的恢復辦法

    使用外部 SRAM 導致死

    STM32單片機使用外部 SRAM 導致死
    發表于 11-25 14:49 ?0次下載

    SPI接口發片選信號導致死

    STM32F103 SPI接口發片選信號導致死
    發表于 12-08 11:53 ?0次下載

    APM32F103RCT6_UART_串口多層并發導致死

    APM32F103RCT6_UART_串口多層并發導致死
    發表于 11-09 21:03 ?4次下載
    APM32F103RCT6_UART_串口多層<b class='flag-5'>并發</b><b class='flag-5'>導致死</b>機

    MySQL并發update導致鎖等待介紹

    生產環境中經常會遇到鎖等待與死鎖相關的問題,這類問題通常比較緊急,而且由于鎖相關影響因素較多,因此分析難度較大。
    的頭像 發表于 05-19 10:54 ?2733次閱讀
    <b class='flag-5'>MySQL</b><b class='flag-5'>并發</b>update<b class='flag-5'>導致</b>鎖等待介紹

    SQLite和MySQL區別

    MySQL時,用戶要考慮很多方面的區別。 1.適用范圍 SQLite比較適合在少量并發用戶、輕負載的場景下使用,一般用于嵌入式系統、移動端等小規模應用;而MySQL則適合大型的、高
    的頭像 發表于 08-28 17:09 ?3358次閱讀

    通過GDB non-stop mode調試MySQL

    通過GDB non-stop mode 調試MySQL, 特別是用于復現死鎖場景, 需要按照一定的并發順序寫入才可以構造出來, 通過GDB non-stop mode 可以非常方便進行
    的頭像 發表于 09-25 10:34 ?338次閱讀
    通過GDB non-stop mode調試<b class='flag-5'>MySQL</b>

    Linux內核死鎖lockdep功能

    死鎖是指兩個或多個進程因爭奪資源而造成的互相等待的現象,如進程A需要資源X,進程B需要資源Y,而雙方都掌握對方所需要的資源,且都不釋放,這會導致死鎖。 在內核開發中,時常要考慮并發設計,即使采用正確
    的頭像 發表于 09-27 15:13 ?426次閱讀
    Linux內核<b class='flag-5'>死鎖</b>lockdep功能

    死鎖的現象及原理

    原理 1.1 復現最簡單的死鎖 線程A占有鎖1,線程B占有鎖2;此時線程A想要獲取鎖2,但是鎖2已經被線程B占有, 此時線程A會休眠等待線程B釋放鎖2后,再去獲得鎖2??梢钥吹较旅娴?b class='flag-5'>場景,線程B想要獲取鎖1,結果線程B也休眠去了。這就
    的頭像 發表于 11-10 16:32 ?263次閱讀
    <b class='flag-5'>死鎖</b>的現象及原理

    mysqlreplace的用法

    MySQL中,REPLACE是用于替換字符串或者更新特定記錄的關鍵字。它可以用于單個表或者多個表,允許你在已有的數據中查找指定的字符串并替換為新的字符串。REPLACE非常強大,可以根據你的需求
    的頭像 發表于 11-30 10:35 ?745次閱讀

    MySQL替換字符串函數REPLACE

    MySQL是目前非常流行的開源數據庫管理系統之一,它具有強大的功能和性能。其中之一的字符串函數REPLACE,可以用于替換字符串中的指定字符或字符串。在本文中,我們將詳細討論MySQL替換字符串函數
    的頭像 發表于 11-30 10:44 ?432次閱讀

    淺談MySQL常見死鎖場景

    這里問題的原因是這個 table 里面只有record 2, 所以這里認真看, 死鎖的時候是等待在 supremum 上的, 因為supremum 的特殊性, supremum 沒有gap lock, 只有 next-key lock
    的頭像 發表于 03-21 14:10 ?272次閱讀
    淺談<b class='flag-5'>MySQL</b>常見<b class='flag-5'>死鎖</b><b class='flag-5'>場景</b>
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>