在RC(Read Committed)和RR(Repeatable Read)兩種事務(wù)隔離級別下驯绎,InnoDB存在兩種數(shù)據(jù)讀取方式:
快照讀(Snapshot Read)
故名思意完慧,快照讀讀取的都是快照數(shù)據(jù),快照怎么來条篷,在InnoDB引擎下是基于undo log骗随,那undo log又是什么?舉例說明赴叹,假設(shè)有這樣一個表:
-- 表結(jié)構(gòu)
CREATE TABLE `innodb_test` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL DEFAULT '0',
`age` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
INDEX `idx_age` (`age`)
)
ENGINE=InnoDB;
-- 初始數(shù)據(jù)
INSERT INTO `innodb_test` (`id`, `name`, `age`) VALUES (1, '貂蟬', 100),(2, '莊周', 120),(3, '項羽', 130);
-
id=1的初始數(shù)據(jù)行
-
事務(wù)A執(zhí)行如下語句
UPDATE innodb_test SET name='嬴政', age=90 WHERE id=1;
此時innodb會做如下操作:
- 把該行修改前的值Copy到undo log(Copy on write)鸿染;
-
修改當前行的值,填寫事務(wù)編號乞巧,使回滾指針指向undo log中的修改前的行涨椒。
-
事務(wù)B執(zhí)行如下語句
UPDATE innodb_test SET name='甄姬', age=91 WHERE id=1;
此時undo log中有2條記錄,它們通過回滾指針相連绽媒。
undo log的存在解決了兩個問題蚕冬,一是數(shù)據(jù)回滾,二是實現(xiàn)了MVCC (Multi-Version Concurrency Control) 是辕,快照讀讀取的就是undo log中的數(shù)據(jù)囤热,所以這種讀取是不需要加鎖的,避免了讀寫沖突获三。常見的快照讀語句就是最常見的SELECT旁蔼,比如:
SELECT * FROM innodb_test WHERE id=1;
快照讀在RC和RR隔離級別下的表現(xiàn)卻是不一樣的锨苏,為了方便說明,現(xiàn)在將數(shù)據(jù)還原到初始數(shù)據(jù)棺聊,然后按照下表的順序操作伞租。
# | 事務(wù)1 | 事務(wù)2 |
---|---|---|
1 | begin; |
begin; |
2 | SELECT * FROM innodb_test WHERE id=1;// 輸出1-貂蟬-100 |
|
3 | UPDATE innodb_test SET name='嬴政', age=90 WHERE id=1; |
|
4 | commit; |
|
5 | SELECT * FROM innodb_test WHERE id=1;// 輸出?限佩?葵诈? |
-
RC
輸出的是最新提交的結(jié)果(1-嬴政-90),RC級別的快照讀遵循以下規(guī)則:- 優(yōu)先讀取當前事務(wù)修改的數(shù)據(jù)祟同,自己修改的作喘,當然可以讀到了;
- 其次讀取最新已提交數(shù)據(jù)耐亏。
會出現(xiàn)前后讀取結(jié)果不一樣的情況徊都,但讀取的是最新數(shù)據(jù)。
-
RR
輸出結(jié)果和第一次查詢是一樣的(1-貂蟬-100)广辰,RR級別的快照讀遵循以下規(guī)則:- 優(yōu)先讀取當前事務(wù)修改的數(shù)據(jù),和RC一樣主之;
- 其次讀取小于當前事務(wù)id的最新一條已提交數(shù)據(jù)择吊,此時數(shù)據(jù)版本已經(jīng)確定了,后面的快照讀取始終讀取這個版本槽奕。
通過這樣的機制几睛,保證了快照讀的可重復(fù)讀,但讀取到的數(shù)據(jù)很可能已經(jīng)過期了粤攒。
當前讀(Current Read)
而當前讀所森,讀取的是最新已提交數(shù)據(jù),并且都會加行鎖夯接,如下語句都會產(chǎn)生當前讀:
SELECT balabala LOCK IN SHARE MODE;
SELECT balabala FOR UPDATE;
INSERT balabala;
UPDATE balabala;
DELETE balabala;
當前讀需要保證其他并發(fā)事務(wù)不能修改當前記錄焕济,對讀取記錄加鎖。其中盔几,第一條語句晴弃,對讀取記錄加S鎖(共享鎖),其他的操作逊拍,都加的是X鎖(排它鎖)上鞠。當前讀在RC和RR隔離級別下的表現(xiàn)也是不一樣的,為了方便說明芯丧,現(xiàn)在將數(shù)據(jù)還原到初始數(shù)據(jù)芍阎,然后按照下表的順序操作。
# | 事務(wù)1 | 事務(wù)2 |
---|---|---|
1 | begin; |
begin; |
2 | SELECT * FROM innodb_test WHERE age=120 FOR UPDATE; |
|
3 | insert into innodb_test(name, age) values('嬴政', 120);// 此時會發(fā)生什么缨恒? |
RC
成功執(zhí)行谴咸,但會造成事務(wù)1的幻讀轮听,前后兩次讀取結(jié)果不一樣。RR
會鎖等待寿冕,在RR隔離級別下蕊程,事務(wù)1的sql不僅會對該記錄加X鎖,還會對上下兩個數(shù)據(jù)間隙加間隙鎖驼唱,以此確保在數(shù)據(jù)讀取期間藻茂,其它事物不會在該間隙內(nèi)增加數(shù)據(jù),從而保證可重復(fù)讀玫恳。
總結(jié)
RR隔離級別下辨赐,快照讀通過undo log來保證可重復(fù)讀,當前讀通過X(S)鎖+GAP鎖來保證可重復(fù)讀京办,但顯然快照讀和當前讀之間無法保證可重復(fù)讀掀序。本文對可重復(fù)讀的實現(xiàn)機制做了闡述,關(guān)于undo log惭婿、鎖等知識僅僅從簡描述不恭,后面有時間再詳細寫一下。
版權(quán)聲明
本博客所有的原創(chuàng)文章财饥,作者皆保留版權(quán)换吧。轉(zhuǎn)載必須包含本聲明,保持本文完整钥星,并以超鏈接形式注明作者高爽和本文原始地址:http://www.reibang.com/p/17967b72139a沾瓦。