1. 程式人生 > >【轉】INSERT...ONDUPLICATEKEYUPDATE產生deathlock死鎖原理講解及解決辦法

【轉】INSERT...ONDUPLICATEKEYUPDATE產生deathlock死鎖原理講解及解決辦法

地址:https://www.2cto.com/database/201711/695662.html

前言

  我們在實際業務場景中,經常會有一個這樣的需求,插入某條記錄,如果已經存在了則更新它如果更新日期或者某些列上的累加操作等,我們肯定會想到使用INSERT … ON DUPLICATE KEY UPDATE語句,一條語句就搞定了查詢是否存在和插入或者更新這幾個步驟,但是使用這條語句在msyql的innodb5.0以上版本有很多的陷阱,即有可能導致death lock死鎖也有可能導致主從模式下的replication產生資料不一致。

正文

  正如前言說的那樣,在實際業務中,曾經有過一個需求就是插入一條業務資料,如果不存在則新增,存在則累加更新某一個欄位的值,於是乎就想到了使用insert… on duplicate key update這個語句,但是有一天去測試環境檢視錯誤日誌時,卻發現了在多個事務併發執行同一條insert…on duplicate key update 語句時,也就是insert的內容相同時,發生 了死鎖


  對於insert…on duplicate key update這個語句會引發dealth lock問題,官方文件也沒有相關描述,只是進行如下描述:

An INSERT … ON DUPLICATE KEY UPDATE statement against a table having more than one unique or primary key is also marked as unsafe. (Bug #11765650, Bug #58637)

也就是如果一個表定義有多個唯一鍵或者主鍵時,是不安全的,這又引發了以一個問題,見https://bugs.mysql.com/bug.php?id=58637

**也就是當mysql執行INSERT ON DUPLICATE KEY的 INSERT時,儲存引擎會檢查插入的行是否會產生重複鍵錯誤。如果是的話,它會將現有的行返回給mysql,mysql會更新它並將其傳送回儲存引擎。**當表具有多個唯一或主鍵時,此語句對儲存引擎檢查金鑰的順序非常敏感。根據這個順序,儲存引擎可以確定不同的行資料給到mysql,因此mysql可以更新不同的行。儲存引擎檢查key的順序不是確定性的。例如,InnoDB按照索引新增到表的順序檢查鍵。首先檢查第一個新增的索引。所以,如果主站和從站按不同的順序新增索引,那麼如果主從複製是基於語句的複製,那麼可能最後同一個語句在master上執行和slaver上執行的結果不一致。

回到死鎖的問題

insert … on duplicate key 在執行時,innodb引擎會先判斷插入的行是否產生重複key錯誤,如果存在,在對該現有的行加上S(共享鎖)鎖,如果返回該行資料給mysql,然後mysql執行完duplicate後的update操作,然後對該記錄加上X(排他鎖),最後進行update寫入。
如果有兩個事務併發的執行同樣的語句,那麼就會產生death lock,如:
在這裡插入圖片描述

解決辦法:

1、儘量對存在多個唯一鍵的table不使用該語句
2、在有可能有併發事務執行的insert 的內容一樣情況下不使用該語句