概念
事務(wù)就是保證一組數(shù)據(jù)庫(kù)操作聪富,要么全部成功斧账,要么全部失敗埋市。
在MySQL中冠桃,事務(wù)是在引擎層實(shí)現(xiàn)的。
特性(ACID)
-
原子性(Atomicity):
一個(gè)事務(wù)中的操作道宅,要么全部成功食听,要么全部失敗胸蛛。 -
一致性(Consistency):
事務(wù)開始前和結(jié)束后,數(shù)據(jù)庫(kù)的完整性沒有被破壞樱报。 -
隔離性(Isolation):
數(shù)據(jù)庫(kù)支持多個(gè)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力葬项,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行導(dǎo)致的數(shù)據(jù)不一致。 -
持久性(Durability)
事務(wù)結(jié)束后肃弟,對(duì)數(shù)據(jù)的修改就是永久的玷室,即使系統(tǒng)故障也不會(huì)丟失。
隔離性與隔離級(jí)別
當(dāng)數(shù)據(jù)庫(kù)上有多個(gè)事務(wù)同時(shí)執(zhí)行時(shí)笤受,就可能出現(xiàn)臟讀穷缤、不可重復(fù)讀、幻讀的問題箩兽,為了解決這些問題津肛,就有了隔離級(jí)別的概念。
SQL標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別包括:
-
讀未提交(read uncommitted)
一個(gè)事務(wù)還沒提交時(shí)汗贫,它做的變更就能被別的事務(wù)看到身坐。 -
讀提交(read committed)
一個(gè)事務(wù)只有提交后,它做的變更才能被別的事務(wù)看到落包。 -
可重復(fù)讀(repeatable read)
一個(gè)事務(wù)執(zhí)行過程中看到的數(shù)據(jù)部蛇,總是跟它在啟動(dòng)時(shí)看到的數(shù)據(jù)一致。 -
串行化(serializable)
當(dāng)出現(xiàn)讀寫鎖沖突的時(shí)候咐蝇,后訪問的事務(wù)必須等前一個(gè)事務(wù)完成涯鲁,才能繼續(xù)執(zhí)行。
舉例說(shuō)明各個(gè)隔離級(jí)別
假設(shè)數(shù)據(jù)表T中只有一列有序,其中一行的值為1抹腿,下面是按照時(shí)間順序執(zhí)行兩個(gè)事務(wù)的行為:
create table T(c int) engine=InnoDB;
insert into T(c) values(1);
來(lái)看看在不同的事務(wù)隔離級(jí)別下,事務(wù)A的V1旭寿,V2警绩,V3的值分別是什么。
- 讀未提交:雖然B還沒提交盅称,但是結(jié)果已經(jīng)被A看到了肩祥,所以V1、V2缩膝、V3都是2
- 讀提交:B的修改提交后混狠,A才能看到,所以V1是1逞盆,V2檀蹋、V3是2
- 可重復(fù)讀:A在提交前,看到的數(shù)據(jù)都和A啟動(dòng)時(shí)看到的一致,所以V1俯逾、V2是1贸桶,V3是2
- 串行化:A先加了讀鎖,B在修改的時(shí)候桌肴,發(fā)生了讀寫鎖沖突皇筛,所以等到A提交后,B的修改才會(huì)接著執(zhí)行坠七,所以V1水醋、V2是1,V3是2
在實(shí)現(xiàn)上彪置,數(shù)據(jù)庫(kù)里面會(huì)創(chuàng)建一個(gè)視圖拄踪,訪問的時(shí)候以視圖的邏輯結(jié)果為準(zhǔn)。
在可重復(fù)讀隔離級(jí)別下拳魁,這個(gè)視圖是在事務(wù)啟動(dòng)時(shí)創(chuàng)建的惶桐,整個(gè)事務(wù)存在期間都用這個(gè)視圖;
在讀提交隔離級(jí)別下潘懊,這個(gè)視圖是在SQL語(yǔ)句開始執(zhí)行時(shí)創(chuàng)建的姚糊;
在讀未提交隔離級(jí)別下,直接返回記錄上的最新值授舟,沒有視圖概念救恨;
在串行化隔離級(jí)別下,直接用加鎖的方式避免并行訪問释树。
事務(wù)隔離的實(shí)現(xiàn)
在MySQL中肠槽,每條記錄在更新的時(shí)候,都會(huì)同時(shí)記錄一條回滾操作躏哩,記錄上的最新值署浩,通過回滾操作揉燃,都可以得到前一個(gè)狀態(tài)的值扫尺。
假設(shè)一個(gè)值從 1 被按順序改成了 2、3炊汤、4正驻,在回滾日志里面就會(huì)有類似下面的記錄。
當(dāng)前值是 4抢腐,但是在查詢這條記錄的時(shí)候姑曙,不同時(shí)刻啟動(dòng)的事務(wù)會(huì)有不同的 read-view。在視圖 A迈倍、B伤靠、C 里,這個(gè)記錄的值分別為 1啼染、2宴合、4焕梅,同一條記錄在系統(tǒng)中可以存在多個(gè)版本,這就是數(shù)據(jù)庫(kù)的多版本并發(fā)控制(MVCC)卦洽。對(duì)于read-view A贞言,想要得到 1,就得將當(dāng)前值依次執(zhí)行圖中所有回滾操作才能得到阀蒂。
回滾日志不會(huì)一直保留该窗,系統(tǒng)會(huì)判斷,當(dāng)沒有事務(wù)需要用到這些回滾日志的時(shí)候蚤霞,回滾日志就會(huì)被刪除酗失。即當(dāng)系統(tǒng)里沒有比這個(gè)回滾日志更早的 read-view 的時(shí)候就會(huì)被刪除。
為什么盡量不要使用長(zhǎng)事務(wù)
長(zhǎng)事務(wù)意味著系統(tǒng)里會(huì)存在很老的事務(wù)視圖昧绣。由于這些事務(wù)隨時(shí)會(huì)訪問數(shù)據(jù)庫(kù)里的任何數(shù)據(jù)级零,所以這個(gè)事務(wù)提交之前,數(shù)據(jù)庫(kù)里它可能用到的回滾記錄都必須保留滞乙,就會(huì)導(dǎo)致占用大量存儲(chǔ)空間奏纪。
在 MySQL5.5 及以前的版本,回滾日志是跟數(shù)據(jù)字典一起放在 ibdata 文件里的斩启,即使長(zhǎng)事務(wù)最終提交序调,回滾段被清理,文件也不會(huì)變小兔簇》⒕睿可能存在數(shù)據(jù)只有20G,而回滾段有 200G 的庫(kù)垄琐,最終只好為了清理回滾段边酒,而重建整個(gè)庫(kù)。
除了對(duì)回滾段的影響狸窘,長(zhǎng)事務(wù)還占用鎖資源墩朦,也可能拖垮整個(gè)庫(kù)。
長(zhǎng)事務(wù)的查詢方式:
可以在 information_schema 庫(kù)的 innodb_trix 表中查詢長(zhǎng)事務(wù)翻擒。比如下面這個(gè)查找持續(xù)時(shí)間超過 60s 的事務(wù):
select
*
from
information_schema.innodb_trx
where
TIME_TO_SEC(timediff(now(),trx_started)) > 60