1. 程式人生 > >mysql insert鎖機制(insert死鎖)

mysql insert鎖機制(insert死鎖)

一、前言

上週遇到一個因insert而引發的死鎖問題,其成因比較令人費解。
於是想要了解一下insert加鎖機制,但是發現網上介紹的文章比較少且零散,挖掘過程比較忙亂。

本以為只需要系統學習一個較完全的邏輯,但是實際牽扯很多innodb鎖相關知識及加鎖方式。我好像並沒有那麼大的能耐,把各種場景的加鎖過程一一列舉並加之分析;亦沒有太多的精力驗證網上的言論的準確性。

只好根據現在瞭解的內容,參考官方文件,說說自己當前的理解。
本文僅供參考,如有誤導,概不負責。

二、現場狀態

不同的mysql版本,不同的引數設定,都可能對加鎖過程有影響。
分析加鎖機制還是應當儘可能多地列舉一下關鍵引數,例如:當前mysql版本、事務隔離級別等。
如下,僅僅只列出個別比較重要的引數。

1.資料庫版本

123456mysql> select version();+-----------+| version() |+-----------+| 5.6.27 |+-----------+

2. 資料庫引擎

12345678mysql> show variables like '%engine%';+----------------------------+--------+| Variable_name | Value |+----------------------------+--------+| default_storage_engine | InnoDB || default_tmp_storage_engine | InnoDB || storage_engine | InnoDB |+----------------------------+--------+

:InnoDB支援事務,Myisam不支援事務;InnoDB支援行鎖和表鎖;Myisam不支援行鎖。

3. 事務隔離級別

123456mysql> select @@global.tx_isolation, @@session.tx_isolation, @@tx_isolation;+-----------------------+------------------------+-----------------+| @@global.tx_isolation | @@session.tx_isolation | @@tx_isolation |+-----------------------+------------------------+-----------------+| REPEATABLE-READ | REPEATABLE-READ | REPEATABLE-READ |+-----------------------+------------------------+-----------------+

:幾種事務隔離級別:READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE

4. 檢視gap鎖開啟狀態

123456mysql> show variables like 'innodb_locks_unsafe_for_binlog';+--------------------------------+-------+| Variable_name | Value |+--------------------------------+-------+| innodb_locks_unsafe_for_binlog | OFF |+--------------------------------+-------+

innodb_locks_unsafe_for_binlog:預設值為0,即啟用gap lock。
最主要的作用就是控制innodb是否對gap加鎖。
但是,這一設定變更並不影響外來鍵和唯一索引(含主鍵)對gap進行加鎖的需要。
開啟innodb_locks_unsafe_for_binlog的REPEATABLE-READ事務隔離級別,很大程度上已經蛻變成了READ-COMMITTED。

參見官方文件[^1]:

By default, the value of innodb_locks_unsafe_for_binlog is 0 (disabled), which means that gap locking is enabled: InnoDB uses next-key locks for searches and index scans. To enable the variable, set it to 1. This causes gap locking to be disabled: InnoDB uses only index-record locks for searches and index scans.

Enabling innodb_locks_unsafe_for_binlog does not disable the use of gap locking for foreign-key constraint checking or duplicate-key checking.

The effect of enabling innodb_locks_unsafe_for_binlog is similar to but not identical to setting the transaction isolation level to READ COMMITTED.

5. 檢視自增鎖模式

123456mysql> show variables like 'innodb_autoinc_lock_mode';+--------------------------+-------+| Variable_name | Value |+--------------------------+-------+| innodb_autoinc_lock_mode | 1 |+--------------------------+-------+

innodb_autoinc_lock_mode有3種配置模式:0、1、2,分別對應”傳統模式”, “連續模式”, “交錯模式”。[^8]
傳統模式:涉及auto-increment列的插入語句加的表級AUTO-INC鎖,只有插入執行結束後才會釋放鎖。這是一種相容MySQL 5.1之前版本的策略。
連續模式:可以事先確定插入行數的語句(包括單行和多行插入),分配連續的確定的auto-increment值;對於插入行數不確定的插入語句,仍加表鎖。這種模式下,事務回滾,auto-increment值不會回滾,換句話說,自增列內容會不連續。
交錯模式:同一時刻多條SQL語句產生交錯的auto-increment值。

由於insert語句常常涉及自增列的加鎖過程,會涉及到AUTO-INC Locks加鎖過程。
為了分步瞭解insert加鎖過程,本文暫不討論任何涉及自增列的加鎖邏輯。
這一引數設定相關內容可能會出現在我的下一篇文章裡。

n. etc

相關的引數配置越詳情越好。

三、InnoDB鎖型別[^2]

1. 基本鎖

基本鎖:共享鎖(Shared Locks:S鎖)與排他鎖(Exclusive Locks:X鎖)

mysql允許拿到S鎖的事務讀一行,允許拿到X鎖的事務更新或刪除一行。
加了S鎖的記錄,允許其他事務再加S鎖,不允許其他事務再加X鎖;
加了X鎖的記錄,不允許其他事務再加S鎖或者X鎖。

mysql對外提供加這兩種鎖的語法如下:
加S鎖:select…lock in share mode
加X鎖:select…for update

2. 意向鎖(Intention Locks)

InnoDB為了支援多粒度(表鎖與行鎖)的鎖並存,引入意向鎖。
意向鎖是表級鎖,可分為意向共享鎖(IS鎖)和意向排他鎖(IX鎖)。

InnoDB supports multiple granularity locking which permits coexistence of row-level locks and locks on entire tables. To make locking at multiple granularity levels practical, additional types of locks called intention locks are used. Intention locks are table-level locks in InnoDB that indicate which type of lock (shared or exclusive) a transaction will require later for a row in that table. There are two types of intention locks used in InnoDB (assume that transaction T has requested a lock of the indicated type on table t):
Intention shared (IS): Transaction T intends to set S locks on individual rows in table t.
Intention exclusive (IX): Transaction T intends to set X locks on those rows.

事務在請求S鎖和X鎖前,需要先獲得對應的IS、IX鎖。

Before a transaction can acquire an S lock on a row in table t, it must first acquire an IS or stronger lock on t. Before a transaction can acquire an X lock on a row, it must first acquire an IX lock on t.

意向鎖產生的主要目的是為了處理行鎖和表鎖之間的衝突,用於表明“某個事務正在某一行上持有了鎖,或者準備去持有鎖”。

The main purpose of IX and IS locks is to show that someone is locking a row, or going to lock a row in the table.

共享鎖、排他鎖與意向鎖的相容矩陣如下:

XIXSIS
X衝突衝突衝突衝突
IX衝突相容衝突相容
S衝突衝突相容相容
IS衝突相容相容相容

思考

從官方文件字面意思上看意向鎖是表級鎖,但是大牛不認為“Intention lock 是表級鎖”[^5]?
另外,由於意向鎖主要用於解決行鎖與表鎖間衝突問題,鑑於平時表級操作特別少,在分析加鎖過程是否可以不用過多考慮意向鎖的問題?

3. 行鎖

記錄鎖(Record Locks)

記錄鎖, 僅僅鎖住索引記錄的一行。
單條索引記錄上加鎖,record lock鎖住的永遠是索引,而非記錄本身,即使該表上沒有任何索引,那麼innodb會在後臺建立一個隱藏的聚集主鍵索引,那麼鎖住的就是這個隱藏的聚集主鍵索引。所以說當一條sql沒有走任何索引時,那麼將會在每一條聚集索引後面加X鎖,這個類似於表鎖,但原理上和表鎖應該是完全不同的。

參見官方文件[^3]:

If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.

間隙鎖(Gap Locks)

區間鎖, 僅僅鎖住一個索引區間(開區間)。
在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄之前或者之後加鎖,並不包括該索引記錄本身。

next-key鎖(Next-Key Locks)

record lock + gap lock, 左開右閉區間。

A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.

By default, InnoDB operates in REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows。

預設情況下,innodb使用next-key locks來鎖定記錄。
但當查詢的索引含有唯一屬性的時候,Next-Key Lock 會進行優化,將其降級為Record Lock,即僅鎖住索引本身,不是範圍。

插入意向鎖(Insert Intention Locks)

Gap Lock中存在一種插入意向鎖(Insert Intention Lock),在insert操作時產生。在多事務同時寫入不同資料至同一索引間隙的時候,並不需要等待其他事務完成,不會發生鎖等待。
假設有一個記錄索引包含鍵值4和7,不同的事務分別插入5和6,每個事務都會產生一個加在4-7之間的插入意向鎖,獲取在插入行上的排它鎖,但是不會被互相鎖住,因為資料行並不衝突。

An insert intention lock is a type of gap lock set by INSERT operations prior to row insertion. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6, respectively, each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

:插入意向鎖並非意向鎖,而是一種特殊的間隙鎖。

4. 行鎖的相容矩陣[^4]

GapInsert IntentionRecordNext-Key
Gap相容相容相容相容
Insert Intention衝突相容相容衝突
Record相容相容衝突衝突
Next-Key相容相容衝突衝突

表注:橫向是已經持有的鎖,縱向是正在請求的鎖。

由於S鎖和S鎖是完全相容的,因此在判別相容性時只考慮持有的鎖與請求的鎖是這三種組合情形:X、S和S、X和X、X。
另外,需要提醒注意的是進行相容判斷也只是針對於加鎖涉及的行有交集的情形。

分析相容矩陣可以得出如下幾個結論:

  • INSERT操作之間不會有衝突。
  • GAP,Next-Key會阻止Insert。
  • GAP和Record,Next-Key不會衝突
  • Record和Record、Next-Key之間相互衝突。
  • 已有的Insert鎖不阻止任何準備加的鎖。

5. 自增鎖(AUTO-INC Locks)

AUTO-INC鎖是一種特殊的表級鎖,發生涉及AUTO_INCREMENT列的事務性插入操作時產生。

官方解釋如下[^3]:

An AUTO-INC lock is a special table-level lock taken by transactions inserting into tables with AUTO_INCREMENT columns. In the simplest case, if one transaction is inserting values into the table, any other transactions must wait to do their own inserts into that table, so that rows inserted by the first transaction receive consecutive primary key values.

四、insert加鎖過程

官方文件[^6]對於insert加鎖的描述如下:

INSERT sets an exclusive lock on the inserted row. This lock is an index-record lock, not a next-key lock (that is, there is no gap lock) and does not prevent other sessions from inserting into the gap before the inserted row.

Prior to inserting the row, a type of gap lock called an insert intention gap lock is set. This lock signals the intent to insert in such a way that multiple transactions inserting into the same index gap need not wait for each other if they are not inserting at the same position within the gap. Suppose that there are index records with values of 4 and 7. Separate transactions that attempt to insert values of 5 and 6 each lock the gap between 4 and 7 with insert intention locks prior to obtaining the exclusive lock on the inserted row, but do not block each other because the rows are nonconflicting.

If a duplicate-key error occurs, a shared lock on the duplicate index record is set. This use of a shared lock can result in deadlock should there be multiple sessions trying to insert the same row if another session already has an exclusive lock.

簡單的insert會在insert的行對應的索引記錄上加一個排它鎖,這是一個record lock,並沒有gap,所以並不會阻塞其他session在gap間隙裡插入記錄。

不過在insert操作之前,還會加一種鎖,官方文件稱它為insertion intention gap lock,也就是意向的gap鎖。這個意向gap鎖的作用就是預示著當多事務併發插入相同的gap空隙時,只要插入的記錄不是gap間隙中的相同位置,則無需等待其他session就可完成,這樣就使得insert操作無須加真正的gap lock。
假設有一個記錄索引包含鍵值4和7,不同的事務分別插入5和6,每個事務都會產生一個加在4-7之間的插入意向鎖,獲取在插入行上的排它鎖,但是不會被互相鎖住,因為資料行並不衝突。

假設發生了一個唯一鍵衝突錯誤,那麼將會在重複的索引記錄上加讀鎖。當有多個session同時插入相同的行記錄時,如果另外一個session已經獲得該行的排它鎖,那麼將會導致死鎖。

思考:Insert Intention Locks作用

Insert Intention Locks的引入,我理解是為了提高資料插入的併發能力。
如果沒有Insert Intention Locks的話,可能就需要使用Gap Locks來代替。

五、insert死鎖場景分析

接下來,帶大家看幾個與insert相關的死鎖場景。

1. duplicate key error引發的死鎖

這個場景主要發生在兩個以上的事務同時進行唯一鍵值相同的記錄插入操作。

相關推薦

Java簡單機制,synchronized並解決

下面例子有一定概率deadLock import com.sun.org.apache.regexp.internal.RE; /** * Created by butter on 16/11/1

mysql insert機制(insert)

一、前言上週遇到一個因insert而引發的死鎖問題,其成因比較令人費解。於是想要了解一下insert加鎖機制,但是發現網上介紹的文章比較少且零散,挖掘過程比較忙亂。本以為只需要系統學習一個較完全的邏輯,但是實際牽扯很多innodb鎖相關知識及加鎖方式。我好像並沒有那麼大的能耐,把各種場景的加鎖過程一一列舉並加

mysqlinsert …select …帶來的問題

mysql中 insert …select …帶來的問題 當使用insert...select...進行記錄的插入時,如果select的表是innodb型別的,不論insert的表是什麼型別的表,都會對select的表的紀錄進行鎖定。 對於那些從oracle遷移過來的應用

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

地址:https://www.2cto.com/database/201711/695662.html 前言   我們在實際業務場景中,經常會有一個這樣的需求,插入某條記錄,如果已經存在了則更新它如果更新日期或者某些列上的累加操作等,我們肯定會想到使用INSERT … ON DUPL

InnoDB RR隔離級別下INSERT SELECT兩種案例剖析

作者:高鵬(重慶八怪) 校稿:葉師傅(部分內容有微調) 原文:http://blog.itpub.net/7728585/viewspace-2146183/ 有網友遇到了在RR隔離級別下insert A select B where B.COL=** 發生死鎖的問題。分析死鎖日誌後,筆者模擬重現了2

mysql 開發進階篇系列 14 問題(避免查看分析)

mysq cit 優化 業務流程 update span tro isp 問題 一. 概述   通常來說,死鎖都是應用設計問題,通過調整業務流程,數據庫對象設計,事務大小,以及訪問數據庫的sql語句,絕大部分死鎖都可以避免,下面介紹幾種避免死鎖的常用 方法.  1. 在應

mysql操作表時出現解決方式

情景:有時頻繁地某個表時,發現不能進行增刪改操作時,出現操作超時死鎖的情況 --顯示所有程序 show processlist kill id --顯示是否有死鎖 show   open tables where In_use > 0; --檢視死鎖 se

MySQL併發事務導致的

前提:InnoDB儲存引擎 + 預設的事務隔離級別 Repeatable Read 用MySQL客戶端模擬併發事務操作資料時,如下表按照時間的先後順序執行命令,會導致死鎖。 資料庫資料如下 select * from a ; +----+ | id | +----+ | 3 | +

MySQL 5.7.25資料庫

1、檢視innodb狀態 show engine innodb status\G 2、開啟lock_monitor監控 use databases sys; create table innodb_lock_monitor(x int) engine=innodb; 3、查詢程序 m

解決MySQL事務未提交導致報錯 避免的方法

解決mysql 事務未提交導致死鎖報錯:        當 sessionA 嘗試修改 B 表資料,因為 sessionB 當前為鎖定狀態,而且 sessionB 對 B 表中資料具有鎖定狀態中,則出現死鎖。sessionB 會自動終止嘗試修改 A 表資料事務, 兩個事務操作

MySQL系列 之

一、什麼是死鎖 1.必須滿足的條件1. 必須有兩個或者兩個以上的事務 2. 不同事務之間都持有對方需要的鎖資源。 A事務需要B的資源,B事務需要A的資源,這就是典型的AB-BA死鎖 2.死鎖相關的引數* innodb_print_all_deadlocks 1. 如果這個引數開啟,那麼死鎖相關的資訊都

MySQL機制之行

行鎖     偏向InnoDB儲存引擎,開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高.     InnoDB和MyISAM的最大不同;一是支援事務(TRANSACTION,二是採用了行級鎖.     MySQL事務        事務是

【Java併發基礎】使用“等待—通知”機制優化中佔用且等待解決方案

前言 在前篇介紹死鎖的文章中,我們破壞等待佔用且等待條件時,用了一個死迴圈來獲取兩個賬本物件。 // 一次性申請轉出賬戶和轉入賬戶,直到成功 while(!actr.apply(this, target))   ; 我們提到過,如果apply()操作耗時非常短,且併發衝突量也不大,這種方案還是可以。否則的話,

MySQL系列-線上問題排查思路

### 前言 MySQL 死鎖異常是我們經常會遇到的線上異常類別,一旦線上業務日間複雜,各種業務操作之間往往會產生鎖衝突,有些會導致死鎖異常。這種死鎖異常一般要在特定時間特定資料和特定業務操作才會復現,並且分析解決時還需要了解 MySQL 鎖衝突相關知識,所以一般遇到這些偶爾出現的死鎖異常,往往一時沒有頭緒

什麽是,簡述發生的四個必要條件,如何避免與預防

競爭 時間 鎖死 分配 獲得 進程 發生 未使用 例如 什麽是死鎖 死鎖是指多個進程因競爭資源而造成的一種僵局(互相等待),若無外力作用,這些進程都將無法向前推進。例如,在某一個計算機系統中只有一臺打印機和一臺輸入 設備,進程P1正占用輸入設備,同時又提出使用打印機的請求,

資料庫機制1------共享

一、共享鎖 什麼是共享鎖:簡單來說就是該鎖鎖定的資源只能進行讀取,不能進行增刪改操作,直到該鎖釋放。 example 1: 請求1:select * from  tabletest 請求2:update tabletast set .... 資料庫執行請求

作業系統(10)程序--概念;處理方法:預防、避免、銀行家演算法、檢測

文章目錄 1. 死鎖概念 2. 死鎖處理方法 1. 死鎖預防 2. 死鎖避免 3. 銀行家演算法 4. 死鎖檢測 1. 死鎖概念 死鎖是指:由於競爭資源或者通

什麼是、導致的原因,活、飢餓又是什麼?

死鎖        是指兩個或兩個以上的程序(或執行緒)在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的程序稱為死鎖程序。        一個執行緒先獲取from鎖,另一個

運維必備--如何徹底解決資料庫的超時及問題

之前有介紹過,我主要是做資料倉庫運維的,業餘也會動手寫 python 程式,django 應用,vue 的 app,有興趣可以加我好友一起學習。最近比較讓我頭疼的是資料倉庫的 datastage 作業經常報 911 錯誤,最終問題得到了解決,在此總結一下,希望能幫到要解決 911 錯誤的朋友

C++ ,socket

我們常常對需要多執行緒共同訪問的資源進行加鎖,但當在同一個執行緒中時,一個鎖還沒離開之前,還可以加一道鎖。。。 例:                 CRITICAL_SECTION cs; InitializeCriticalSection(&cs);Ente