一致性非鎖定讀與一致性鎖定讀
背景
innodb存儲引擎實現(xiàn)了兩種標準的行級鎖:S鎖和X鎖,S鎖被稱為共享鎖,允許事務讀一行數(shù)據(jù),X鎖被稱為排它鎖,允許事務刪除或更新一行數(shù)據(jù)。
一致性非鎖定讀指的是如果一條記錄被加了X鎖,其他事務還能讀取這條記錄。
一致性鎖定讀指的是一個事務可以通過SELECT語句給某條記錄加X鎖或者X鎖。
一個小栗子
我們假設有一個表和兩個事務,表名字為mytest,事務名字為t1和t2:
t1 | t2 | t3 | t4 |
---|---|---|---|
a | bb | bb | ccc |
t1和t2的執(zhí)行時序如下:
這里我先拋出兩個問題:
- 上面Mark A處顯然t1已經(jīng)給記錄加了X鎖,并且在事務內(nèi)修改了數(shù)據(jù),此時t2看到的數(shù)據(jù)是什么?
- 上面Mark B處事務t1已經(jīng)提交此時t2看到的數(shù)據(jù)是什么?
行多版本控制
行多版本將的是innodb為每個行記錄存儲了多個版本,記住,這里是多個版本不是兩個版本,在剛開始接觸多版本的時候,我的疑問是innodb對每個行要存儲多個版本是多么浪費存儲空間呀?然而進一步了解,原來所謂的多版本只是innodb聰明地撒了個謊,多個版本是通過undo日志實現(xiàn)的,這里可以理解為既然undo日志包括了所有用來恢復歷史版本數(shù)據(jù)的信息,那么我們只要將“不同版本”指針指向不同時間節(jié)點的undo日志即可,這樣讀取的時候通過對不同時間節(jié)點的undo日志進行恢復從而得到不同的版本數(shù)據(jù)。同時對于undo日志的讀取是不需要加鎖的,因此這極大地提高了數(shù)據(jù)庫的并發(fā)性。
這里回答了上面的***個問題:t2此時看到的應該是歷史版本的數(shù)據(jù),也就是t1修改之前的數(shù)據(jù),如下:
- mysql> select * from mytest where t2='bb';
- +------+------+------+------+
- | t1 | t2 | t3 | t4 |
- +------+------+------+------+
- | a | bb | bb | ccc |
- +------+------+------+------+
- 1 row in set (0.00 sec)
- READ COMMITTED 與 REPEATABLE READ
這里復習一下SQL標準定義的四個隔離級別分別為:
- READ UNCOMMITTED
- READ COMMITTED
- REPEATABLE READ
- SERIALIZABLE
innodb默認的隔離級別為REPEATABLE READ且使用next key locking技術解決的幻讀的問題,READ COMMITTED值的是一個事務可以讀取其他事務已經(jīng)提交的數(shù)據(jù),而REPEATABLE READ要求一個事務在事務內(nèi)可以重復讀取一條記錄,因此上面第二個問題的答案是此時t2看到的是什么跟此時數(shù)據(jù)庫的隔離級別有關系,比如此時的隔離級別為:
- mysql> select @@tx_isolation;
- +-----------------+
- | @@tx_isolation |
- +-----------------+
- | REPEATABLE-READ |
- +-----------------+
- 1 row in set (0.00 sec)
因此t2在Mark B的地方看到的應該是老數(shù)據(jù):
- mysql> select * from mytest where t2='bb';
- +------+------+------+------+
- | t1 | t2 | t3 | t4 |
- +------+------+------+------+
- | a | bb | bb | ccc |
- +------+------+------+------+
- 1 row in set (0.00 sec)
- mysql>
如果此時的事務隔離級別為READ COMMITTED,則t2在Mark B處看到的應該是新數(shù)據(jù)。
一致性鎖定讀
一致性非鎖定讀的情況下即使記錄因為UPDATE而被加了X鎖,其他事務仍然能夠讀取記錄,不會阻塞。而如果一個事務希望在讀取的時候就把記錄鎖住,不允許其他事務進行修改應該怎么做呢?那就是SELECT … FOR UPDATE,SELECT … FOR UPDATE顯式地給一條記錄加X鎖,因此其他事務不能獲取該記錄的任何鎖。我們也可以使用SELECT … LOCK IN SHARE MODE來給記錄顯式地加S鎖,因此其他事務能夠獲取該記錄的S鎖而不能獲取該記錄的X鎖,這兩種語句都是有特定的應用場景的。
總結
再總結一下,一致性非鎖定讀講的是一條記錄被加了X鎖其他事務仍然可以讀而不被阻塞,是通過innodb的行多版本實現(xiàn)的,行多版本并不是實際存儲多個版本記錄而是通過undo實現(xiàn)。一致性鎖定讀講的是我可以通過SELECT語句顯式地給一條記錄加X鎖從而保證特定應用場景下的數(shù)據(jù)一致性。