一致性讀,又稱為快照讀。使用的是MVCC機制讀取undo中的已經提交的數據。所以它的讀取是非阻塞的。
相關文檔:http://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html
A consistent read means that InnoDB
uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction.
一致性讀肯定是讀取在某個時間點已經提交了的數據,有個特例:本事務中修改的數據,即使未提交的數據也可以在本事務的后面部分讀取到。
1. RC 隔離 和 RR 隔離中一致性讀的區別
根據隔離級別的不同,一致性讀也是不一樣的。不同點在於判斷是否提交的“某個時間點”:
1)對於RR隔離:
If the transaction isolation level is REPEATABLE READ
(the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction.
文檔中說的是:the first such read in that transaction。實際上實驗的結果表明,這里的 the first such read指的是:對同一個表或者不同表進行的第一次select語句建立了該事務中一致性讀的snapshot. 其它update, delete, insert 語句和一致性讀snapshot的建立沒有關系。在snapshot建立之后提交的數據,一致性讀就讀不到,之前提交的數據就可以讀到。
事務的起始點其實是以執行的第一條語句為起始點的,而不是以begin作為事務的起始點的。
實驗1:
sesseion A
|
session B
|
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
|
mysql> set tx_isolation='repeatable-read';
Query OK, 0 rows affected (0.00 sec)
|
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
|
|
|
mysql> select * from t1;
Empty set (0.00 sec)
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
|
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
|
|
上面的實驗說明:RR隔離級別下的一致性讀,不是以begin開始的時間點作為snapshot建立時間點,而是以第一條select語句的時間點作為snapshot建立的時間點。
實驗2:
session A
|
session B
|
mysql> set tx_isolation='repeatable-read';
|
mysql> set tx_isolation='repeatable-read';
|
|
mysql> select * from t1;
Empty set (0.00 sec)
|
mysql> begin;
mysql> select * from t;
|
|
|
mysql> insert into t1(c1,c2) values(1,1);
Query OK, 1 row affected (0.01 sec)
|
mysql> select * from t1;
Empty set (0.00 sec) |
|
該使用說明:RR隔離級別下的一致性讀,是以第一條select語句的執行點作為snapshot建立的時間點的,即使是不同表的select語句。這里因為session A在insert之前對 t 表執行了select,所以建立了snapshot,所以后面的select * from t1 不能讀取到insert的插入的值。
實驗3:
session A
|
session B
|
mysql> set tx_isolation='repeatable-read';
|
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
|
mysql> begin;
|
|
mysql> select * from t1;
Empty set (0.00 sec)
|
mysql> select * from t1;
Empty set (0.00 sec)
|
|
mysql> insert into t1(c1,c2) values(1,1);
|
mysql> select * from t1;
Empty set (0.01 sec)
|
|
該實驗中:session A 的第一條語句,發生在session B的 insert語句提交之前,所以session A中的第二條select還是不能讀取到數據。因為RR中的一致性讀是以事務中第一個select語句執行的時間點作為snapshot建立的時間點的。而此時,session B的insert語句還沒有執行,所以讀取不到數據。
實驗4:
session A
|
session B
|
mysql> set tx_isolation='repeatable-read';
|
mysql> set tx_isolation='repeatable-read';
mysql> select * from t1;
Empty set (0.00 sec)
|
mysql> select * from t1;
Empty set (0.00 sec)
|
|
|
mysql> insert into t1(c1,c2) values(1,1),(2,2);
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
| 2 | 2 |
+----+------+
2 rows in set (0.01 sec)
|
mysql> select * from t1;
Empty set (0.00 sec)
|
|
mysql> update t1 set c2=100 where c1=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 100 |
+----+------+
1 row in set (0.00 sec)
|
|
該實驗說明:本事務中進行修改的數據,即使沒有提交,在本事務中的后面也可以讀取到。update 語句因為進行的是“當前讀”,所以它可以修改成功。
2)對於RC隔離就簡單多了:
With READ COMMITTED
isolation level, each consistent read within a transaction sets and reads its own fresh snapshot.
事務中每一次讀取都是以當前的時間點作為判斷是否提交的實際點,也即是 reads its own fresh snapshot.
RC是語句級多版本(事務的多條只讀語句,創建不同的ReadView,代價更高),RR是事務級多版本(一個ReadView);
3. MySQL 中事務開始的時間
一般我們會認為 begin/start transaction 是事務開始的時間點,也就是一旦我們執行了 start transaction,就認為事務已經開始了,其實這是錯誤的。上面的實驗也說明了這一點。事務開始的真正的時間點(LSN),是 start transaction 之后執行的第一條語句,不管是什么語句,不管成功與否。
但是如果你想要達到將 start transaction 作為事務開始的時間點,那么我們必須使用:
START TRANSACTION WITH consistent snapshot
它的含義是:執行 start transaction 同時建立本事務一致性讀的 snapshot . 而不是等到執行第一條語句時,才開始事務,並且建立一致性讀的 snapshot .
The WITH CONSISTENT SNAPSHOT
modifier starts a consistent read for storage engines that are capable of it. This applies only to InnoDB
. The effect is the same as issuing a START TRANSACTION
followed by a SELECT
from any InnoDB
table. See Section 14.2.2.2, “Consistent Nonlocking Reads”. The WITH CONSISTENT SNAPSHOT
modifier does not change the current transaction isolation level, so it provides a consistent snapshot only if the current isolation level is one that permits a consistent read. The only isolation level that permits a consistent read is REPEATABLE READ
. For all other isolation levels, the WITH CONSISTENT SNAPSHOT
clause is ignored. As of MySQL 5.7.2, a warning is generated when the WITH CONSISTENT SNAPSHOT
clause is ignored.
http://dev.mysql.com/doc/refman/5.6/en/commit.html
效果等價於: start transaction 之后,馬上執行一條 select 語句(此時會建立一致性讀的snapshot)。
If the transaction isolation level is REPEATABLE READ
(the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries. (RR隔離級別中的一致性讀的snapshot是第一條select語句執行時建立的,其實應該是第一條任何語句執行時建立的)
http://dev.mysql.com/doc/refman/5.6/en/innodb-consistent-read.html
我們在 mysqldump --single-transaction 中使用的就是該語句:
SET session TRANSACTION isolation LEVEL REPEATABLE read START TRANSACTION /*!40100 WITH consistent snapshot */
所以事務開始時間點,分為兩種情況:
1)START TRANSACTION 時,是第一條語句的執行時間點,就是事務開始的時間點,第一條select語句建立一致性讀的snapshot;
2)START TRANSACTION WITH consistent snapshot 時,則是立即建立本事務的一致性讀snapshot,當然也開始事務了;
實驗1:
session A
|
session B
|
mysql> set tx_isolation='repeatable-read';
|
mysql> set tx_isolation='repeatable-read';
|
|
mysql> select * from t1;
Empty set (0.01 sec)
|
mysql> start transaction;
|
|
|
mysql> insert into t1(c1,c2) values(1,1);
|
mysql> select * from t1;
+----+------+
| c1 | c2 |
+----+------+
| 1 | 1 |
+----+------+
1 row in set (0.00 sec)
|
|
mysql> set tx_isolation='repeatable-read';
|
mysql> set tx_isolation='repeatable-read';
|
|
mysql> select * from t1;
Empty set (0.01 sec)
|
mysql> start transaction with consistent snapshot;
|
|
|
mysql> insert into t1(c1,c2) values(1,1);
|
mysql> select * from t1;
Empty set (0.00 sec)
|
|
3. Oracle中的一致性讀
Oracle讀一致性是指一個查詢所獲得的數據來自同一時間點。
Oracle讀一致性分為語句級讀一致性和事務級讀一致性。
語句級讀一致性:Oracle強制實現語句級讀一致性。一個查詢語句只讀取語句開始之前提交的數據。
事務級讀一致性:隔離級別為SERIALIZABLE和read only的事務才支持事務級讀一致性。事務中的所有查詢語句只讀取 事務開始之前提交的數據。
Oracle只實現了RC和serializable,沒有實現Read uncommitted 和 RR。其實Oracle的serializable級別才實現了可重復讀。
4. 當前讀(current read) 和 一致性讀
一致性讀是指普通的select語句,不帶 for update, in share mode 等等子句。使用的是undo中的提交的數據,不需要使用鎖(MDL除外)。而當前讀,是指update, delete, select for update, select in share mode等等語句進行的讀,它們讀取的是數據庫中的最新的數據,並且會鎖住讀取的行和gap(RR隔離時)。如果不能獲得鎖,則會一直等待,直到獲得或者超時。RC隔離級別的當前讀沒有gap lock,RC的update語句進行的是“半一致性讀”,和RR的update語句的當前讀不一樣。
5. 一致性讀與 mysqldump --single-transaction
我們知道 mysqldump --single-transaction的原理是:設置事務為RR模式,然后利用事務的特性,來獲得一致性的數據,但是:
--single-transaction
Creates a consistent snapshot by dumping all tables in a
single transaction. Works ONLY for tables stored in
storage engines which support multiversioning (currently
only InnoDB does); the dump is NOT guaranteed to be
consistent for other storage engines. While a
--single-transaction dump is in process, to ensure a
valid dump file (correct table contents and binary log
position), no other connection should use the following
statements: ALTER TABLE, DROP TABLE, RENAME TABLE,
TRUNCATE TABLE, as consistent snapshot is not isolated
from them. Option automatically turns off --lock-tables.
在mysqldump運行期間,不能執行 alter table, drop table, rename table, truncate table 等等的DDL語句,因為一致性讀和這些語句時無法隔離的。
那么在mysqldump --single-transaction 執行期間,執行了上面那些DDL,會發生什么呢?
mysqldump --single-transaction 的執行過程是:設置RR,然后開始事務,對應了一個LSN,然后對所有選中的表,一個一個的執行下面的過程:
save point sp; --> select * from t1 --> rollback to sp;
save point sp; --> select * from t2 --> rollback to sp;
... ...
1> 那么如果對t2表的DDL發生在 save point sp 之前,那么當mysqldump處理到 t2 表時,mysqldump 會立馬報錯:表結構已經改變......
2> 如果對t2表的DDL發生在 save point sp 之后,rollback to sp 之前,那么要么DDL被阻塞,要么mysqldump被阻塞,具體誰被阻塞,看誰先執行了。
被阻塞額原因是:DDL需要t2表的 MDL 的互斥鎖,而select * from t1 需要MDL的共享鎖,所以阻塞發生。
3> 如果對t2表的DDL發生在 rollback to sp 之后,那么因為對 t2 表的dump已經完成,不會發生錯誤或者阻塞。
那么為什么: 對t2表的DDL發生在 save point sp 之前,那么當mysqldump開始處理 t2 表時,mysqldump 立馬報錯呢?
其原因就是 一致性讀的胳膊拗不過DDL的大腿:
Consistent read does not work over certain DDL statements:(一致性讀的胳膊拗不過DDL的大腿)
-
Consistent read does not work over
DROP TABLE
, because MySQL cannot use a table that has been dropped andInnoDB
destroys the table. -
Consistent read does not work over
ALTER TABLE
, because that statement makes a temporary copy of the original table and deletes the original table when the temporary copy is built. When you reissue a consistent read within a transaction, rows in the new table are not visible because those rows did not exist when the transaction's snapshot was taken. In this case, the transaction returns an error as of MySQL 5.6.6:ER_TABLE_DEF_CHANGED
, “Table definition has changed, please retry transaction”.
原因:ALTER TABLE, DROP TABLE, RENAME TABLE, TRUNCATE TABLE 這些DDL語句的執行,會導致無法使用undo構造出正確的一致性讀,一致性讀和它們是無法隔離的。
1. 數據庫事務ACID特性
數據庫事務的4個特性:
原子性(Atomic): 事務中的多個操作,不可分割,要么都成功,要么都失敗; All or Nothing.
一致性(Consistency): 事務操作之后, 數據庫所處的狀態和業務規則是一致的; 比如a,b賬戶相互轉賬之后,總金額不變;
隔離性(Isolation): 多個事務之間就像是串行執行一樣,不相互影響;
持久性(Durability): 事務提交后被持久化到永久存儲.
2. 隔離性
其中 隔離性 分為了四種:
READ UNCOMMITTED:可以讀取未提交的數據,未提交的數據稱為臟數據,所以又稱臟讀。此時:幻讀,不可重復讀和臟讀均允許;
READ COMMITTED:只能讀取已經提交的數據;此時:允許幻讀和不可重復讀,但不允許臟讀,所以RC隔離級別要求解決臟讀;
REPEATABLE READ:同一個事務中多次執行同一個select,讀取到的數據沒有發生改變;此時:允許幻讀,但不允許不可重復讀和臟讀,所以RR隔離級別要求解決不可重復讀;
SERIALIZABLE: 幻讀,不可重復讀和臟讀都不允許,所以serializable要求解決幻讀;
3. 幾個概念
臟讀:可以讀取未提交的數據。RC 要求解決臟讀;
不可重復讀:同一個事務中多次執行同一個select, 讀取到的數據發生了改變(被其它事務update並且提交);
可重復讀:同一個事務中多次執行同一個select, 讀取到的數據沒有發生改變(一般使用MVCC實現);RR各級級別要求達到可重復讀的標准;
幻讀:同一個事務中多次執行同一個select, 讀取到的數據行發生改變。也就是行數減少或者增加了(被其它事務delete/insert並且提交)。SERIALIZABLE要求解決幻讀問題;
這里一定要區分 不可重復讀 和 幻讀:
不可重復讀的重點是修改:
同樣的條件的select, 你讀取過的數據, 再次讀取出來發現值不一樣了
幻讀的重點在於新增或者刪除:
同樣的條件的select, 第1次和第2次讀出來的記錄數不一樣
從結果上來看, 兩者都是為多次讀取的結果不一致。但如果你從實現的角度來看, 它們的區別就比較大:
對於前者, 在RC下只需要鎖住滿足條件的記錄,就可以避免被其它事務修改,也就是 select for update, select in share mode; RR隔離下使用MVCC實現可重復讀;
對於后者, 要鎖住滿足條件的記錄及所有這些記錄之間的gap,也就是需要 gap lock。
而ANSI SQL標准沒有從隔離程度進行定義,而是定義了事務的隔離級別,同時定義了不同事務隔離級別解決的三大並發問題:
Isolation Level |
Dirty Read |
Unrepeatable Read |
Phantom Read |
Read UNCOMMITTED |
YES |
YES |
YES |
READ COMMITTED |
NO |
YES |
YES |
READ REPEATABLE |
NO |
NO |
YES |
SERIALIZABLE |
NO |
NO |
NO |
參見:你真的明白事務的隔離性嗎? (姜承堯)
4. 數據庫的默認隔離級別
除了MySQL默認采用RR隔離級別之外,其它幾大數據庫都是采用RC隔離級別。
但是他們的實現也是極其不一樣的。Oracle僅僅實現了RC 和 SERIALIZABLE隔離級別。默認采用RC隔離級別,解決了臟讀。但是允許不可重復讀和幻讀。其SERIALIZABLE則解決了臟讀、不可重復讀、幻讀。
MySQL的實現:MySQL默認采用RR隔離級別,SQL標准是要求RR解決不可重復讀的問題,但是因為MySQL采用了gap lock,所以實際上MySQL的RR隔離級別也解決了幻讀的問題。那么MySQL的SERIALIZABLE是怎么回事呢?其實MySQL的SERIALIZABLE采用了經典的實現方式,對讀和寫都加鎖。
5. MySQL 中RC和RR隔離級別的區別
MySQL數據庫中默認隔離級別為RR,但是實際情況是使用RC 和 RR隔離級別的都不少。好像淘寶、網易都是使用的 RC 隔離級別。那么在MySQL中 RC 和 RR有什么區別呢?我們該如何選擇呢?為什么MySQL將RR作為默認的隔離級別呢?
5.1 RC 與 RR 在鎖方面的區別
1> 顯然 RR 支持 gap lock(next-key lock),而RC則沒有gap lock。因為MySQL的RR需要gap lock來解決幻讀問題。而RC隔離級別則是允許存在不可重復讀和幻讀的。所以RC的並發一般要好於RR;
2> RC 隔離級別,通過 where 條件過濾之后,不符合條件的記錄上的行鎖,會釋放掉(雖然這里破壞了“兩階段加鎖原則”);但是RR隔離級別,即使不符合where條件的記錄,也不會是否行鎖和gap lock;所以從鎖方面來看,RC的並發應該要好於RR;另外 insert into t select ... from s where 語句在s表上的鎖也是不一樣的,參見下面的例子2;
例子1:
下面是來自 itpub 的一個例子:http://www.itpub.net/thread-1941624-1-1.html
MySQL5.6, 隔離級別RR,autocommit=off;
表結構:
mysql> show create table t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `a` int(11) NOT NULL, `b` int(11) NOT NULL, `c` int(11) NOT NULL, `d` int(11) NOT NULL, `e` varchar(20) DEFAULT NULL, PRIMARY KEY (`a`), KEY `idx_t1_bcd` (`b`,`c`,`d`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 1 row in set (0.00 sec)
表數據:
mysql> select * from t1; +---+---+---+---+------+ | a | b | c | d | e | +---+---+---+---+------+ | 1 | 1 | 1 | 1 | a | | 2 | 2 | 2 | 2 | b | | 3 | 3 | 2 | 2 | c | | 4 | 3 | 1 | 1 | d | | 5 | 2 | 3 | 5 | e | | 6 | 6 | 4 | 4 | f | | 7 | 4 | 5 | 5 | g | | 8 | 8 | 8 | 8 | h | +---+---+---+---+------+ 8 rows in set (0.00 sec)
操作過程:
session 1:
delete from t1 where b>2 and b<5 and c=2;
執行計划如下:
mysql> explain select * from t1 where b>2 and b<5 and c=2\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 type: range possible_keys: idx_t1_bcd key: idx_t1_bcd key_len: 4 ref: NULL rows: 2 Extra: Using index condition 1 row in set (0.00 sec)
session 2:
delete from t1 where a=4
結果 session 2 被鎖住。
session 3:
mysql> select * from information_schema.innodb_locks;
+---------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+ | lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data | +---------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+ | 38777:390:3:5 | 38777 | X | RECORD | `test`.`t1` | PRIMARY | 390 | 3 | 5 | 4 | | 38771:390:3:5 | 38771 | X | RECORD | `test`.`t1` | PRIMARY | 390 | 3 | 5 | 4 | +---------------+-------------+-----------+-----------+-------------+------------+------------+-----------+----------+-----------+
根據鎖及ICP的知識,此時加鎖的情況應該是在索引 idx_t1_bcd 上的b>2 and b<5之間加gap lock, idx_t1_bcd 上的c=2 加 X鎖主鍵 a=3 加 x 鎖。
應該a=4上是沒有加X鎖的,可以進行刪除與更改。
但是從session3上的結果來,此時a=4上被加上了X鎖。
求大牛解惑,謝謝。
-------
要理解這里為什么 a=4 被鎖住了,需要理解 gap lock,鎖處理 RR 隔離級別和RC隔離級別的區別等等。
這里的原因如下:
很簡單,我們注意到:key_len: 4 和 Extra: Using index condition
這說明了,僅僅使用了索引 idx_t1_bcd 中的 b 一列,沒有使用到 c 這一列。c 這一列是在ICP時進行過濾的。所以:
delete from t1 where b>2 and b<5 and c=2 其實鎖定的行有:
mysql> select * from t1 where b>2 and b<=6; +---+---+---+---+------+ | a | b | c | d | e | +---+---+---+---+------+ | 3 | 3 | 2 | 2 | c | | 4 | 3 | 1 | 1 | d | | 6 | 6 | 4 | 4 | f | | 7 | 4 | 5 | 5 | g | +---+---+---+---+------+ 4 rows in set (0.00 sec)
所以顯然 delete from t1 where a=4 就被阻塞了。那么為什么 delete from t1 where a=6 也會被阻塞呢???
這里 b<=6 的原因是,b 列中沒有等於 5 的記錄,所以 and b<5 實現為鎖定 b<=6 的所有索引記錄,這里有等於號的原因是,如果我們不鎖定 =6 的索引記錄,那么怎么實現鎖定 <5 的gap 呢?也就是說鎖定 b=6 的索引記錄,是為了實現鎖定 b< 5 的gap。也就是不能刪除 b=6 記錄的原因。
而這里 b >2 沒有加等於號(b>=2) 的原因,是因為 b>2的這個gap 是由 b=3這個索引記錄(的gap)來實現的,不是由 b=2索引記錄(的gap) 來實現的,b=2的索引記錄的gap lock只能實現鎖定<2的gap,b>2的gap鎖定功能,需要由 b=3的索引記錄對應的gap來實現(b>2,b<3的gap)。
所以我們在session2中可以刪除:a=1,2,5,8的記錄,但是不能刪除 a=6(因為該行的b=6)的記錄。
如果我們使用 RC 隔離級別時,則不會發生阻塞,其原因就是:
RC和RR隔離級別中的鎖處理不一樣,RC隔離級別時,在使用c列進行ICP where條件過濾時,對於不符合條件的記錄,鎖會釋放掉,而RR隔離級別時,即使不符合條件的記錄,鎖也不會釋放(雖然違反了“2階段鎖”原則)。所以RC隔離級別時session 2不會被阻塞。
Gap lock: This is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.
例子2:insert into t select ... from s where 在RC 和 RR隔離級別下的加鎖過程
下面是官方文檔中的說明:http://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html
INSERT INTO T SELECT ... FROM S WHERE ... sets an exclusive index record lock (without a gap lock) on each row inserted into T. If the transaction isolation level is READ COMMITTED, or innodb_locks_unsafe_for_binlog is enabled and the transaction isolation level is not SERIALIZABLE, InnoDB does the search on S as a consistent read (no locks). Otherwise, InnoDB sets shared next-key locks on rows from S. InnoDB has to set locks in the latter case: In roll-forward recovery from a backup, every SQL statement must be executed in exactly the same way it was done originally.
CREATE TABLE ... SELECT ... performs the SELECT with shared next-key locks or as a consistent read, as for INSERT ... SELECT.
When a SELECT is used in the constructs REPLACE INTO t SELECT ... FROM s WHERE ... or UPDATE t ... WHERE col IN (SELECT ... FROM s ...), InnoDB sets shared next-key locks on rows from table s.
insert inot t select ... from s where ... 語句和 create table ... select ... from s where 加鎖過程是相似的(RC 和 RR 加鎖不一樣):
1> RC 隔離級別時和 RR隔離級別但是設置innodb_locks_unsafe_for_binlog=1 時,select ... from s where 對 s 表進行的是一致性讀,所以是無需加鎖的;
2> 如果是RR隔離級別(默認innodb_locks_unsafe_for_binlog=0),或者是 serializable隔離級別,那么對 s 表上的每一行都要加上 shared next-key lock.
這個區別是一個很大的不同,下面是生成中的一個 insert into t select ... from s where 導致的系統宕機的案例:
一程序猿執行一個分表操作:
insert into tb_async_src_acct_201508 select * from tb_async_src_acct
where src_status=3 and create_time>='2015-08-01 00:00:00' and create_time <= '2015-08-31 23:59:59';
表 tb_async_src_acct有4000W數據。分表的目的是想提升下性能。結果一執行該語句,該條SQL被卡住,然后所有向 tb_async_src_acct的寫操作,要么是 get lock fail, 要么是 lost connection,全部卡住,然后主庫就宕機了。
顯然這里的原因,就是不知道默認RR隔離級別中 insert into t select ... from s where 語句的在 s 表上的加鎖過程,該語句一執行,所有符合 where 條件的 s 表中的行記錄都會加上 shared next-key lock(如果沒有使用到索引,還會鎖住表中所有行),在整個事務過程中一直持有,因為表 tb_async_src_acct 數據很多,所以運行過程是很長的,所以加鎖過程也是很長,所以其它所有的對 tb_async_src_acct 的insert, delete, update, DDL 都會被阻塞掉,這樣被阻塞的事務就越來越多,而事務也會申請其它的表中的行鎖,結果就是系統中被卡住的事務越來越多,系統自然就宕機了。
5.2 RC 與 RR 在復制方面的區別
1> RC 隔離級別不支持 statement 格式的bin log,因為該格式的復制,會導致主從數據的不一致;只能使用 mixed 或者 row 格式的bin log; 這也是為什么MySQL默認使用RR隔離級別的原因。復制時,我們最好使用:binlog_format=row
具體參見:
http://blog.itpub.net/29254281/viewspace-1081678/
http://www.cnblogs.com/vinchen/archive/2012/11/19/2777919.html
2> MySQL5.6 的早期版本,RC隔離級別是可以設置成使用statement格式的bin log,后期版本則會直接報錯;
5.3 RC 與 RR 在一致性讀方面的區別
簡單而且,RC隔離級別時,事務中的每一條select語句會讀取到他自己執行時已經提交了的記錄,也就是每一條select都有自己的一致性讀ReadView; 而RR隔離級別時,事務中的一致性讀的ReadView是以第一條select語句的運行時,作為本事務的一致性讀snapshot的建立時間點的。只能讀取該時間點之前已經提交的數據。
具體可以參加:MySQL 一致性讀 深入研究
5.4 RC 支持半一致性讀,RR不支持
RC隔離級別下的update語句,使用的是半一致性讀(semi consistent);而RR隔離級別的update語句使用的是當前讀;當前讀會發生鎖的阻塞。
1> 半一致性讀:
A type of read operation used for UPDATE statements, that is a combination of read committed and consistent read. When an UPDATE statement examines a row that is already locked, InnoDB returns the latest committed version to MySQL so that MySQL can determine whether the row matches the WHERE condition of the UPDATE. If the row matches (must be updated), MySQL reads the row again, and this time InnoDB either locks it or waits for a lock on it. This type of read operation can only happen when the transaction has the read committed isolation level, or when the innodb_locks_unsafe_for_binlog option is enabled.
簡單來說,semi-consistent read是read committed與consistent read兩者的結合。一個update語句,如果讀到一行已經加鎖的記錄,此時InnoDB返回記錄最近提交的版本,由MySQL上層判斷此版本是否滿足 update的where條件。若滿足(需要更新),則MySQL會重新發起一次讀操作,此時會讀取行的最新版本(並加鎖)。semi-consistent read只會發生在read committed隔離級別下,或者是參數innodb_locks_unsafe_for_binlog被設置為true(該參數即將被廢棄)。
對比RR隔離級別,update語句會使用當前讀,如果一行被鎖定了,那么此時會被阻塞,發生鎖等待。而不會讀取最新的提交版本,然后來判斷是否符合where條件。
半一致性讀的優點:
減少了update語句時行鎖的沖突;對於不滿足update更新條件的記錄,可以提前放鎖,減少並發沖突的概率。
具體可以參見:http://hedengcheng.com/?p=220
Oracle中的update好像有“重啟動”的概念。