https://www.runoob.com/mysql/mysql-transaction.html
1.事務(wù)簡介
(1)在 MySQL 中只有使用了 Innodb 數(shù)據(jù)庫引擎的數(shù)據(jù)庫或表才支持事務(wù)。
(2)事務(wù)處理可以用來維護(hù)數(shù)據(jù)庫的完整性嗡贺,保證成批的 SQL 語句要么全部執(zhí)行机断,要么全部不執(zhí)行缭受。
(3)事務(wù)用來管理 insert,update,delete 語句艾帐。
2.事務(wù)四大特征
一般來說惰瓜,事務(wù)是必須滿足4個條件(ACID)::原子性(Atomicity梅桩,或稱不可分割性)塔鳍、一致性(Consistency)伯铣、隔離性(Isolation,又稱獨(dú)立性)轮纫、持久性(Durability)腔寡。
原子性:一個事務(wù)(transaction)中的所有操作,要么全部完成掌唾,要么全部不完成放前,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤糯彬,會被回滾(Rollback)到事務(wù)開始前的狀態(tài)凭语,就像這個事務(wù)從來沒有執(zhí)行過一樣。
一致性:在事務(wù)開始之前和事務(wù)結(jié)束以后撩扒,數(shù)據(jù)庫的完整性沒有被破壞似扔。這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度搓谆、串聯(lián)性以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作炒辉。(比如:A向B轉(zhuǎn)賬,不可能A扣了錢泉手,B卻沒有收到)
隔離性:數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時對其數(shù)據(jù)進(jìn)行讀寫和修改的能力黔寇,隔離性可以防止多個事務(wù)并發(fā)執(zhí)行時由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級別斩萌,包括讀未提交(Read uncommitted)缝裤、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)颊郎。(比u人:A正在從一張銀行卡里面取錢憋飞,在A取錢的過程中,B不能向這張銀行卡打錢)
持久性:事務(wù)處理結(jié)束后袭艺,對數(shù)據(jù)的修改就是永久的搀崭,即便系統(tǒng)故障也不會丟失叨粘。
3.事務(wù)提交猾编、回滾
mysql> start transaction;#手動開啟事務(wù)
mysql> insert into t_user(name) values('pp');
mysql> commit;#commit之后即可改變底層數(shù)據(jù)庫數(shù)據(jù)
mysql> select * from t_user;
+----+------+
| id | name |
+----+------+
| 1 | jay |
| 2 | man |
| 3 | pp |
+----+------+
3 rows in set (0.00 sec)
mysql> start transaction;
mysql> insert into t_user(name) values('yy');
mysql> rollback;
mysql> select * from t_user;
+----+------+
| id | name |
+----+------+
| 1 | jay |
| 2 | man |
| 3 | pp |
+----+------+
3 rows in set (0.00 sec)
4.事務(wù)特性--隔離性
4.1 隔離性有隔離級別(4個)
(1)讀未提交:read uncommitted
(2)讀已提交:read committed
(3)可重復(fù)讀:repeatable read
(4)串行化:serializable
數(shù)據(jù)庫事務(wù)隔離級別-- 臟讀瘤睹、幻讀、不可重復(fù)讀(清晰解釋)
臟讀 | 不可重復(fù)讀 | 幻讀 | |
---|---|---|---|
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
4.2 讀未提交
- 事物A和事物B答倡,事物A未提交的數(shù)據(jù)轰传,事物B可以讀取到
- 這里讀取到的數(shù)據(jù)叫做“臟數(shù)據(jù)”
- 這種隔離級別最低,這種級別一般是在理論上存在瘪撇,數(shù)據(jù)庫隔離級別一般都高于該級別
公司發(fā)工資了获茬,領(lǐng)導(dǎo)把5000元打到singo的賬號上,但是該事務(wù)并未提交倔既,而singo正好去查看賬戶恕曲,發(fā)現(xiàn)工資已經(jīng)到賬,是5000元整渤涌,非常高 興佩谣。可是不幸的是实蓬,領(lǐng)導(dǎo)發(fā)現(xiàn)發(fā)給singo的工資金額不對茸俭,是2000元,于是迅速回滾了事務(wù)安皱,修改金額后调鬓,將事務(wù)提交,最后singo實(shí)際的工資只有 2000元酌伊,singo空歡喜一場腾窝。
出現(xiàn)上述情況,即我們所說的臟讀 居砖,兩個并發(fā)的事務(wù)燕锥,“事務(wù)A:領(lǐng)導(dǎo)給singo發(fā)工資”苟穆、“事務(wù)B:singo查詢工資賬戶”游两,事務(wù)B讀取了事務(wù)A尚未提交的數(shù)據(jù)。
4.3 讀已提交
- 事物A和事物B葵孤,事物A提交的數(shù)據(jù)鼻由,事物B才能讀取到
- 這種隔離級別高于讀未提交
- 換句話說暇榴,對方事物提交之后的數(shù)據(jù),我當(dāng)前事物才能讀取到
- 這種級別可以避免“臟數(shù)據(jù)”
- 這種隔離級別會導(dǎo)致“不可重復(fù)讀取”
- Oracle默認(rèn)隔離級別
singo拿著工資卡去消費(fèi)蕉世,系統(tǒng)讀取到卡里確實(shí)有2000元蔼紧,而此時她的老婆也正好在網(wǎng)上轉(zhuǎn)賬,把singo工資卡的2000元轉(zhuǎn)到另一賬戶狠轻,并在 singo之前提交了事務(wù)奸例,當(dāng)singo扣款時,系統(tǒng)檢查到singo的工資卡已經(jīng)沒有錢,扣款失敗查吊,singo十分納悶谐区,明明卡里有錢,為何......
出現(xiàn)上述情況逻卖,即我們所說的不可重復(fù)讀 宋列,兩個并發(fā)的事務(wù),“事務(wù)A:singo消費(fèi)”评也、“事務(wù)B:singo的老婆網(wǎng)上轉(zhuǎn)賬”炼杖,事務(wù)A事先讀取了數(shù)據(jù),事務(wù)B緊接了更新了數(shù)據(jù)盗迟,并提交了事務(wù)坤邪,而事務(wù)A再次讀取該數(shù)據(jù)時,數(shù)據(jù)已經(jīng)發(fā)生了改變罚缕。
當(dāng)隔離級別設(shè)置為Read committed 時罩扇,避免了臟讀,但是可能會造成不可重復(fù)讀怕磨。
大多數(shù)數(shù)據(jù)庫的默認(rèn)級別就是Read committed喂饥,比如Sql Server , Oracle。如何解決不可重復(fù)讀這一問題肠鲫,請看下一個隔離級別员帮。
4.4 可重復(fù)讀
- 事務(wù)A和事務(wù)B,事務(wù)A提交之后的數(shù)據(jù)导饲,事務(wù)B讀取不到
- 事務(wù)B是可重復(fù)讀取數(shù)據(jù)
- 這種隔離級別高于讀已提交
- 換句話說捞高,對方提交之后的數(shù)據(jù),我還是讀取不到
- 這種隔離級別可以避免“不可重復(fù)讀取”渣锦,達(dá)到可重復(fù)讀取
- 比如1點(diǎn)和2點(diǎn)讀到數(shù)據(jù)是同一個
- MySQL默認(rèn)級別
- 雖然可以達(dá)到可重復(fù)讀取硝岗,但是會導(dǎo)致“幻像讀”
當(dāng)隔離級別設(shè)置為Repeatable read 時,可以避免不可重復(fù)讀袋毙。當(dāng)singo拿著工資卡去消費(fèi)時型檀,一旦系統(tǒng)開始讀取工資卡信息(即事務(wù)開始),singo的老婆就不可能對該記錄進(jìn)行修改听盖,也就是singo的老婆不能在此時轉(zhuǎn)賬胀溺。
雖然Repeatable read避免了不可重復(fù)讀,但還有可能出現(xiàn)幻讀 皆看。
singo的老婆工作在銀行部門仓坞,她時常通過銀行內(nèi)部系統(tǒng)查看singo的信用卡消費(fèi)記錄。有一天腰吟,她正在查詢到singo當(dāng)月信用卡的總消費(fèi)金額 (select sum(amount) from transaction where month = 本月)為80元无埃,而singo此時正好在外面胡吃海塞后在收銀臺買單,消費(fèi)1000元,即新增了一條1000元的消費(fèi)記錄(insert transaction ... )嫉称,并提交了事務(wù)侦镇,隨后singo的老婆將singo當(dāng)月信用卡消費(fèi)的明細(xì)打印到A4紙上,卻發(fā)現(xiàn)消費(fèi)總額為1080元澎埠,singo的老婆很詫異,以為出現(xiàn)了幻覺始藕,幻讀就這樣產(chǎn)生了蒲稳。
4.5 串行化
- 事務(wù)A和事務(wù)B,事務(wù)A在操作數(shù)據(jù)庫時伍派,事務(wù)B只能排隊(duì)等待
- 這種隔離級別很少使用江耀,吞吐量太低,用戶體驗(yàn)差
- 這種級別可以避免“幻像讀”诉植,每一次讀取的都是數(shù)據(jù)庫中真實(shí)存在數(shù)據(jù)祥国,事務(wù)A與事務(wù)B串行,而不并發(fā)
4.6 臟讀晾腔、幻讀舌稀、不可重復(fù)讀
1.臟讀:(讀取未提交數(shù)據(jù))
臟讀就是指當(dāng)一個事務(wù)正在訪問數(shù)據(jù),并且對數(shù)據(jù)進(jìn)行了修改灼擂,而這種修改還沒有提交到數(shù)據(jù)庫中壁查,這時,另外一個事務(wù)也訪問這個數(shù)據(jù)剔应,然后使用了這個數(shù)據(jù)睡腿。
時間順序 | 轉(zhuǎn)賬事務(wù) | 取款事務(wù) |
---|---|---|
1 | 開始事務(wù) | |
2 | 開始事務(wù) | |
3 | 查詢賬戶余額為2000元 | |
4 | 取款1000元,余額被更改為1000元(未提交) | |
5 | 查詢賬戶余額為1000元(產(chǎn)生臟數(shù)據(jù)) | |
6 | 取款操作發(fā)生未知錯誤峻贮,事務(wù)回滾席怪,余額變更為2000元 | |
7 | 轉(zhuǎn)入2000元,余額被更改為3000元(臟讀1000+2000) | |
8 | 提交事務(wù) | |
備注 | 按照正常邏輯此時賬戶應(yīng)該為4000元 |
2.不可重復(fù)讀:(前后多次讀取纤控,數(shù)據(jù)內(nèi)容不一致)
是指在一個事務(wù)內(nèi)挂捻,多次讀同一數(shù)據(jù)。在這個事務(wù)還沒有結(jié)束時船万,另外一個事務(wù)也訪問該同一數(shù)據(jù)细层。那么,在第一個事務(wù)中的兩次讀數(shù)據(jù)之間唬涧,由于第二個事務(wù)的修改疫赎,那么第一個事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的。這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的碎节,因此稱為是不可重復(fù)讀捧搞。(即不能讀到相同的數(shù)據(jù)內(nèi)容)
例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間胎撇,作者重寫了該文檔介粘。當(dāng)編輯人員第二次讀取文檔時,文檔已更改晚树。原始讀取不可重復(fù)姻采。如果只有在作者全部完成編寫后編輯人員才可以讀取文檔,則可以避免該問題爵憎。
時間順序 | 事務(wù)A | 事務(wù)B |
---|---|---|
1 | 開始事務(wù) | |
2 | 第一次查詢慨亲,小明的年齡為20歲 | |
3 | 開始事務(wù) | |
4 | 其他操作 | |
5 | 更改小明的年齡為30歲 | |
6 | 提交事務(wù) | |
7 | 第二次查詢,小明的年齡為30歲 | |
備注 | 按照正確邏輯宝鼓,事務(wù)A前后兩次讀取到的數(shù)據(jù)應(yīng)該一致 |
3.幻讀:(前后多次讀取刑棵,數(shù)據(jù)總量不一致)
事務(wù)在插入已經(jīng)檢查過不存在的記錄時,驚奇的發(fā)現(xiàn)這些數(shù)據(jù)已經(jīng)存在了愚铡,之前的檢測獲取到的數(shù)據(jù)如同鬼影一般蛉签。
- 例子1:
時間順序 | 事務(wù)A | 事務(wù)B |
---|---|---|
1 | 開始事務(wù) | |
2 | 第一次查詢,數(shù)據(jù)總量為100條 | |
3 | 開始事務(wù) | |
4 | 其他操作 | |
5 | 新增100條數(shù)據(jù) | |
6 | 提交事務(wù) | |
7 | 第二次查詢沥寥,數(shù)據(jù)總量為200條 | |
備注 | 按照正確邏輯碍舍,按照正確邏輯,事務(wù)A前后兩次讀取到的數(shù)據(jù)總量應(yīng)該一致 |
- 例子2:
在事務(wù)1中邑雅,查詢User表id為1的是用戶否存在乒验,如果不存在則插入一條id為1的數(shù)據(jù)。
select * from User where id = 1;
在事務(wù)1查詢結(jié)束后蒂阱,事務(wù)2往User表中插入了一條id為1的數(shù)據(jù)锻全。
insert into `User`(`id`, `name`) values (1, 'Joonwhee');
此時,由于事務(wù)1查詢到id為1的用戶不存在录煤,因此插入1條id為1的數(shù)據(jù)鳄厌。
insert into ` User`(`id`, `name`) values (1, 'Chillax');
但是由于事務(wù)2已經(jīng)插入了1條id為1的數(shù)據(jù),因此此時會報(bào)主鍵沖突妈踊,對于事務(wù)1 的業(yè)務(wù)來說是執(zhí)行失敗的了嚎,這里事務(wù)1 就是發(fā)生了幻讀,因?yàn)槭聞?wù)1讀取的數(shù)據(jù)狀態(tài)并不能支持他的下一步的業(yè)務(wù)廊营,見鬼了一樣歪泳。這里要靈活的理解讀取的意思,第一次select是讀取露筒,第二次的insert其實(shí)也屬于隱式的讀取呐伞,只不過是在mysql的機(jī)制中讀取的,插入數(shù)據(jù)也是要先讀取一下有沒有主鍵沖突才能決定是否執(zhí)行插入慎式。
- 例子3:
目前工資為1000的員工有10人伶氢。
事務(wù)1趟径,讀取所有工資為1000的員工,共讀取10條記錄 癣防。
con1 = getConnection();
Select * from employee where salary =1000;
這時另一個事務(wù)向employee表插入了一條員工記錄蜗巧,工資也為1000
con2 = getConnection();
Insert into employee(employeeName,salary) values("Lili",1000);
con2.commit();
事務(wù)1再次讀取所有工資為1000的員工,共讀取到了11條記錄蕾盯,這就產(chǎn)生了幻讀幕屹。
//con1
select * from employee where salary =1000;
不可重復(fù)讀和幻讀到底有什么區(qū)別呢?
(1)不可重復(fù)讀是讀取了其他事務(wù)更改的數(shù)據(jù)级遭,針對update操作
解決:使用行級鎖望拖,鎖定該行,事務(wù)A多次讀取操作完成后才釋放該鎖装畅,這個時候才允許其他事務(wù)更改剛才的數(shù)據(jù)靠娱。
(2)幻讀是讀取了其他事務(wù)新增的數(shù)據(jù)沧烈,針對insert與delete操作
解決:使用表級鎖掠兄,鎖定整張表,事務(wù)A多次讀取數(shù)據(jù)總量之后才釋放該鎖锌雀,這個時候才允許其他事務(wù)新增數(shù)據(jù)蚂夕。
幻讀和不可重復(fù)讀都是指的一個事務(wù)范圍內(nèi)的操作受到其他事務(wù)的影響了。只不過幻讀是重點(diǎn)在插入和刪除腋逆,不可重復(fù)讀重點(diǎn)在修改
5婿牍、事務(wù)實(shí)現(xiàn)的原理
https://juejin.im/post/5cb2e3b46fb9a0686e40c5cb
下面我首先講實(shí)現(xiàn)事務(wù)功能的三個技術(shù),分別是日志文件(redo log 和 undo log)惩歉,鎖技術(shù)以及MVCC等脂,然后再講事務(wù)的實(shí)現(xiàn)原理,包括原子性是怎么實(shí)現(xiàn)的撑蚌,隔離型是怎么實(shí)現(xiàn)的等等上遥。最后在做一個總結(jié),希望大家能夠耐心看完
- redo log與undo log介紹
- mysql鎖技術(shù)以及MVCC基礎(chǔ)
- 事務(wù)的實(shí)現(xiàn)原理
5.1 redo log 與 undo log介紹
5.1.1争涌、什么是redo log ?
redo log叫做重做日志粉楚,是用來實(shí)現(xiàn)事務(wù)的持久性。該日志文件由兩部分組成:重做日志緩沖(redo log buffer)以及重做日志文件(redo log),前者是在內(nèi)存中亮垫,后者在磁盤中模软。當(dāng)事務(wù)提交之后會把所有修改信息都會存到該日志中。
start transaction;
select balance from bank where name="zhangsan";
// 生成 重做日志 balance=600
update bank set balance = balance - 400;
// 生成 重做日志 amount=400
update finance set amount = amount + 400;
5.1.2饮潦、redo log作用是什么燃异?
mysql 為了提升性能不會把每次的修改都實(shí)時同步到磁盤,而是會先存到Boffer Pool(緩沖池)里頭继蜡,把這個當(dāng)作緩存來用特铝。然后使用后臺線程去做緩沖池和磁盤之間的同步暑中。
那么問題來了,如果還沒來的同步的時候宕機(jī)或斷電了怎么辦鲫剿?還沒來得及執(zhí)行上面圖中紅色的操作鳄逾。這樣會導(dǎo)致丟部分已提交事務(wù)的修改信息!
所以引入了redo log來記錄已成功提交事務(wù)的修改信息灵莲,并且會把redo log持久化到磁盤雕凹,系統(tǒng)重啟之后在讀取redo log恢復(fù)最新數(shù)據(jù)。
- 總結(jié):redo log是用來恢復(fù)數(shù)據(jù)的政冻,用于保障枚抵,已提交事務(wù)的持久化特性(記錄了已經(jīng)提交的操作)
5.1.3、什么是undo log明场?
undo log 叫做回滾日志汽摹,用于記錄數(shù)據(jù)被修改前的信息。他正好跟前面所說的重做日志所記錄的相反苦锨,重做日志記錄數(shù)據(jù)被修改后的信息逼泣。undo log主要記錄的是數(shù)據(jù)的邏輯變化,為了在發(fā)生錯誤時回滾之前的操作舟舒,需要將之前的操作都記錄下來拉庶,然后在發(fā)生錯誤時才可以回滾。
還用上面那兩張表
每次寫入數(shù)據(jù)或者修改數(shù)據(jù)之前都會把修改前的信息記錄到 undo log秃励。
5.1.4氏仗、undo log 有什么作用?
undo log 記錄事務(wù)修改之前版本的數(shù)據(jù)信息夺鲜,因此假如由于系統(tǒng)錯誤或者rollback操作而回滾的話可以根據(jù)undo log的信息來進(jìn)行回滾到?jīng)]被修改前的狀態(tài)皆尔。
- 總結(jié):undo log是用來回滾數(shù)據(jù)的用于保障,未提交事務(wù)的原子性
5.2 mysql鎖技術(shù)以及MVCC基礎(chǔ)
5.2.1 mysql鎖技術(shù)
當(dāng)有多個請求來讀取表中的數(shù)據(jù)時可以不采取任何操作币励,但是多個請求里有讀請求慷蠕,又有修改請求時必須有一種措施來進(jìn)行并發(fā)控制。不然很有可能會造成不一致榄审。
讀寫鎖
解決上述問題很簡單砌们,只需用兩種鎖的組合來對讀寫請求進(jìn)行控制即可,這兩種鎖被稱為:
共享鎖(shared lock),又叫做"讀鎖"
讀鎖是可以共享的搁进,或者說多個讀請求可以共享一把鎖讀數(shù)據(jù)浪感,不會造成阻塞。-
排他鎖(exclusive lock),又叫做"寫鎖"
寫鎖會排斥其他所有獲取鎖的請求饼问,一直阻塞影兽,直到寫入完成釋放鎖。
總結(jié):通過讀寫鎖莱革,可以做到讀讀可以并行峻堰,但是不能做到寫讀讹开,寫寫并行
5.2.2 MVCC基礎(chǔ)
MVCC介紹
MVCC (MultiVersion Concurrency Control) 叫做多版本并發(fā)控制。一般情況下捐名,事務(wù)性儲存引擎不是只使用表鎖旦万,行加鎖的處理數(shù)據(jù),而是結(jié)合了MVCC機(jī)制镶蹋,以處理更多的并發(fā)問題成艘。Mvcc處理高并發(fā)能力最強(qiáng),但系統(tǒng)開銷 比最大(較表鎖贺归、行級鎖)淆两,這是最求高并發(fā)付出的代價(jià)。
InnoDB的 MVCC 拂酣,是通過在每行記錄的后面保存兩個隱藏的列來實(shí)現(xiàn)的秋冰。這兩個列, 一個保存了行的創(chuàng)建時間婶熬,一個保存了行的過期時間剑勾, 當(dāng)然存儲的并不是實(shí)際的時間值,而是系統(tǒng)版本號尸诽。
以上片段摘自《高性能Mysql》這本書對MVCC的定義甥材。他的主要實(shí)現(xiàn)思想是通過數(shù)據(jù)多版本來做到讀寫分離盯另。從而實(shí)現(xiàn)不加鎖讀進(jìn)而做到讀寫并行性含。MVCC在mysql中的實(shí)現(xiàn)依賴的是undo log與read view;
- undo log :undo log 中記錄某行數(shù)據(jù)的多個版本的數(shù)據(jù)。
-
read view :用來判斷當(dāng)前版本數(shù)據(jù)的可見性
MVCC的實(shí)現(xiàn)鸳惯,是通過保存數(shù)據(jù)在某個時間點(diǎn)的快照來實(shí)現(xiàn)的商蕴。也就是說,不管需要執(zhí)行多長時間芝发,每個事務(wù)看到的數(shù)據(jù)是一致的绪商。根據(jù)事務(wù)開始的時間不同,每個事務(wù)對同一張表辅鲸,同一時刻看到的數(shù)據(jù)可能是不一樣的格郁。不同存儲引擎的MVCC實(shí)現(xiàn)是不同的,典型的有樂觀(optimistic)并發(fā)控制和悲觀(pessimistic)并發(fā)控制独悴。
5.2.1 MVCC具體實(shí)現(xiàn)分析
輕松理解MYSQL MVCC 實(shí)現(xiàn)機(jī)制
下面,我們通過InnoDB的MVCC實(shí)現(xiàn)來分析MVCC使怎樣進(jìn)行并發(fā)控制的.
InnoDB的MVCC,是通過在每行記錄后面保存兩個隱藏的列來實(shí)現(xiàn)的,這兩個列例书,分別保存了這個行的創(chuàng)建時間,一個保存的是行的刪除時間刻炒。這里存儲的并不是實(shí)際的時間值,而是系統(tǒng)版本號(可以理解為事務(wù)的ID)决采,沒開始一個新的事務(wù),系統(tǒng)版本號就會自動遞增坟奥,事務(wù)開始時刻的系統(tǒng)版本號會作為事務(wù)的ID.下面看一下在REPEATABLE READ隔離級別下,MVCC具體是如何操作的.
- 例子:
- CREATE(創(chuàng)建表)
create table yang(
id int primary key auto_increment,
name varchar(20));
- INSERT(插入數(shù)據(jù))
start transaction;
insert into yang values(NULL,'yang') ;
insert into yang values(NULL,'long');
insert into yang values(NULL,'fei');
commit;
假設(shè)系統(tǒng)的版本號從1開始.
對應(yīng)在數(shù)據(jù)中的表如下(后面兩列是隱藏列,我們通過查詢語句并看不到)
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | undefined |
2 | long | 1 | undefined |
3 | fei | 1 | undefined |
- SELECT(查詢數(shù)據(jù))
InnoDB會根據(jù)以下兩個條件檢查每行記錄:
(1)InnoDB只會查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是,行的系統(tǒng)版本號小于或等于事務(wù)的系統(tǒng)版本號)树瞭,這樣可以確保事務(wù)讀取的行拇厢,要么是在事務(wù)開始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過的.
(2)行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號,這可以確保事務(wù)讀取到的行晒喷,在事務(wù)開始之前未被刪除.
只有a,b同時滿足的記錄孝偎,才能返回作為查詢結(jié)果.
- DELETE(刪除數(shù)據(jù))
InnoDB會為刪除的每一行保存當(dāng)前系統(tǒng)的版本號(事務(wù)的ID)作為刪除標(biāo)識.
看下面的具體例子分析:
第二個事務(wù),ID為2;
start transaction;
select * from yang; //(1)
select * from yang; //(2)
commit;
假設(shè)1
假設(shè)在執(zhí)行這個事務(wù)ID為2的過程中,剛執(zhí)行到(1),這時,有另一個事務(wù)ID為3往這個表里插入了一條數(shù)據(jù); 第三個事務(wù)ID為3;
start transaction;
insert into yang values(NULL,'tian');
commit;
這時表中的數(shù)據(jù)如下:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | undefined |
2 | long | 1 | undefined |
3 | fei | 1 | undefined |
4 | tian | 3 | undefined |
然后接著執(zhí)行事務(wù)2中的(2),由于id=4的數(shù)據(jù)的創(chuàng)建時間(事務(wù)ID為3),執(zhí)行當(dāng)前事務(wù)的ID為2,而InnoDB只會查找事務(wù)ID小于等于當(dāng)前事務(wù)ID的數(shù)據(jù)行,所以id=4的數(shù)據(jù)行并不會在執(zhí)行事務(wù)2中的(2)被檢索出來,在事務(wù)2中的兩條select 語句檢索出來的數(shù)據(jù)都只會下表:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | undefined |
2 | long | 1 | undefined |
3 | fei | 1 | undefined |
假設(shè)2
假設(shè)在執(zhí)行這個事務(wù)ID為2的過程中,剛執(zhí)行到(1),假設(shè)事務(wù)執(zhí)行完事務(wù)3后,接著又執(zhí)行了事務(wù)4; 第四個事務(wù):
start transaction;
delete from yang where id=1;
commit;
此時數(shù)據(jù)庫中的表如下:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | 4 |
2 | long | 1 | undefined |
3 | fei | 1 | undefined |
4 | tian | 3 | undefined |
接著執(zhí)行事務(wù)ID為2的事務(wù)(2),根據(jù)SELECT 檢索條件可以知道,它會檢索創(chuàng)建時間(創(chuàng)建事務(wù)的ID)小于當(dāng)前事務(wù)ID的行和刪除時間(刪除事務(wù)的ID)大于當(dāng)前事務(wù)的行,而id=4的行上面已經(jīng)說過,而id=1的行由于刪除時間(刪除事務(wù)的ID)大于當(dāng)前事務(wù)的ID,所以事務(wù)2的(2)select * from yang也會把id=1的數(shù)據(jù)檢索出來.所以,事務(wù)2中的兩條select 語句檢索出來的數(shù)據(jù)都如下:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | 4 |
2 | long | 1 | undefined |
3 | fei | 1 | undefined |
- UPDATE
InnoDB執(zhí)行UPDATE凉敲,實(shí)際上是新插入了一行記錄邪媳,并保存其創(chuàng)建時間為當(dāng)前事務(wù)的ID,同時保存當(dāng)前事務(wù)ID到要UPDATE的行的刪除時間.
假設(shè)3
假設(shè)在執(zhí)行完事務(wù)2的(1)后又執(zhí)行,其它用戶執(zhí)行了事務(wù)3,4,這時荡陷,又有一個用戶對這張表執(zhí)行了UPDATE操作;第5個事務(wù):
start transaction;
update yang set name='Long' where id=2;
commit;
根據(jù)update的更新原則:會生成新的一行,并在原來要修改的列的刪除時間列上添加本事務(wù)ID,得到表如下:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | 4 |
2 | long | 1 | 5 |
3 | fei | 1 | undefined |
4 | tian | 3 | undefined |
2 | Long | 5 | undefined |
繼續(xù)執(zhí)行事務(wù)2的(2),根據(jù)select 語句的檢索條件,得到下表:
id | name | 創(chuàng)建時間(事務(wù)ID) | 刪除時間(事務(wù)ID) |
---|---|---|---|
1 | yang | 1 | 4 |
2 | long | 1 | 5 |
3 | fei | 1 | undefined |
還是和事務(wù)2中(1)select 得到相同的結(jié)果.