搜尋
首頁資料庫SQL驚!一行SQL語句竟然這麼多鎖..

驚!一行SQL語句竟然這麼多鎖..

推薦(免費):SQL教學

#間隙鎖再加上行鎖,很容易判斷是否會出現鎖等待的問題上犯錯。

因為間隙鎖定在可重複讀取隔離等級下才有效,本文預設可重複讀取。

加鎖規則

  • 原則1
    加鎖的基本單位是next-key lock,前開後閉區間。
  • 原則2
    尋找過程中存取到的物件才會加鎖。
  • 優化1
    索引上的等值查詢,給唯一索引加鎖的時候,next-key lock退化為行鎖。
  • 優化2
    索引上的等值查詢,向右遍歷時且最後一個值不滿足等值條件的時候,next-key lock退化為間隙鎖定。
  • 一個bug
    唯一索引上的範圍查詢會存取到不滿足條件的第一個值為止。

資料準備

表名:t
新增資料:(0,0,0),(5,5,5), (10,10,10),(15,15,15),(20,20,20),(25,25,25)
接下來的例子基本上都是配合圖片說明的,所以我建議你可以對照著文稿看,有些例子可能會“毀三觀”,也建議你讀完文章後親手實踐一下。

案例

等值查詢間隙鎖定

  • 等值查詢的間隙鎖定

  • 表t中無id=7,所以根據原則1,加鎖單位next-key lock,所以session A加鎖範圍(5,10]

  • 同時根據最佳化2,等值查詢(id=7),而id=10不滿足,next-key lock退化成間隙鎖,因此最終加鎖範圍(5,10)

所以,session B要往這個間隙裡面插入id=8的記錄會被鎖住,但是session C修改id=10這行是可以的。

非唯一索引等值鎖定

  • 只加在非唯一索引上的鎖定

  • ##session A要給索引c的c=5這行加讀鎖定

    根據原則1,加鎖單位next-key lock,因此給(0,5]加next-key lock
    c是
    普通索引,因此只訪問c=5這條記錄不能馬上停下來,需要向右遍歷,查到c=10才放棄。根據原則2,訪問到的都要加鎖,因此要給(5,10]加next-key lock 同時符合優化2:等值判斷,向右遍歷,最後一個值不滿足c=5這個等值條件,因此退化成間隙鎖(5,10)
    根據原則2 ,只有存取到的物件才會加鎖,這個查詢使用覆蓋索引,並不需要存取主鍵索引,所以主鍵索引上沒有加任何鎖,所以session B的update語句可以執行完成。
    但session C要插入(7,7,7),就會被session A的間隙鎖(5,10)鎖住。

這個例子中,lock in share mode只鎖覆蓋索引,但如果是for update就不一樣了。當執行for update時,系統會認為你接下來要更新數據,因此會順便給主鍵索引上滿足條件的行加上行鎖。

這例說明,鎖是加在索引上的;同時,它給我們的指導是,如果你要用lock in share mode來給行加讀鎖避免資料被更新的話,就必須要繞過覆蓋索引的最佳化,在查詢欄位中加入索引中不存在的欄位。例如,將session A的查詢語句改成select d from t where c=5 lock in share mode。你可以自己驗證一下效果。

3 主鍵索引範圍鎖定

範圍查詢。

對於我們這個表t,下面這兩個查詢語句,加鎖範圍相同嗎?

mysql> select * from t where id=10 for update;

mysql> select * from t where id>=10 and id 你可能會想,id定義為int型,這兩個語句就是等價的吧?其實,它們並不完全等價。

在邏輯上,這兩個查語句一定是等價的,但是它們的加鎖規則不太一樣。現在,我們就請session A執行第二個查詢語句,來看看加鎖效果。

圖3 主鍵索引上範圍查詢的鎖定

現在我們就用前面提到的加鎖規則,來分析一下session A 會加什麼鎖呢?

開始執行的時候,要找出第一個id=10的行,因此本該是next-key lock(5,10]。根據最佳化1, 主鍵id上的等值條件,退化成行鎖,只加了id=10這一行的行鎖。

範圍查找就往後繼續找,找到id=15這一行停下來,因此需要加next-key lock(10,15]。

所以,session A這時候鎖的範圍就是在主鍵索引上,行鎖id=10和next-key lock(10,15]。這樣,session B和session C的結果你就能理解了.

這裡你需要注意一點,首次session A定位查找id=10的行的時候,是當做等值查詢來判斷的,而向右掃描到id=15的時候,用的是範圍查詢判斷。

再看看範圍查詢加鎖,你可以對照著案例三

非唯一索引範圍鎖定

session_1 session_2 session_3
#begin;
select * from t where c>=10 and c



#insert into t values(8,8 ,8);(blocked)


update t 設定 d =d 1 where c=15;(blocked)
  • #session1第一次用c=10定位記錄時,索引c加了(5,10] next-key lock
  • c是非唯一索引,無最佳化規則,即不會退化為行鎖定
  • 因此最終sesion1加鎖為c的(5,10](10,15] next-key lock。
##所以從結果上來看,sesson2要插入(8,8,8)的這個insert語句時就被阻塞。

非唯一索引要掃到c=15,才知道無需繼續往後遍歷。

唯一索引範圍鎖定bug

前四案例用到兩個原則和兩個最佳化,再看加鎖規則bug案例。

##session_1begin; select * from t set d=d 1
session_2 session_3
where id>10 and id








##update t
where id=20;(阻塞)

  • ##insert into t values(16,16,16);(阻塞)

session1是範圍查詢

依原則1,索引id只加

(10,15] next-key lock
,因為id是唯一鍵,所以迴圈判斷到

id=15

這行就該停止遍歷。

但實作上,InnoDB會繼續掃描到第一個不滿足條件的行,即id=20

,且由於這是範圍掃描,因此id上的

(15,20] next-key lock
也會被鎖。
所以session2要更新id=20這行會被阻塞。

session3要插入id=16,也會被阻塞。


按理說鎖住id=20這行沒必要,因為唯一索引掃描到id=15即可確定不用繼續遍歷。但實現上還是這麼做了,可能是個bug。

非唯一索引上存在"等值"的例子

為更好地說明「間隙」概念。

插入記錄7

#新插入的這一行c=10,也就是現在表裡有兩個c=10。那麼,這時索引c上的間隙是什麼狀態了呢? 由於非唯一索引上包含主鍵的值,所以不可能存在「相同」兩行。但現在雖然有兩個c=10,它們的主鍵值id卻不同,因此這兩個c=10記錄之間也有間隙。看如下案例。6delete加鎖邏輯類似select ... for update ,即也符合一開始的規則。 session_3where c=10


session_1
session_2

begin;
delete * 從 t




  • insert into t
  • values(13,13,13);(阻塞)

update t set d=d 1 where c=15;然後,session1 向右找,直到碰到(c=15,id=15)這行,循環結束。根據最佳化2,等值查詢,向右查找到不滿足條件的行,所以退化成(c=10,id=10) 到(c=15,id=15)的間隙鎖(開區間,(c=5,id=5)和(c=15,id=15)這兩行無鎖)。session_1

#session1遍歷時先存取第一個c=10:
依原則1,這裡加是(c=5,id=5)到(c=10,id=10) next-key lock

7 limit 語句加上鎖定

###session_2####################### ###begin; ### delete * from t ###where c=10 limit 2#################################### #insert into t ###values(13,13,13);(阻塞)############

session1 的delete語句加了 limit 2。你知道表t裡c=10的記錄其實只有兩條,因此加不加limit 2,刪除的效果都是一樣的,但是加鎖的效果卻不同。可以看到,session B的insert語句執行通過了,跟案例六的結果不同。

這是因為,案例七里的delete語句明確加了limit 2的限制,因此在遍歷到(c=10, id=30)這一行之後,滿足條件的語句已經有兩條,循環就結束了。

因此,索引c上的加鎖範圍就變成了從(c=5,id=5)到(c=10,id=30)這個前開後閉區間,如下圖所示:

帶有limit 2的加鎖效果

可以看到,(c=10,id=30)之後的這個間隙並沒有在加鎖範圍裡,因此insert語句插入c=12是可以執行成功的。

這個例子對我們實踐的指導意義就是,在刪除資料的時候盡量加上limit。這樣不僅可以控制刪除資料的條數,讓操作更安全,還可以減少加鎖的範圍。

一個死鎖的例子

前面的例子中,我們在分析的時候,是依照next-key lock的邏輯來分析的,因為這樣分析比較方便。最後我們再看一個案例,目的是說明:next-key lock其實是間隙鎖和行鎖加起來的結果。

你一​​定會疑惑,這個概念不是一開始就說了嗎?不要急,我們先來看下面這個例子:

案例八的操作序列

#session A 啟動事務後執行查詢語句加lock in share mode,在索引c上加了next-key lock(5,10] 和間隙鎖(10,15);

session B 的update語句也要在索引c上加next-key lock(5,10] ,進入鎖定等待;

然後session A要再插入(8,8,8)這一行,被session B的間隙鎖鎖住。由於出現了死鎖,InnoDB讓session B回滾。

你可能會問,session B的next-key lock不是還沒申請成功嗎?

#其實是這樣的,session B的「加next-key lock(5,10] 」操作,實際上分成了兩步,先是加(5,10)的間隙鎖,加鎖成功;然後加c=10的行鎖,這時候才被鎖住的。

#也就是說,我們在分析加鎖規則的時候可以用next-key lock來分析。但是要知道,具體執行的時候,是要分成間隙鎖和行鎖兩段來執行的。

總結

所有案例都是在可重複讀取下驗證,可重複讀取遵守兩階段鎖定協議,所有加鎖的資源,都是在事務提交或回滾的時候才釋放。

在最後的案例中,你可以清楚地知道next-key lock實際上是由間隙鎖加行鎖實現的。如果切換到讀取提交隔離級別(read-committed)的話,就好理解了,過程中去掉間隙鎖的部分,也就是只剩下行鎖的部分。

在讀取提交隔離等級下還有一個最佳化,即:語句執行過程中加上的行鎖,在語句執行完成後,就要把「不符合條件的行」上的行鎖直接釋放了,不需要等到交易提交。
讀取提交隔離等級下,鎖的範圍更小,鎖的時間更短,所以不少業務也預設使用讀取提交。

在業務需要使用可重複讀取時,解決幻讀問題同時,最大限度提升系統並行處理事務的能力。

間隙鎖再加上行鎖,很容易在判斷是否會出現鎖定等待的問題上犯錯。

因為間隙鎖定在可重複讀取隔離等級下才有效,本文預設可重複讀取。

更多相關知識敬請造訪SQL免費欄位~~

#

以上是驚!一行SQL語句竟然這麼多鎖..的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:CSDN。如有侵權,請聯絡admin@php.cn刪除
SQL和MySQL:了解核心差異SQL和MySQL:了解核心差異Apr 17, 2025 am 12:03 AM

SQL是一種用於管理關係數據庫的標準語言,而MySQL是一個具體的數據庫管理系統。 SQL提供統一語法,適用於多種數據庫;MySQL輕量、開源,性能穩定但在大數據處理上有瓶頸。

SQL:初學者的學習曲線SQL:初學者的學習曲線Apr 16, 2025 am 12:11 AM

SQL學習曲線陡峭,但通過實踐和理解核心概念可掌握。 1.基礎操作包括SELECT、INSERT、UPDATE、DELETE。 2.查詢執行分為解析、優化、執行三步。 3.基本用法如查詢僱員信息,高級用法如使用JOIN連接表。 4.常見錯誤包括未使用別名和SQL注入,需使用參數化查詢防範。 5.性能優化通過選擇必要列和保持代碼可讀性實現。

SQL:命令,mysql:引擎SQL:命令,mysql:引擎Apr 15, 2025 am 12:04 AM

SQL命令在MySQL中分為DQL、DDL、DML、DCL和TCL五類,用於定義、操作和控制數據庫數據。 MySQL通過詞法分析、語法分析、優化和執行等階段處理SQL命令,並利用索引和查詢優化器提升性能。使用示例包括SELECT用於數據查詢,JOIN用於多表操作。常見錯誤有語法、邏輯和性能問題,優化策略包括使用索引、優化查詢和選擇合適的存儲引擎。

用於數據分析的SQL:商業智能的高級技術用於數據分析的SQL:商業智能的高級技術Apr 14, 2025 am 12:02 AM

SQL的高級查詢技巧包括子查詢、窗口函數、CTE和復雜JOIN,能夠處理複雜數據分析需求。 1)子查詢用於找出每個部門工資最高的員工。 2)窗口函數和CTE用於分析員工的薪資增長趨勢。 3)性能優化策略包括索引優化、查詢重寫和使用分區表。

MySQL:SQL的特定實現MySQL:SQL的特定實現Apr 13, 2025 am 12:02 AM

MySQL是開源的關係型數據庫管理系統,提供了標準SQL功能和擴展。 1)MySQL支持標準SQL操作如CREATE、INSERT、UPDATE、DELETE,並擴展了LIMIT子句。 2)它使用InnoDB和MyISAM等存儲引擎,適用於不同場景。 3)用戶可以通過創建表、插入數據和使用存儲過程等高級功能高效使用MySQL。

SQL:使所有人都可以訪問數據管理SQL:使所有人都可以訪問數據管理Apr 12, 2025 am 12:14 AM

sqlmakesdatamanagectAccessibletoAllbyProvidingAsimpleyetpoperfultoolSetSetForQuerquereingAndManagingDatabases.1)ItworkswithrelationalDatabases,允許inserstospecefifywhattheywanttododowithththedata.2)

SQL索引策略:通過數量級提高查詢性能SQL索引策略:通過數量級提高查詢性能Apr 11, 2025 am 12:04 AM

SQL索引可以通过巧妙的设计显著提升查询性能。1.选择合适的索引类型,如B-tree、哈希或全文索引。2.使用复合索引优化多字段查询。3.避免过度索引以减少数据维护开销。4.定期维护索引,包括重建和移除不必要的索引。

sql怎麼刪除約束sql怎麼刪除約束Apr 10, 2025 pm 12:21 PM

在 SQL 中刪除約束,請執行以下步驟:識別要刪除的約束名稱;使用 ALTER TABLE 語句:ALTER TABLE 表名 DROP CONSTRAINT 約束名;確認刪除。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)