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