MySQL選用可重複讀之前一定要想到的事情
MySQL選用可重複讀隔離級別之前一定要想到的事情.
間隙鎖
MySQL在使用之前有三個務必要知道..(隨著瞭解的深入這個極有可能再增加..)
1.DDL會引起metadata lock,導致請求連環阻塞,甚至是查詢請求.
http://blog.itpub.net/29254281/viewspace-1383193/
2.MySQLDump和XtraBackup的flush table命令會引起waiting for table連環阻塞,同樣也會阻塞查詢請求.
http://blog.itpub.net/29254281/viewspace-1392757/
3.選用可重複讀事務隔離級別,會開啟間隙鎖.他鎖定的內容比實際需要的要多,並且很可能導致死鎖.
本文是何登成大神文章的讀後感和總結.
連結如下:
(關於這方面最好的文章,沒有之一)
Oracle和MySQL(讀提交和可重複讀)都實現了MVCC多版本併發控制.
這意味著普通的Select(一致性讀或者說快照讀)可以以非鎖定的形式讀取資料.
而當前讀(oracle的db block gets)都需要加鎖.
比如:
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
除了第一個SQL對記錄上共享鎖,其餘都是排他鎖.
MySQL雖然在可重複讀事務隔離級別實現了避免幻讀.
但是可重複讀隔離級別的當前讀,會啟動間隙鎖.
當前讀(鎖定讀)加鎖情況.
根據何老師的例子(另記一份,現在的網站也是說沒就沒)
檢視同樣的SQL
(delete from t1 where id = 10;)
在不同的情況下,鎖定的情況.
首先是讀提交事務隔離級別,
如果id是主鍵索引,鎖定主鍵索引的鍵值
如果id是唯一索引,鎖定唯一索引和主鍵索引的相關鍵值.
如果id是普通索引,則鎖定普通索引和主鍵索引的相關鍵值.
最後,如果id沒有索引,在InnoDB層,會對所有主鍵索引上排他鎖,到MySQL伺服器層對不符合條件的記錄進行解鎖.
而在可重複讀事務隔離級別,
如果id是主鍵索引,和讀提交一樣,會鎖定主鍵索引的相關鍵值.
如果id是唯一索引,MySQL會將間隙鎖退化為行級鎖,僅僅鎖定唯一索引和主鍵索引的相關鍵值.同讀提交隔離級別一樣.
例外:
如果是組合的唯一索引 create unique index inx_1 on test(a,b,c);
但是查詢只用到了a,b select * from test where a=? and b=? for update
MySQL這時不會退化為行級鎖,他的處理方式會等同於下面說到的普通索引.(行級鎖+間隙鎖)
如果id是普通索引,MySQL會鎖定普通索引和主鍵索引的相關鍵值,並且鎖定相關普通索引之間的間隙.
以上圖為例,select * from t1 where id=100 for update;
這個SQL沒有查到任何的記錄,但是他同樣會上間隙鎖.
(在這種情況下,即使id是主鍵索引或者唯一索引,也會產生間隙鎖)
在可重複讀的隔離級別下,如果id沒有索引
他會鎖定主鍵索引的所有記錄和所有間隙.
和讀提交不一樣,在MySQL伺服器層,並不會對不符合條件的記錄解鎖.並且它會鎖定主鍵索引的所有間隙.
參考何老師文章中的一個例子
在可重複讀隔離級別下,
Index key:pubtime > 1 and puptime < 20。此條件,用於確定SQL在idx_t1_pu索引上的查詢範圍。
Index Filter:userid = ‘hdc’ 。此條件,可以在idx_t1_pu索引上進行過濾,但不屬於Index Key。
Table Filter:comment is not NULL。此條件,在idx_t1_pu索引上無法過濾,只能在聚簇索引上過濾。
他的加鎖情況如下
從圖中可以看出,在Repeatable Read隔離級別下,由Index Key所確定的範圍,被加上了GAP鎖;Index Filter鎖給定的條件 (userid = ‘hdc’)何時過濾,視MySQL的版本而定,在MySQL 5.6版本之前,不支援Index Condition Pushdown(ICP),因此Index Filter在MySQL Server層過濾,在5.6後支援了Index Condition Pushdown,則在index上過濾。若不支援ICP,不滿足Index Filter的記錄,也需要加上記錄X鎖,若支援ICP,則不滿足Index Filter的記錄,無需加記錄X鎖 (圖中,用紅色箭頭標出的X鎖,是否要加,視是否支援ICP而定);而Table Filter對應的過濾條件,則在聚簇索引中讀取後,在MySQL Server層面過濾,因此聚簇索引上也需要X鎖。最後,選取出了一條滿足條件的記錄[8,hdc,d,5,good],但是加鎖的數量,要遠遠大於滿足條件的記錄數量。
結論:在Repeatable Read隔離級別下,針對一個複雜的SQL,首先需要提取其where條件。Index Key確定的範圍,需要加上GAP鎖;Index Filter過濾條件,視MySQL版本是否支援ICP,若支援ICP,則不滿足Index Filter的記錄,不加X鎖,否則需要X鎖;Table Filter過濾條件,無論是否滿足,都需要加X鎖。
以上是對何老師文章的總結.
這個文章看了很長時間,但是有一個問題一直很疑惑.
實驗環境
實驗資料
create table t
(
a int primary key,
b int,
c int,
key (b,c)
) engine=innodb;
insert into t
values
(1,10,10),
(3,10,20),
(5,20,30),
(7,20,40),
(9,20,50);
commit;
終端一:
select * from t where b=10 and c=10 for update;
理論上,他會鎖定 (b=10 c=10)的輔助索引,(a=1)的主鍵索引並且會鎖定(10,負無窮)至(10,10),(10,10)至(10,20)的範圍,也就是間隙鎖.
但是終端二:
select * from t where b=10 and c=15 for update;
查詢並沒有阻塞.這不對啊.因為在這個隔離級別下,即使沒有資料,也會產生間隙鎖.
終端三:
回滾終端二,在終端三輸入
insert into t values(50,10,15);
我發現這個SQL被阻塞了,也就是說明終端一的間隙鎖是存在的.
這個問題查閱了官方文件,
http://dev.mysql.com/doc/refman/5.6/en/innodb-record-level-locks.html
但其實沒有看懂..
在缺乏理論依據的基礎上,
我猜測雖然當前讀的行鎖是排他的(除了那個lock in share mode).
但是間隙鎖有共享鎖的性質,一旦出現間隙鎖,在這個間隙的範圍內不能新出現任何記錄
無論是update修改過來的,還是insert新增過來的
只要間隙內不出現新的記錄,間隙鎖之間就可以共存.
參考:
開啟鎖監控,可以在show engine innodb status\G看到更多鎖資訊,並且定時將資訊寫入錯誤日誌.
create table innodb_lock_monitor(a int) engine=innodb;
檢視ICP是否開啟
select @@optimizer_switch\G
間隙鎖
MySQL在使用之前有三個務必要知道..(隨著瞭解的深入這個極有可能再增加..)
1.DDL會引起metadata lock,導致請求連環阻塞,甚至是查詢請求.
http://blog.itpub.net/29254281/viewspace-1383193/
2.MySQLDump和XtraBackup的flush table命令會引起waiting for table連環阻塞,同樣也會阻塞查詢請求.
http://blog.itpub.net/29254281/viewspace-1392757/
3.選用可重複讀事務隔離級別,會開啟間隙鎖.他鎖定的內容比實際需要的要多,並且很可能導致死鎖.
本文是何登成大神文章的讀後感和總結.
連結如下:
(關於這方面最好的文章,沒有之一)
Oracle和MySQL(讀提交和可重複讀)都實現了MVCC多版本併發控制.
這意味著普通的Select(一致性讀或者說快照讀)可以以非鎖定的形式讀取資料.
而當前讀(oracle的db block gets)都需要加鎖.
比如:
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
除了第一個SQL對記錄上共享鎖,其餘都是排他鎖.
MySQL雖然在可重複讀事務隔離級別實現了避免幻讀.
但是可重複讀隔離級別的當前讀,會啟動間隙鎖.
當前讀(鎖定讀)加鎖情況.
|
讀提交 | 可重複讀 |
主鍵索引 |
鎖定主鍵索引 |
鎖定主鍵索引 |
唯一索引 |
鎖定唯一索引的值和主鍵索引的值 |
鎖定唯一索引的值和主鍵索引的值 |
普通索引 |
鎖定普通索引的值和主鍵索引的值 |
鎖定普通索引的值和主鍵索引的值,普通索引增加間隙鎖 |
無索引 |
鎖定所有的主鍵索引,在伺服器層對不符合條件的記錄解鎖 |
鎖定所有的主鍵索引和主鍵索引的間隙 |
根據何老師的例子(另記一份,現在的網站也是說沒就沒)
檢視同樣的SQL
(delete from t1 where id = 10;)
在不同的情況下,鎖定的情況.
首先是讀提交事務隔離級別,
如果id是主鍵索引,鎖定主鍵索引的鍵值
如果id是唯一索引,鎖定唯一索引和主鍵索引的相關鍵值.
如果id是普通索引,則鎖定普通索引和主鍵索引的相關鍵值.
最後,如果id沒有索引,在InnoDB層,會對所有主鍵索引上排他鎖,到MySQL伺服器層對不符合條件的記錄進行解鎖.
而在可重複讀事務隔離級別,
如果id是主鍵索引,和讀提交一樣,會鎖定主鍵索引的相關鍵值.
如果id是唯一索引,MySQL會將間隙鎖退化為行級鎖,僅僅鎖定唯一索引和主鍵索引的相關鍵值.同讀提交隔離級別一樣.
例外:
如果是組合的唯一索引 create unique index inx_1 on test(a,b,c);
但是查詢只用到了a,b select * from test where a=? and b=? for update
MySQL這時不會退化為行級鎖,他的處理方式會等同於下面說到的普通索引.(行級鎖+間隙鎖)
如果id是普通索引,MySQL會鎖定普通索引和主鍵索引的相關鍵值,並且鎖定相關普通索引之間的間隙.
以上圖為例,select * from t1 where id=100 for update;
這個SQL沒有查到任何的記錄,但是他同樣會上間隙鎖.
(在這種情況下,即使id是主鍵索引或者唯一索引,也會產生間隙鎖)
在可重複讀的隔離級別下,如果id沒有索引
他會鎖定主鍵索引的所有記錄和所有間隙.
和讀提交不一樣,在MySQL伺服器層,並不會對不符合條件的記錄解鎖.並且它會鎖定主鍵索引的所有間隙.
參考何老師文章中的一個例子
在可重複讀隔離級別下,
Index key:pubtime > 1 and puptime < 20。此條件,用於確定SQL在idx_t1_pu索引上的查詢範圍。
Index Filter:userid = ‘hdc’ 。此條件,可以在idx_t1_pu索引上進行過濾,但不屬於Index Key。
Table Filter:comment is not NULL。此條件,在idx_t1_pu索引上無法過濾,只能在聚簇索引上過濾。
他的加鎖情況如下
從圖中可以看出,在Repeatable Read隔離級別下,由Index Key所確定的範圍,被加上了GAP鎖;Index Filter鎖給定的條件 (userid = ‘hdc’)何時過濾,視MySQL的版本而定,在MySQL 5.6版本之前,不支援Index Condition Pushdown(ICP),因此Index Filter在MySQL Server層過濾,在5.6後支援了Index Condition Pushdown,則在index上過濾。若不支援ICP,不滿足Index Filter的記錄,也需要加上記錄X鎖,若支援ICP,則不滿足Index Filter的記錄,無需加記錄X鎖 (圖中,用紅色箭頭標出的X鎖,是否要加,視是否支援ICP而定);而Table Filter對應的過濾條件,則在聚簇索引中讀取後,在MySQL Server層面過濾,因此聚簇索引上也需要X鎖。最後,選取出了一條滿足條件的記錄[8,hdc,d,5,good],但是加鎖的數量,要遠遠大於滿足條件的記錄數量。
結論:在Repeatable Read隔離級別下,針對一個複雜的SQL,首先需要提取其where條件。Index Key確定的範圍,需要加上GAP鎖;Index Filter過濾條件,視MySQL版本是否支援ICP,若支援ICP,則不滿足Index Filter的記錄,不加X鎖,否則需要X鎖;Table Filter過濾條件,無論是否滿足,都需要加X鎖。
以上是對何老師文章的總結.
這個文章看了很長時間,但是有一個問題一直很疑惑.
實驗環境
實驗資料
create table t
(
a int primary key,
b int,
c int,
key (b,c)
) engine=innodb;
insert into t
values
(1,10,10),
(3,10,20),
(5,20,30),
(7,20,40),
(9,20,50);
commit;
終端一:
select * from t where b=10 and c=10 for update;
理論上,他會鎖定 (b=10 c=10)的輔助索引,(a=1)的主鍵索引並且會鎖定(10,負無窮)至(10,10),(10,10)至(10,20)的範圍,也就是間隙鎖.
但是終端二:
select * from t where b=10 and c=15 for update;
查詢並沒有阻塞.這不對啊.因為在這個隔離級別下,即使沒有資料,也會產生間隙鎖.
終端三:
回滾終端二,在終端三輸入
insert into t values(50,10,15);
我發現這個SQL被阻塞了,也就是說明終端一的間隙鎖是存在的.
這個問題查閱了官方文件,
http://dev.mysql.com/doc/refman/5.6/en/innodb-record-level-locks.html
但其實沒有看懂..
在缺乏理論依據的基礎上,
我猜測雖然當前讀的行鎖是排他的(除了那個lock in share mode).
但是間隙鎖有共享鎖的性質,一旦出現間隙鎖,在這個間隙的範圍內不能新出現任何記錄
無論是update修改過來的,還是insert新增過來的
只要間隙內不出現新的記錄,間隙鎖之間就可以共存.
參考:
開啟鎖監控,可以在show engine innodb status\G看到更多鎖資訊,並且定時將資訊寫入錯誤日誌.
create table innodb_lock_monitor(a int) engine=innodb;
檢視ICP是否開啟
select @@optimizer_switch\G
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1398273/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL選用可重複讀之前一定要想到的事情(ICP驗證和勘誤)MySql
- MySQL選用可重複讀之前一定要想到的事情(執行計劃影響)MySql
- MySQL選用可重複讀之前一定要想到的事情(無索引加鎖驗證)MySql索引
- MySQL在建立索引之前一定要想到的事情MySql索引
- MySQLDump在使用之前一定要想到的事情MySql
- 【MySQL】可重複讀下的幻讀MySql
- MySQL 之隔離級別:可重複讀MySql
- MySQL的可重複讀級別能解決幻讀嗎MySql
- Mysql可重複讀(1) —— 快照何時建立MySql
- Mysql RC/RR隔離原理和區別 不可重複讀和可重複讀MySql
- 為什麼mysql選可重複讀作為預設的隔離級別MySql
- 【MySQL】可重複讀模式下 unique key失效案例MySql模式
- 重讀 swift 之一:Optional(可選型)Swift
- MySQL 實戰 | 08 懵逼,可重複讀好像失效了?MySql
- MySQL 可重複讀,差點就我背上了一個 P0 事故!MySql
- 簡單聊聊mysql的髒讀、不可重複讀MySql
- 【Mysql】資料庫事務,髒讀、幻讀、不可重複讀MySql資料庫
- sql語句中where一定要放在group by 之前SQL
- 髒讀!幻讀!不可重複讀!mysql併發事務引發的問題MySql
- 一文詳解髒讀、不可重複讀、幻讀
- 髒讀,幻讀,不可重複讀
- MySQL 事務隔離實驗-認識:髒讀、不可重複讀、幻讀MySql
- 生成14個可重複一次的隨機數隨機
- mysql 查詢出重複資料的第一條MySql
- [Flutter翻譯]我希望在構建Flutter應用程式之前知道的事情Flutter
- 一句話從 MySQL 取出重複行MySql
- 髒讀、不可重複讀、幻讀區別
- MySQL 查詢重複的資料MySql
- 一定要會用selenium的等待,3種等待方式解讀
- 資料庫中的-髒讀,幻讀,不可重複讀資料庫
- 使用Redis之前5個必須瞭解的事情Redis
- mysql 刪除重複項MySql
- mysql 清除重複資料MySql
- MySQL中處理各種重複的一些方法MySql
- 如何編寫一個可複用元件元件
- MySQL資料庫行去重複和列去重複MySql資料庫
- C#如何建立一個可快速重複使用的專案模板C#
- excel怎麼篩選重複的內容 excel找出重複項並提取Excel