事務(wù)就是要保證一組數(shù)據(jù)庫操作锈津,要么全部成功,要么全部失敗隆箩。在 MySQL 中该贾,事務(wù)支持是在引擎層實(shí)現(xiàn)的。MySQL 是一個(gè)支持多引擎的系統(tǒng)捌臊,但并不是所有的引擎都支持事務(wù)杨蛋。比如 MySQL 原生的 MyISAM 引擎就不支持事務(wù),這也是 MyISAM 被 InnoDB 取代的重要原因之一
隔離性與隔離級(jí)別
ACID
- Atomicity理澎,原子性
- Consistency逞力,一致性
- Isolation,隔離性
- Durability糠爬,持久性
當(dāng)數(shù)據(jù)庫上有多個(gè)事務(wù)同時(shí)執(zhí)行的時(shí)候寇荧,就可能出現(xiàn)臟讀(dirty read)、不可重復(fù)讀(non-repeatable read)执隧、幻讀(phantom read)的問題揩抡,為了解決這些問題,就有了“隔離級(jí)別”的概念
SQL 標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別
- 讀未提交(read uncommitted):一個(gè)事務(wù)還沒提交時(shí)镀琉,它做的變更就能被別的事務(wù)看到
- 讀提交(read committed):一個(gè)事務(wù)提交之后峦嗤,它做的變更才會(huì)被其他事務(wù)看到,新聞線上數(shù)據(jù)庫都是READ-COMMITTED
- 可重復(fù)讀(repeatable read):一個(gè)事務(wù)執(zhí)行過程中看到的數(shù)據(jù)屋摔,總是跟這個(gè)事務(wù)在啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的烁设。當(dāng)然在可重復(fù)讀隔離級(jí)別下,未提交變更對其他事務(wù)也是不可見的钓试。MySQL默認(rèn)是REPEATABLE-READ
- 串行化(serializable ):對于同一行記錄装黑,“寫”會(huì)加“寫鎖”耙替,“讀”會(huì)加“讀鎖”。當(dāng)出現(xiàn)讀寫鎖沖突的時(shí)候曹体,后訪問的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行
隔離的實(shí)現(xiàn)
事務(wù)啟動(dòng)的時(shí)候會(huì)創(chuàng)建一個(gè)視圖硝烂,訪問的時(shí)候以視圖的邏輯結(jié)果為準(zhǔn)
- 在“可重復(fù)讀”隔離級(jí)別下箕别,這個(gè)視圖是在事務(wù)啟動(dòng)時(shí)創(chuàng)建的,整個(gè)事務(wù)存在期間都用這個(gè)視圖
- 在“讀提交”隔離級(jí)別下滞谢,這個(gè)視圖是在每個(gè) SQL 語句開始執(zhí)行的時(shí)候創(chuàng)建的
- 在“讀未提交”隔離級(jí)別下直接返回記錄上的最新值串稀,沒有視圖概念
- 而“串行化”隔離級(jí)別下直接用加鎖的方式來避免并行訪問
隔離示例
假設(shè)數(shù)據(jù)表 T 中只有一列,其中一行的值為 1狮杨,下面是按照時(shí)間順序執(zhí)行兩個(gè)事務(wù)的行為母截,在不同的隔離級(jí)別下,事務(wù) A 會(huì)有哪些不同的返回結(jié)果橄教,也就是圖里面 V1清寇、V2、V3 的返回值分別是多少:
- 若隔離級(jí)別是“讀未提交”护蝶, 則 V1 的值就是 2华烟。這時(shí)候事務(wù) B 雖然還沒有提交,但是結(jié)果已經(jīng)被 A 看到了持灰。因此盔夜,V2、V3 也都是 2
- 若隔離級(jí)別是“讀提交”闰蚕,則 V1 是 1软族,V2 的值是 2证九。事務(wù) B 的更新在提交后才能被 A 看到。所以椭微, V3 的值也是 2
- 若隔離級(jí)別是“可重復(fù)讀”,則 V1盲链、V2 是 1赏表,V3 是 2。之所以 V2 還是 1匈仗,遵循的就是這個(gè)要求:事務(wù)在執(zhí)行期間看到的數(shù)據(jù)前后必須是一致的
- 若隔離級(jí)別是“串行化”瓢剿,則在事務(wù) B 執(zhí)行“將 1 改成 2”的時(shí)候,會(huì)被鎖住悠轩。直到事務(wù) A 提交后间狂,事務(wù) B 才可以繼續(xù)執(zhí)行。所以從 A 的角度看火架, V1鉴象、V2 值是 1忙菠,V3 的值是 2
隔離的應(yīng)用
假設(shè)你在管理一個(gè)個(gè)人銀行賬戶表
一個(gè)表存了每個(gè)月月底的余額,一個(gè)表存了賬單明細(xì)
候你要做數(shù)據(jù)校對纺弊,也就是判斷上個(gè)月的余額和當(dāng)前余額的差額牛欢,是否與本月的賬單明細(xì)一致
你一定希望在校對過程中,即使有用戶發(fā)生了一筆新的交易淆游,也不影響你的校對結(jié)果, 這時(shí)候使用“可重復(fù)讀”隔離級(jí)別就很方便
事務(wù)啟動(dòng)時(shí)的視圖可以認(rèn)為是靜態(tài)的傍睹,不受其他事務(wù)更新的影響
事務(wù)的啟動(dòng)
-
事務(wù)的啟動(dòng)方式
- 隱式啟動(dòng)事務(wù)語句,set autocommit = 1, 在 autocommit 為 1 的情況下犹菱,用 begin 顯式啟動(dòng)的事務(wù)拾稳,如果執(zhí)行 commit 則提交事務(wù)。如果執(zhí)行 commit work and chain腊脱,則是提交事務(wù)并自動(dòng)啟動(dòng)下一個(gè)事務(wù)访得,這樣也省去了再次執(zhí)行 begin 語句的開銷。同時(shí)帶來的好處是從程序開發(fā)的角度明確地知道每個(gè)語句是否處于事務(wù)中
- 顯式啟動(dòng)事務(wù)語句陕凹, begin 或 start transaction悍抑。配套的提交語句是 commit,回滾語句是 rollback杜耙。begin/start transaction 命令并不是一個(gè)事務(wù)的起點(diǎn)传趾,在執(zhí)行到它們之后的第一個(gè)操作 InnoDB 表的語句,事務(wù)才真正啟動(dòng)泥技。
- 如果你想要馬上啟動(dòng)一個(gè)事務(wù)浆兰,可以使用 start transaction with consistent snapshot 這個(gè)命令。上邊這種啟動(dòng)方式珊豹,一致性視圖是在第執(zhí)行第一個(gè)快照讀語句時(shí)創(chuàng)建的簸呈; 這種啟動(dòng)方式,一致性視圖是在執(zhí)行 start transaction with consistent snapshot 時(shí)創(chuàng)建的
set autocommit=0店茶,這個(gè)命令會(huì)將這個(gè)線程的自動(dòng)提交關(guān)掉蜕便。意味著如果你只執(zhí)行一個(gè) select 語句,這個(gè)事務(wù)就啟動(dòng)了贩幻,而且并不會(huì)自動(dòng)提交轿腺。這個(gè)事務(wù)持續(xù)存在直到你主動(dòng)執(zhí)行 commit 或 rollback 語句,或者斷開連接
有些客戶端連接框架會(huì)默認(rèn)連接成功后先執(zhí)行一個(gè) set autocommit=0 的命令丛楚。這就導(dǎo)致接下來的查詢都在事務(wù)中族壳,如果是長連接,就導(dǎo)致了意外的長事務(wù)
MySQL默認(rèn)是autocommit = on
你可以在 information_schema 庫的 innodb_trx 這個(gè)表中查詢長事務(wù)趣些,比如下面這個(gè)語句仿荆,用于查找持續(xù)時(shí)間超過 60s 的事務(wù)
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
事務(wù)隔離的實(shí)現(xiàn)
在 MySQL 中,每條記錄在更新的時(shí)候都會(huì)同時(shí)記錄一條回滾操作(undo log)。記錄上的最新值拢操,通過回滾操作锦亦,都可以得到前一個(gè)狀態(tài)的值,這樣同一條記錄在系統(tǒng)中可以存在多個(gè)版本令境,就是數(shù)據(jù)庫的多版本并發(fā)控制(MVCC)
多版本是咋么實(shí)現(xiàn)的
InnoDB 里面每個(gè)事務(wù)有一個(gè)唯一的事務(wù) ID杠园,叫作 transaction id。它是在事務(wù)開始的時(shí)候向 InnoDB 的事務(wù)系統(tǒng)申請的舔庶,是按申請順序嚴(yán)格遞增的抛蚁。 而每行數(shù)據(jù)也都是有多個(gè)版本的。每次事務(wù)更新數(shù)據(jù)的時(shí)候栖茉,都會(huì)生成一個(gè)新的數(shù)據(jù)版本,并且把 transaction id 賦值給這個(gè)數(shù)據(jù)版本的事務(wù) ID孵延,記為 row trx_id吕漂。同時(shí),舊的數(shù)據(jù)版本要保留尘应,并且在新的數(shù)據(jù)版本中惶凝,能夠有信息可以直接拿到它。 也就是說犬钢,數(shù)據(jù)表中的一行記錄苍鲜,其實(shí)可能有多個(gè)版本 (row),每個(gè)版本有自己的 row trx_id
下圖是一個(gè)記錄被多個(gè)事務(wù)連續(xù)更新后的狀態(tài):
- 圖中虛線框里是同一行數(shù)據(jù)的 4 個(gè)版本玷犹,當(dāng)前最新版本是 V4混滔,k 的值是 22,它是被 transaction id 為 25 的事務(wù)更新的歹颓,因此它的 row trx_id 也是 25
- 圖 2中的三個(gè)虛線箭頭坯屿,就是 undo log;而 V1巍扛、V2领跛、V3 并不是物理上真實(shí)存在的,而是每次需要的時(shí)候根據(jù)當(dāng)前版本和 undo log 計(jì)算出來的撤奸。比如吠昭,需要 V2 的時(shí)候,就是通過 V4 依次執(zhí)行 U3胧瓜、U2 算出來
事務(wù)隔離是咋么實(shí)現(xiàn)的
- 按照可重復(fù)讀的定義矢棚,一個(gè)事務(wù)啟動(dòng)的時(shí)候,能夠看到所有已經(jīng)提交的事務(wù)結(jié)果府喳。但是之后幻妓,這個(gè)事務(wù)執(zhí)行期間,其他事務(wù)的更新對它不可見。 因此肉津,一個(gè)事務(wù)如果一個(gè)數(shù)據(jù)版本是在他啟動(dòng)之前生成的强胰,就認(rèn);如果是他啟動(dòng)以后才生成的妹沙,就不認(rèn)偶洋,必須通過undo log找到它的上一個(gè)版本”。 當(dāng)然距糖,如果“上一個(gè)版本”也不可見玄窝,那就得繼續(xù)往前找
- 還有,如果是這個(gè)事務(wù)自己更新的數(shù)據(jù)悍引,它自己還是要認(rèn)的
- 在實(shí)現(xiàn)上恩脂, InnoDB 為每個(gè)事務(wù)構(gòu)造了一個(gè)數(shù)組,用來保存這個(gè)事務(wù)啟動(dòng)瞬間趣斤,當(dāng)前正在“活躍”的所有事務(wù) ID俩块。“活躍”指的就是浓领,啟動(dòng)了但還沒提交
- 數(shù)組里面事務(wù) ID 的最小值記為低水位 low-water-mark玉凯,相對于已提交的事務(wù)就是up-limit-id,當(dāng)前系統(tǒng)里面已經(jīng)創(chuàng)建過的事務(wù) ID 的最大值加 1 記為高水位 high-water-mark 相對于未開始的事務(wù)就是low-limit-id
- 這個(gè)視圖數(shù)組和高水位联贩,就組成了當(dāng)前事務(wù)的一致性視圖(read-view)漫仆。 而數(shù)據(jù)版本的可見性規(guī)則,就是基于數(shù)據(jù)的 row trx_id 和這個(gè)一致性視圖的對比結(jié)果得到的
這個(gè)視圖數(shù)組把所有的 row trx_id 分成了幾種不同的情況
對于當(dāng)前事務(wù)的啟動(dòng)瞬間來說泪幌,一個(gè)數(shù)據(jù)版本的 row trx_id盲厌,有以下幾種可能:
- 如果落在綠色部分,表示這個(gè)版本是已提交的事務(wù)或者是當(dāng)前事務(wù)自己生成的祸泪,這個(gè)數(shù)據(jù)是可見的
- 如果落在紅色部分狸眼,表示這個(gè)版本是由將來啟動(dòng)的事務(wù)生成的,是肯定不可見的
- 如果落在黃色部分浴滴,那就包括兩種情況
- 若 row trx_id 在數(shù)組中拓萌,表示這個(gè)版本是由還沒提交的事務(wù)生成的,不可見
- 若 row trx_id 不在數(shù)組中升略,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的微王,可見
比如,對于上圖中的數(shù)據(jù)來說品嚣,如果有一個(gè)事務(wù)炕倘,它的低水位是 18,那么當(dāng)它訪問這一行數(shù)據(jù)時(shí)翰撑,就會(huì)從 V4 通過 U3 計(jì)算出 V3罩旋,所以在它看來,這一行的值是 11。有了這個(gè)聲明后涨醋,系統(tǒng)里面隨后發(fā)生的更新瓜饥,就跟這個(gè)事務(wù)看到的內(nèi)容無關(guān)了,因?yàn)橹蟮母略÷睿傻陌姹疽欢▽儆谏厦娴?2 或者 3(1) 的情況乓土,而對它來說,這些新的數(shù)據(jù)版本是不存在的溯警,所以這個(gè)事務(wù)的快照趣苏,就是“靜態(tài)”的了
小插曲
MySQL中的兩個(gè)視圖
一個(gè)是 view。它是一個(gè)用查詢語句定義的虛擬表梯轻,在調(diào)用的時(shí)候執(zhí)行查詢語句并生成結(jié)果食磕。創(chuàng)建視圖的語法是 create view … ,而它的查詢方法與表一樣
另一個(gè)是 InnoDB 在實(shí)現(xiàn) MVCC 時(shí)用到的一致性讀視圖喳挑,即 consistent read view彬伦,用于支持 RC(Read Committed,讀提交)和 RR(Repeatable Read蟀悦,可重復(fù)讀)隔離級(jí)別的實(shí)現(xiàn)媚朦。
回滾日志總不能一直保留吧氧敢,什么時(shí)候刪除呢日戈?
答案是,在不需要的時(shí)候才刪除孙乖。也就是說浙炼,系統(tǒng)會(huì)判斷,當(dāng)沒有事務(wù)再需要用到這些回滾日志時(shí)唯袄,回滾日志會(huì)被刪除就是當(dāng)系統(tǒng)里沒有比這個(gè)回滾日志更早的 read-view 的時(shí)候
為什么建議你盡量不要使用長事務(wù)
長事務(wù)意味著系統(tǒng)里面會(huì)存在很老的事務(wù)視圖
由于這些事務(wù)隨時(shí)可能訪問數(shù)據(jù)庫里面的任何數(shù)據(jù)弯屈,所以這個(gè)事務(wù)提交之前,數(shù)據(jù)庫里面它可能用到的回滾記錄都必須保留恋拷,這就會(huì)導(dǎo)致大量占用存儲(chǔ)空間
在 MySQL 5.5 及以前的版本资厉,回滾日志是跟數(shù)據(jù)字典一起放在 ibdata 文件里的,即使長事務(wù)最終提交蔬顾,回滾段被清理宴偿,文件也不會(huì)變小。見過數(shù)據(jù)只有 20GB诀豁,而回滾段有 200GB 的庫窄刘。最終只好為了清理回滾段,重建整個(gè)庫
除了對回滾段的影響舷胜,長事務(wù)還占用鎖資源娩践,也可能拖垮整個(gè)庫
事務(wù)隔離下的查詢&更新
建表語句:
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
3個(gè)事務(wù)的執(zhí)行時(shí)機(jī):
假設(shè)
- .事務(wù) A 開始前,系統(tǒng)里面只有一個(gè)活躍事務(wù) ID 是 99
- 事務(wù) A、B翻伺、C 的版本號(hào)分別是 100材泄、101、102穆趴,且當(dāng)前系統(tǒng)里只有這四個(gè)事務(wù)
- 三個(gè)事務(wù)開始前脸爱,(1,1)這一行數(shù)據(jù)的 row trx_id 是 90
這樣,事務(wù) A 的視圖數(shù)組就是 [99,100], 事務(wù) B 的視圖數(shù)組是 [99,100,101], 事務(wù) C 的視圖數(shù)組是 [99,100,101,102]
查詢
事務(wù) A 的語句返回的結(jié)果未妹,為什么是 k=1?
為了簡化分析簿废,只畫出跟事務(wù) A 查詢邏輯有關(guān)的操作:
從圖中可以看到
- 第一個(gè)有效更新是事務(wù) C,把數(shù)據(jù)從 (1,1) 改成了 (1,2)络它。這時(shí)候族檬,這個(gè)數(shù)據(jù)的最新版本的 row trx_id 是 102,而 90 這個(gè)版本已經(jīng)成為了歷史版本
- 第二個(gè)有效更新是事務(wù) B化戳,把數(shù)據(jù)從 (1,2) 改成了 (1,3)单料。這時(shí)候,這個(gè)數(shù)據(jù)的最新版本(即 row trx_id)是 101点楼,而 102 又成為了歷史版本
- 在事務(wù) A 查詢的時(shí)候扫尖,其實(shí)事務(wù) B 還沒有提交,但是它生成的 (1,3) 這個(gè)版本已經(jīng)變成當(dāng)前版本了掠廓。但這個(gè)版本對事務(wù) A 必須是不可見的换怖,否則就變成臟讀了
現(xiàn)在事務(wù) A 要來讀數(shù)據(jù)了,它的視圖數(shù)組是 [99,100]蟀瞧。當(dāng)然了沉颂,讀數(shù)據(jù)都是從當(dāng)前版本讀起的。所以悦污,事務(wù) A 查詢語句的讀數(shù)據(jù)流程是這樣的:
- 找到 (1,3) 的時(shí)候铸屉,判斷出 row trx_id=101,比高水位大切端,處于紅色區(qū)域彻坛,不可見
- 接著,找到上一個(gè)歷史版本踏枣,一看 row trx_id=102昌屉,比高水位大,處于紅色區(qū)域椰于,不可見
- 再往前找怠益,終于找到了(1,1),它的 row trx_id=90瘾婿,比低水位小蜻牢,處于綠色區(qū)域烤咧,可見
這樣執(zhí)行下來,雖然期間這一行數(shù)據(jù)被修改過抢呆,但是事務(wù) A 不論在什么時(shí)候查詢煮嫌,看到這行數(shù)據(jù)的結(jié)果都是一致的,所以我們稱之為一致性讀
更新
事務(wù) B 的 update 語句抱虐,如果按照一致性讀昌阿,好像結(jié)果不對。 你看圖 5 中恳邀,事務(wù) B 的視圖數(shù)組是先生成的懦冰,之后事務(wù) C 才提交,不是應(yīng)該看不見 (1,2) 嗎谣沸,怎么能算出 (1,3) 來刷钢?
- 如果事務(wù) B 在更新之前查詢一次數(shù)據(jù),這個(gè)查詢返回的 k 的值確實(shí)是 1
- 但是乳附,當(dāng)它要去更新數(shù)據(jù)的時(shí)候内地,就不能再在歷史版本上更新了,否則事務(wù) C 的更新就丟失了
- 因此赋除,事務(wù) B 此時(shí)的 set k=k+1 是在(1,2)的基礎(chǔ)上進(jìn)行的操作
- 這里就用到了這樣一條規(guī)則:更新數(shù)據(jù)都是先讀后寫的阱缓,而這個(gè)讀,只能讀當(dāng)前的值举农,稱為“當(dāng)前讀”(current read)
- 因此荆针,在更新的時(shí)候,當(dāng)前讀拿到的數(shù)據(jù)是 (1,2)并蝗,更新后生成了新版本的數(shù)據(jù) (1,3)祭犯,這個(gè)新版本的 row trx_id 是 101
- 所以秸妥,在執(zhí)行事務(wù) B 查詢語句的時(shí)候滚停,一看自己的版本號(hào)是 101,最新數(shù)據(jù)的版本號(hào)也是 101粥惧,是自己的更新键畴,可以直接使用,所以查詢得到的 k 的值是 3
這里我們提到了一個(gè)概念突雪,叫作當(dāng)前讀起惕。其實(shí),除了 update 語句外咏删,select 語句如果加鎖惹想,也是當(dāng)前讀
- 如果把事務(wù) A 的查詢語句 select * from t where id=1 修改一下,加上 lock in share mode 或 for update督函,也都可以讀到版本號(hào)是 101 的數(shù)據(jù)嘀粱,返回的 k 的值是 3(會(huì)進(jìn)行所等待)
下面這兩個(gè) select 語句激挪,就是分別加了讀鎖(S 鎖,共享鎖)和寫鎖(X 鎖锋叨,排他鎖)
mysql> select k from t where id=1 lock in share mode;
mysql> select k from t where id=1 for update;
假設(shè)事務(wù) C 不是馬上提交的垄分,而是變成了下面的事務(wù) C’,會(huì)怎么樣呢
事務(wù) C的不同是娃磺,更新后并沒有馬上提交薄湿,在它提交前,事務(wù) B 的更新語句先發(fā)起了偷卧。雖然事務(wù) C’還沒提交豺瘤,但是 (1,2) 這個(gè)版本也已經(jīng)生成了,并且是當(dāng)前的最新版本听诸。那么炉奴,事務(wù) B 的更新語句會(huì)怎么處理呢?
- 這個(gè)時(shí)候蛇更,“兩階段鎖協(xié)議”就要上場了瞻赶。
- C沒提交,也就是說 (1,2) 這個(gè)版本上的寫鎖還沒釋放
- 而事務(wù) B 是當(dāng)前讀派任,必須要讀最新版本砸逊,而且必須加鎖,因此就被鎖住了掌逛,必須等到事務(wù) C’釋放這個(gè)鎖师逸,才能繼續(xù)它的當(dāng)前讀
到這里,一致性讀豆混、當(dāng)前讀和行鎖就串起來了
讀提交下的事務(wù)
上邊說的例子是可重復(fù)讀情況下的篓像,讀提交隔離級(jí)別下呢
先總結(jié)下可重復(fù)讀
- 可重復(fù)讀的核心就是一致性讀(consistent read)
- 務(wù)更新數(shù)據(jù)的時(shí)候,只能用當(dāng)前讀
- 當(dāng)前的記錄的行鎖被其他事務(wù)占用的話皿伺,就需要進(jìn)入鎖等待
讀提交的邏輯和可重復(fù)讀的邏輯類似员辩,最主要的區(qū)別是:
- 在可重復(fù)讀隔離級(jí)別下,只需要在事務(wù)開始的時(shí)候創(chuàng)建一致性視圖鸵鸥,之后事務(wù)里的其他查詢都共用這個(gè)一致性視圖
- 在讀提交隔離級(jí)別下奠滑,每一個(gè)語句執(zhí)行前都會(huì)重新算出一個(gè)新的視圖
在讀提交隔離級(jí)別下,事務(wù) A 和事務(wù) B 的查詢語句查到的 k妒穴,分別應(yīng)該是多少呢
下面是讀提交時(shí)的狀態(tài)圖宋税,可以看到這兩個(gè)查詢語句的創(chuàng)建視圖數(shù)組的時(shí)機(jī)發(fā)生了變化,就是圖中的 read view 框讼油。(注意:這里杰赛,我們用的還是事務(wù) C 的邏輯直接提交,而不是事務(wù) C’)
- 這時(shí)矮台,事務(wù) A 的查詢語句的視圖數(shù)組是在執(zhí)行這個(gè)語句的時(shí)候創(chuàng)建的乏屯,時(shí)序上 (1,2)阔墩、(1,3) 的生成時(shí)間都在創(chuàng)建這個(gè)視圖數(shù)組的時(shí)刻之前。
- 但是瓶珊,在這個(gè)時(shí)刻: (1,3) 還沒提交啸箫,屬于情況 1,不可見伞芹; (1,2) 提交了忘苛,屬于情況 3,可見
- 所以唱较,這時(shí)候事務(wù) A 查詢語句返回的是 k=2扎唾。 顯然地,事務(wù) B 查詢結(jié)果 k=3
內(nèi)容有點(diǎn)多南缓,總結(jié)一下
- InnoDB 的行數(shù)據(jù)有多個(gè)版本胸遇,每個(gè)數(shù)據(jù)版本有自己的 row trx_id,每個(gè)事務(wù)或者語句有自己的一致性視圖
- 普通查詢語句是一致性讀汉形,一致性讀會(huì)根據(jù) row trx_id 和一致性視圖確定數(shù)據(jù)版本的可見性纸镊。
- 對于可重復(fù)讀,查詢只承認(rèn)在事務(wù)啟動(dòng)前就已經(jīng)提交完成的數(shù)據(jù)
- 對于讀提交概疆,查詢只承認(rèn)在語句啟動(dòng)前就已經(jīng)提交完成的數(shù)據(jù)逗威;
- 而當(dāng)前讀,總是讀取已經(jīng)提交完成的最新版本岔冀,讀未提交隔離用的就是當(dāng)前讀
"start transaction with consistent snapshot; "
意思是從這個(gè)語句開始凯旭,創(chuàng)建一個(gè)持續(xù)整個(gè)事務(wù)的一致性快照。所以使套,在讀提交隔離級(jí)別下罐呼,這個(gè)用法就沒意義了,等效于普通的 start transaction