RR與RC隔離級別下MySQL不同的加鎖解鎖方式
| RC與RR隔離級別下MySQL不同的加鎖解鎖方式
-
MySQL5.7.21
-
資料準備
點選(此處)摺疊或開啟
-
root@localhost : pxs 05:26:27> show create table dots\G
-
*************************** 1. row ***************************
-
Table: dots
-
Create Table: CREATE TABLE `dots` ( `id` int(11) NOT NULL, `color` varchar(20) COLLATE utf8_bin NOT NULL,
-
PRIMARY KEY (`id`)
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin 1 row in set (0.00 sec)
-
root@localhost : pxs 05:27:34> select * from dots;
-
+----+-------+ | id | color |
-
+----+-------+
-
| 1 | black | | 2 | white |
-
| 3 | black | | 4 | white |
-
+----+-------+
-
4 rows in set (0.00 sec)
-
root@localhost : pxs 01:57:02> show variables like 'innodb_locks_unsafe_for_binlog';
-
+--------------------------------+-------+
-
| Variable_name | Value | +--------------------------------+-------+ | innodb_locks_unsafe_for_binlog | OFF |
-
+--------------------------------+-------+
-
1 row in set (0.00 sec)
點選(此處)摺疊或開啟
-
root@localhost : pxs 05:26:27> show create table dots\G
-
*************************** 1. row ***************************
-
Table: dots
-
Create Table: CREATE TABLE `dots` ( `id` int(11) NOT NULL, `color` varchar(20) COLLATE utf8_bin NOT NULL,
-
PRIMARY KEY (`id`)
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin 1 row in set (0.00 sec)
-
root@localhost : pxs 05:27:34> select * from dots;
-
+----+-------+ | id | color |
-
+----+-------+
-
| 1 | black | | 2 | white |
-
| 3 | black | | 4 | white |
-
+----+-------+
-
4 rows in set (0.00 sec)
-
root@localhost : pxs 01:57:02> show variables like 'innodb_locks_unsafe_for_binlog';
-
+--------------------------------+-------+
-
| Variable_name | Value | +--------------------------------+-------+ | innodb_locks_unsafe_for_binlog | OFF |
-
+--------------------------------+-------+
- 1 row in set (0.00 sec)
1.RC隔離級別
-
確認隔離級別
點選(此處)摺疊或開啟
-
root@localhost : pxs 05:27:35> show variables like '%iso%';
-
+-----------------------+----------------+ | Variable_name | Value |
-
+-----------------------+----------------+
-
| transaction_isolation | READ-COMMITTED | | tx_isolation | READ-COMMITTED |
-
+-----------------------+----------------+
- 2 rows in set (0.01 sec)
-
root@localhost : pxs 05:27:35> show variables like '%iso%';
-
同時開啟兩個會話,按下圖的流程開始操作。
2.RR隔離級別
-
確認隔離級別
點選(此處)摺疊或開啟
-
root@localhost : pxs 05:24:41> show variables like '%iso%';
-
+-----------------------+-----------------+ | Variable_name | Value |
-
+-----------------------+-----------------+
-
| transaction_isolation | REPEATABLE-READ | | tx_isolation | REPEATABLE-READ |
-
+-----------------------+-----------------+
- 2 rows in set (0.01 sec)
-
同時開啟兩個會話,按下圖的流程開始操作。
3.半一致讀semi-consistent read
3.1 半一致讀發生條件
-
RC隔離級別
-
RR隔離級別,且innodb_locks_unsafe_for_binlog=true
3.2 innodb_locks_unsafe_for_binlog
-
innodb_locks_unsafe_for_binlog預設為off。
-
如果設定為1,會禁用gap鎖,但對於外來鍵衝突檢測(foreign-key constraint checking)或者重複鍵檢測(duplicate-key checking)還是會用到gap鎖。
-
啟用innodb_locks_unsafe_for_binlog產生的影響等同於將隔離級別設定為RC,不同之處是:
1)innodb_locks_unsafe_for_binlog是全域性引數,影響所有session;但隔離級別可以是全域性也可以是會話級別。
2)innodb_locks_unsafe_for_binlog只能在資料庫啟動的時候設定;但隔離級別可以隨時更改。
基於上述原因,RC相比於innodb_locks_unsafe_for_binlog會更好更靈活。
啟用innodb_locks_unsafe_for_binlog還有以下作用:
-
對於update或者delete語句,InnoDB只會持有匹配條件的記錄的鎖。在MySQL Server過濾where條件,發現不滿足後,會把不滿足條件的記錄釋放鎖。這可以大幅降低死鎖發生的機率。
-
簡單來說,semi-consistent read是read committed與consistent read兩者的結合。一個update語句,如果讀到一行已經加鎖的記錄,此時InnoDB返回記錄最近提交的版本,由MySQL上層判斷此版本是否滿足update的where條件。若滿足(需要更新),則MySQL會重新發起一次讀操作,此時會讀取行的最新版本(並加鎖)。
來看下面這個例子:
點選(此處)摺疊或開啟
- CREATE TABLE t (a INT NOT NULL, b INT) ENGINE = InnoDB; INSERT INTO t VALUES (1,2),(2,3),(3,2),(4,3),(5,2); COMMIT;
這個例子中,表上沒有索引,所以對於記錄鎖會用到隱藏主鍵。
假設某個client開啟了一個update:
點選(此處)摺疊或開啟
- SET autocommit = 0; UPDATE t SET b = 5 WHERE b = 3;
假設另一個client緊接著也開啟一個update:
點選(此處)摺疊或開啟
每當InnoDB發起update,會先對每一行記錄加上排它鎖,然後再決定記錄是否滿足條件。如果不匹配,則innodb_locks_unsafe_for_binlog開啟,InnoDB就會把記錄上的鎖釋放掉。否則,InnoDB會一直持有鎖直到事務結束。具體如下:
如果innodb_locks_unsafe_for_binlog沒有開啟,第一個update會一直持有x鎖
點選(此處)摺疊或開啟
第二個update會阻塞住直到第一個update提交或者回滾
點選(此處)摺疊或開啟
如果innodb_locks_unsafe_for_binlog開啟,第一個update先持有x鎖,然後會釋放不匹配的記錄上面的x鎖
點選(此處)摺疊或開啟
-
x-lock(1,2); unlock(1,2)
-
x-lock(2,3); update(2,3) to (2,5); retain x-lock x-lock(3,2); unlock(3,2)
- x-lock(4,3); update(4,3) to (4,5); retain x-lock x-lock(5,2); unlock(5,2)
對於第二個update,InnoDB會開啟半一致讀,此時InnoDB返回記錄最近提交的版本,由MySQL上層判斷此版本是否滿足update的where條件。
點選(此處)摺疊或開啟
-
x-lock(1,2); update(1,2) to (1,4); retain x-lock x-lock(2,3); unlock(2,3)
-
x-lock(3,2); update(3,2) to (3,4); retain x-lock x-lock(4,3); unlock(4,3)
- x-lock(5,2); update(5,2) to (5,4); retain x-lock
4.一開始的例子
4.1 RC隔離級別
session 1
session 1執行:
點選(此處)摺疊或開啟
- update dots set color = 'black' where color = 'white';
由於color列無索引,因此只能走聚簇索引,進行全部掃描。加鎖如下:
注:如果一個條件無法透過索引快速過濾,那麼儲存引擎層面就會將所有記錄加鎖後返回,然後由MySQL Server層進行過濾。因此也就把所有的記錄,都鎖上了。
但在實際中,MySQL做了最佳化,如同前面作用1所提到的。在MySQL Server過濾條件,發現不滿足後,會呼叫unlock_row方法,把不滿足條件的記錄放鎖 (違背了2PL的約束)。這樣做,保證了最後只會持有滿足條件記錄上的鎖,但是每條記錄的加鎖操作還是不能省略的。
實際加鎖如下:
session 2
session 2執行:
點選(此處)摺疊或開啟
session 2嘗試加鎖的時候,發現行上已經存在鎖,InnoDB會開啟semi-consistent read,返回最新的committed版本(1,black),(2,white),(3,black),(4,white)。MySQL會重新發起一次讀操作,此時會讀取行的最新版本(並加鎖)。如同前面作用2所提到的。
加鎖如下:
MySQL最佳化後實際加鎖如下:
4.2 RR隔離級別
session 1
session 1執行:
點選(此處)摺疊或開啟
由於color列無索引,因此只能走聚簇索引,進行全部掃描。加鎖如下:
session 2
session 2執行:
點選(此處)摺疊或開啟
等session 1提交commit之後,session 2update才會成功。
引申:RR隔離級別,且開啟innodb_locks_unsafe_for_binlog=ON
-
環境準備
點選(此處)摺疊或開啟
-
root@localhost : (none) 04:57:46> show variables like '%iso%';
-
+-----------------------+-----------------+ | Variable_name | Value |
-
+-----------------------+-----------------+
-
| transaction_isolation | REPEATABLE-READ | | tx_isolation | REPEATABLE-READ |
-
+-----------------------+-----------------+
-
2 rows in set (0.01 sec)
-
root@localhost : (none) 04:55:25> show variables like 'innodb_locks_unsafe_for_binlog';
-
+--------------------------------+-------+
-
| Variable_name | Value | +--------------------------------+-------+ | innodb_locks_unsafe_for_binlog | ON |
-
+--------------------------------+-------+
-
1 row in set (0.00 sec)
-
root@localhost : pxs 05:00:54> select * from dots;
-
+----+-------+
-
| id | color | +----+-------+ | 1 | black |
-
| 2 | white | | 3 | black |
- | 4 | white | +
-
開始操作
注:過程現象滿足RR隔離級別,也符合設定innodb_locks_unsafe_for_binlog=ON的情況。因為前面所講的啟用innodb_locks_unsafe_for_binlog會產生作用1與作用2,所以整個加鎖與解鎖情況與RC隔離級別類似。
參考:
《資料庫事務處理的藝術:事務管理與併發控制》
https://dev.mysql.com/doc/refman/5.5/en/innodb-parameters.html#sysvar_innodb_locks_unsafe_for_binlog
| 作者簡介
韓傑 沃趣科技MySQL資料庫工程師
熟悉mysql體系架構、主從複製,熟悉問題定位與解決。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28218939/viewspace-2154284/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL在RR隔離級別下的unique失效和死鎖模擬MySql
- Mysql鎖與事務隔離級別MySql
- Innodb:RR隔離級別下insert...select 對select表加鎖模型和死鎖案列模型
- 詳解Mysql事務隔離級別與鎖機制MySql
- Mysql事務隔離級別與鎖機制MySql
- Mysql加鎖過程詳解(6)-資料庫隔離級別(1)MySql資料庫
- mysql事務隔離級別和鎖MySql
- MySQL RR隔離級別的更新衝突策略MySql
- Mysql加鎖過程詳解(6)-資料庫隔離級別(2)-通過例子理解事務的4種隔離級別MySql資料庫
- mysql(InnoDB)事務隔離級別(REPEATABLE READ) 與 鎖,MVCCMySqlMVC
- 事務隔離(二):基於加鎖方式的事務隔離原理
- MySQL資料庫事務各隔離級別加鎖情況--Repeatable ReaMySql資料庫
- MySQL資料庫引擎、事務隔離級別、鎖MySql資料庫
- Fescar鎖和隔離級別的理解
- MySQL 預設隔離級別是RR,為什麼阿里這種大廠會改成RC?MySql阿里
- MySQL InnoDB中的事務隔離級別和鎖的關係MySql
- MySQL資料庫事務各隔離級別加鎖情況--read uncommittMySql資料庫MIT
- MySQL 事務的隔離級別及鎖操作的一點點演示MySql
- Mysql RC/RR隔離原理和區別 不可重複讀和可重複讀MySql
- SQL鎖機制和事務隔離級別SQL
- 深入理解Mysql事務隔離級別與鎖機制問題RLOGMySql
- MySQL的隔離級別MySql
- PostgreSQL 併發控制機制(4):RR隔離級別,MySQL vs PostgreSQLMySql
- Mysql 隔離級別MySql
- MySQL:RR模式下死鎖一列MySql模式
- mysql 事務,鎖,隔離機制MySql
- PostgreSQL10.0preview功能增強-序列隔離級別預加鎖閾值可控SQLView
- MySQL事務隔離級別詳解MySql
- 在?MySQL事務隔離級別瞭解一下?MySql
- MySQL事務的隔離級別MySql
- MySQL的事務隔離級別MySql
- MySQL 的隔離級別 自理解MySql
- Mysql加鎖與實踐MySql
- MySQL 事務隔離級別MySql
- MySQL入門--隔離級別MySql
- MySQL設定隔離級別MySql
- MySQL事務隔離級別MySql
- [Mysql]事務/隔離級別MySql