1. 程式人生 > >Mysql中update後insert造成死鎖的分析

Mysql中update後insert造成死鎖的分析

問題描述

sql如下:

START TRANSACTION;
UPDATE table_a SET ... WHERE id = x ;
IF(ROW_COUNT() = 0) THEN
    INSERT INTO table_a id VALUES x;
END IF; 
COMMIT;

其中id為主鍵。
平均一天有不到10次的死鎖。

排查過程

首先檢視程式日誌,發現死鎖都只有新使用者首次登入時才出現。也就是說,update時發現數據庫中並沒有相應的行,所以會進行接下來的插入操作,這時發生了死鎖。

然後,查詢了innodb的日誌,這裡貼出關鍵的部分。

------------------------
LATEST DETECTED DEADLOCK
------------------------
2018-02-02 23:35:03 7fe7f03ff700
*** (1) TRANSACTION:
TRANSACTION 72155984, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 1279838, OS thread handle 0x7fe803bff700, query id 988205122 172.16.0.123 acspassport update
INSERT INTO table_a id VALUES x
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3763 n bits 184 index `PRIMARY` of table `accountdb`.`last_login_openid` trx id 72155984 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) TRANSACTION:
TRANSACTION 72155983, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1184, 2 row lock(s)
MySQL thread id 1248122, OS thread handle 0x7fe7f03ff700, query id 988205121 172.16.0.123 acspassport update
INSERT INTO table_a id VALUES x
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 85 page no 3763 n bits 184 index `PRIMARY` of table `accountdb`.`last_login_openid` trx id 72155983 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 85 page no 3763 n bits 184 index `PRIMARY` of table `accountdb`.`last_login_openid` trx id 72155983 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (2)

從兩個transaction的WAITING FOR THIS LOCK TO BE GRANTED

可以看出兩個transaction都在insert申請insert intent X lock 時等待,從而導致死鎖。

總結出精簡後的導致死鎖的過程如下,可以輕鬆的使用控制檯復現。

transaction A transaction B
begin begin
update row a失敗
update row b失敗
insert row a等待
insert row b等待
死鎖發生

成因

innodb不是行鎖嗎,為什麼會發生死鎖呢?

這裡就涉及到innodb的鎖機制了,innodb使用了Repeatable Read(RR)的隔離級別。在此級別下,innodb為了防止幻讀(Phantom Rows),在實現上使用了gap lock。並且在search和scan的時候使用next-key lock(record lock + gap lock),但是對於在主鍵或唯一索引上進行查詢的時候僅使用record lock。這裡有一個例外,即當使用主鍵找不到的時候該記錄的時候,則在該區間加gap lock。這次死鎖的就是由這個例外情況引起的。

下面根據以上原理分析本次死鎖的成因。

transaction A 鎖的情況 transaction B 鎖的情況
begin begin
update row a(a>x)失敗 區間(x,正無窮)gap lock
update row b(b>x)失敗 區間(x,正無窮)gap lock(gap lock之間不互斥)
insert row a等待 insert intention lock等待(gap lock only stop other transactions from inserting to the gap
insert row b等待 insert intention lock 等待
死鎖發生

根據以上原理,包括

select ... where id=x lock in share mode/for update;
if(found_rows()=0)
    insert into id values 1;

也可能導致死鎖。

解決方法

根據select結果判斷,這裡有極小的概率出現insert duplicate,因為每次多一次select,效率肯定不如原來的。如果還有問題可以試試使用insert ignore或者insert on duplicate key update。

SELECT 1 FROM table_a WHERE id=x;
IF(FOUND_ROWS() = 0) THEN
    INSERT INTO table_a id VALUES x;
ELSE
    UPDATE table_a SET ... WHERE id=x;
END IF;

相關推薦

Mysqlupdateinsert造成分析

問題描述 sql如下: START TRANSACTION; UPDATE table_a SET ... WHERE id = x ; IF(ROW_COUNT() = 0) THEN INSERT INTO table_a id VALUES x; END I

mysqlupdate的low_priority解決並發問題

replace 信息 顯式 沒有 let 記錄 狀態 ons 阻塞 在處理訪客信息更新是遇到了大並發的問題,low_priority,低優先級,可以讓並發沒那麽占CPU,對於低配VPS來說,作用還是很大的。UPDATE [LOW_PRIORITY] tbl_name SET

select for update引發分析

而在 back ons 關系 級別 分析 得到 ica 分享 本文針對MySQL中在Repeatable Read的隔離級別下使用select for update可能引發的死鎖問題進行分析。 1. 案例 業務中需要對各種類型的實體進行編號,例如對於x類實體的編號可能是x2

MySQL的事務及讀寫實現並發訪問控制

hang dea 執行c 定時 ack 幫助 持久 表操作 查看 一、並發控制中鎖的概念   鎖是並發控制中最核心的概念之一,在MySQL中的鎖分兩大類,一種是讀鎖,一種是寫鎖,讀鎖也可以稱為共享鎖(shared lock),寫鎖也通常稱為排它鎖(exclusive loc

【轉】【MySQLMysql併發時經典常見的原因及解決方法

https://www.cnblogs.com/zejin2008/p/5262751.html   1.    mysql都有什麼鎖   MySQL有三種鎖的級別:頁級、表級、行級。 表級鎖:開銷小,加鎖快;不會出現死

MySQL分析一例

Tomcat日誌報死鎖錯誤,show innodb status獲取死鎖資訊: ------------------------ LATEST DETECTED DEADLOCK ------------------------ 181107 9:30:46 *** (1) TRANSACTION:

一次線上mysql分析

一、現象 發運車次呼叫發車介面時發生異常,後臺丟擲資料庫死鎖日誌。   二、原因分析   通過日誌可以看出事務T1等待 heap no 8的行鎖 (X locks 排他鎖)                 事務T2持有heap no 8的行鎖,等待heap no 7的行鎖 兩個更新運

Linux gcc for 迴圈 i=i++ 會造成迴圈問題及 ++i / i++ 彙編分析

在把 Windows 程式移植到 Linux 時遇到了死迴圈,最後定位到了類似這種的語句 for (i = 0; i < 1; i = i++), 別問我是誰寫的,為什麼這麼寫(淚目!)。 根據我自己的感覺, i = i++ 應該等價於 i++(C標準中 i=i++ 的行為未

mybatisupdate預設的返回值型別

後端的資料持久化使用的是 Mybatis ,在做高併發下賬戶增減餘額的時候,打算使用樂觀鎖來解決這個問題。在獲取update操作的返回值時遇到了一個問題,似乎 Mybatis 進行 update 操作得到的 int 返回值並不是影響的行數。這下就尷尬了。 一般而言,我們知道當我們使用 Mybat

Mysql併發時經典常見的原因及解決方法

1.    mysql都有什麼鎖 MySQL有三種鎖的級別:頁級、表級、行級。 表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。 行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。 頁面鎖:開

你的眼睛背叛你的心:解決 .NET Core GetHostAddressesAsync 引起的 EnyimMemcached 問題

在我們將站點從 ASP.NET + Windows 遷移至 ASP.NET Core + Linux 的過程中,目前遇到的最大障礙就是 —— 沒有可用的支援 .NET Core 的 memcached 客戶端。 我們一直用的是 EnyimMemcached ,在沒有其它選擇的情況下,我們自己嘗試著將 Eny

mysql一條insert語句批量插入多條記錄

插入語句常用寫法: INSERT INTO items(name,city,price,number,picture)  VALUES('耐克運動鞋','廣州',500,1000,'003.jpg')

spinlock造成的原因

舉個例子:程序A中呼叫了spin_lock(&lock)然後進入臨界區,此時來了一箇中斷(interrupt), 該中斷也執行在和程序A相同的CPU上,並且在該中斷處理程式中恰巧也會spin_lock(&lock) 試圖獲取同一個鎖。由於是在

mysqlupdate和select結合使用

在遇到需要update設定的引數來自從其他表select出的結果時,需要把update和select結合使用,不同資料庫支援的形式不一樣,在mysql中如下:update A inner join(se

控制檯進入mysql,修改密碼造成輸入新或舊密碼都無法登陸

原因是修改密碼的語句輸入有誤錯誤1:-p與原密碼之間有空格mysqladmin -u root -p 123 passwordEnter password:***mysqladmin:Unknown command:'123'由於語句輸入有誤,因此回車輸入密碼後報錯:Unkn

儲存過程使用dblink,碰到dblink解決方案

CREATE OR REPLACE PROCEDURE synchronous_pm_t_material AS PRAGMA AUTONOMOUS_TRANSACTION; ---開啟自動事務機制,此處的分號別忘了     last_version VARCHAR2(14)

java 多執行緒之淺析

出現死鎖的前提條件: 1.必須是至少2個以上的執行緒在執行; 2.同時要保證其中至少有兩個執行緒中的鎖(至少有兩個)是相同的,而且都有鎖的巢狀; 分析:首先要明確的是當兩個執行緒都擁有相同的鎖時候,誰先拿到鎖,誰就有執行權(比如執行緒①先拿到執行權),同時②執行緒就沒有執

MySQL分析及解決的方法--例子

http://soft.chinabyte.com/database/385/12532885.shtml 5、死鎖舉例分析   在MySQL中,行級鎖並不是直接鎖記錄,而是鎖索引。索引分為主鍵索引和非主鍵索引兩種,如果一條sql語句操作了主鍵索引,MySQL就會鎖

題外話+Mysqllimit不能跟變數問題的解決方法

先說兩句體外話:好久沒在CSDN的blog寫東西了,一個原因是自己最近工作比較忙,最主要的原因還是CSDN的blog反映太遲鈍,偶爾有想法想記錄下來,開啟部落格主頁都打不開,要麼進度條在下面緩慢的載入,要麼乾脆就報 已取消到該網頁的導航

Boost.mutex連續兩次加造成

問題程式碼如下:#include<iostream> #include<iomanip> using namespace std; #include <boo