我們都知道事務(wù)的4個特性,原子性 一致性 隔離性和持久化
事務(wù)具有4個特征遏考,分別是原子性捂人、一致性逐纬、隔離性和持久性蛔屹,簡稱事務(wù)的ACID特性;
一豁生、原子性(atomicity)
一個事務(wù)要么全部提交成功兔毒,要么全部失敗回滾,不能只執(zhí)行其中的一部分操作甸箱,這就是事務(wù)的原子性
二育叁、一致性(consistency)
事務(wù)的執(zhí)行不能破壞數(shù)據(jù)庫數(shù)據(jù)的完整性和一致性,一個事務(wù)在執(zhí)行之前和執(zhí)行之后芍殖,數(shù)據(jù)庫都必須處于一致性狀態(tài)豪嗽。
如果數(shù)據(jù)庫系統(tǒng)在運行過程中發(fā)生故障,有些事務(wù)尚未完成就被迫中斷,這些未完成的事務(wù)對數(shù)據(jù)庫所作的修改有一部分已寫入物理數(shù)據(jù)庫昵骤,這是數(shù)據(jù)庫就處于一種不正確的狀態(tài)树碱,也就是不一致的狀態(tài)
三、隔離性(isolation)
事務(wù)的隔離性是指在并發(fā)環(huán)境中变秦,并發(fā)的事務(wù)時相互隔離的成榜,一個事務(wù)的執(zhí)行不能不被其他事務(wù)干擾。不同的事務(wù)并發(fā)操作相同的數(shù)據(jù)時蹦玫,每個事務(wù)都有各自完成的數(shù)據(jù)空間赎婚,即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對其他并發(fā)事務(wù)時隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能相互干擾樱溉。
在標準SQL規(guī)范中挣输,定義了4個事務(wù)隔離級別,不同的隔離級別對事務(wù)的處理不同福贞,分別是:未授權(quán)讀取撩嚼,授權(quán)讀取,可重復(fù)讀取和串行化
1挖帘、讀未提交(Read Uncommited)完丽,該隔離級別允許臟讀取,其隔離級別最低拇舀;比如事務(wù)A和事務(wù)B同時進行逻族,事務(wù)A在整個執(zhí)行階段,會將某數(shù)據(jù)的值從1開始一直加到10骄崩,然后進行事務(wù)提交聘鳞,此時,事務(wù)B能夠看到這個數(shù)據(jù)項在事務(wù)A操作過程中的所有中間值(如1變成2要拂,2變成3等)抠璃,而對這一系列的中間值的讀取就是未授權(quán)讀取
2、授權(quán)讀取也稱為已提交讀(Read Commited)脱惰,授權(quán)讀取只允許獲取已經(jīng)提交的數(shù)據(jù)鸡典。比如事務(wù)A和事務(wù)B同時進行,事務(wù)A進行+1操作枪芒,此時彻况,事務(wù)B無法看到這個數(shù)據(jù)項在事務(wù)A操作過程中的所有中間值,只能看到最終的10舅踪。另外纽甘,如果說有一個事務(wù)C,和事務(wù)A進行非常類似的操作抽碌,只是事務(wù)C是將數(shù)據(jù)項從10加到20悍赢,此時事務(wù)B也同樣可以讀取到20决瞳,即授權(quán)讀取允許不可重復(fù)讀取。
3左权、可重復(fù)讀(Repeatable Read)
就是保證在事務(wù)處理過程中皮胡,多次讀取同一個數(shù)據(jù)時,其值都和事務(wù)開始時刻是一致的赏迟,因此該事務(wù)級別禁止不可重復(fù)讀取和臟讀取屡贺,但是有可能出現(xiàn)幻影數(shù)據(jù)。所謂幻影數(shù)據(jù)锌杀,就是指同樣的事務(wù)操作甩栈,在前后兩個時間段內(nèi)執(zhí)行對同一個數(shù)據(jù)項的讀取,可能出現(xiàn)不一致的結(jié)果糕再。在上面的例子中量没,可重復(fù)讀取隔離級別能夠保證事務(wù)B在第一次事務(wù)操作過程中,始終對數(shù)據(jù)項讀取到1突想,但是在下一次事務(wù)操作中殴蹄,即使事務(wù)B(注意,事務(wù)名字雖然相同猾担,但是指的是另一個事務(wù)操作)采用同樣的查詢方式饶套,就可能讀取到10或20;
4垒探、串行化
是最嚴格的事務(wù)隔離級別,它要求所有事務(wù)被串行執(zhí)行怠李,即事務(wù)只能一個接一個的進行處理圾叼,不能并發(fā)執(zhí)行。
四捺癞、持久性(durability)
一旦事務(wù)提交夷蚊,那么它對數(shù)據(jù)庫中的對應(yīng)數(shù)據(jù)的狀態(tài)的變更就會永久保存到數(shù)據(jù)庫中。--即使發(fā)生系統(tǒng)崩潰或機器宕機等故障髓介,只要數(shù)據(jù)庫能夠重新啟動惕鼓,那么一定能夠?qū)⑵浠謴?fù)到事務(wù)成功結(jié)束的狀態(tài)
Mysql的mvcc機制是多版本并發(fā)控制主要是針對innodb的讀已提交和可重復(fù)度,不支持讀未提交和串行化(將所有的操作串行化執(zhí)行唐础,效率低)箱歧。
多版本控制(Multiversion Concurrency Control): 指的是一種提高并發(fā)的技術(shù)。最早的數(shù)據(jù)庫系統(tǒng)一膨,只有讀讀之間可以并發(fā)呀邢,讀寫,寫讀豹绪,寫寫都要阻塞价淌。引入多版本之后,只有寫寫之間相互阻塞,其他三種操作都可以并行蝉衣,這樣大幅度提高了InnoDB的并發(fā)度括尸。在內(nèi)部實現(xiàn)中,InnoDB通過undo log保存每條數(shù)據(jù)的多個版本病毡,并且能夠找回數(shù)據(jù)歷史版本提供給用戶讀濒翻,每個事務(wù)讀到的數(shù)據(jù)版本可能是不一樣的。
Mysql利用mvcc+悲觀鎖和mvcc+樂觀鎖,提高數(shù)據(jù)庫的整體性能mvcc解決讀寫沖突,鎖解決了謝謝沖突剪验。
當前讀和快照讀
? 當前讀
像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操作都是一種當前讀肴焊,為什么叫當前讀?就是它讀取的是記錄的最新版本功戚,讀取時還要保證其他并發(fā)事務(wù)不能修改當前記錄娶眷,會對讀取的記錄進行加鎖。
? 快照讀
像不加鎖的select操作就是快照讀啸臀,即不加鎖的非阻塞讀届宠;快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀乘粒;之所以出現(xiàn)快照讀的情況豌注,是基于提高并發(fā)性能的考慮,快照讀的實現(xiàn)是基于多版本并發(fā)控制灯萍,即MVCC,可以認為MVCC是行鎖的一個變種轧铁,但它在很多情況下,避免了加鎖操作旦棉,降低了開銷齿风;既然是基于多版本,即快照讀可能讀到的并不一定是數(shù)據(jù)的最新版本绑洛,而有可能是之前的歷史版本救斑。
Mvcc的幾個概念
隱藏字段
(1) 事務(wù)id:DB_TRX_ID,表示最近一次對本記錄行作修改(insert | update)的事務(wù)ID真屯。至于delete操作脸候,InnoDB認為是一個update操作,不過會更新一個另外的刪除位绑蔫,將行表示為deleted运沦。并非真正刪除。
(2) 回滾指針: DB_ROLL_PTR, 回滾指針配深,指向當前記錄行的undo log信息茶袒,也就是記錄行的歷史版本
(3) 行號:DB_ROW_ID ,如果定義了主鍵,那么InnoDB會使用主鍵作為聚簇索引,如果沒有定義主鍵凉馆,那么會使用第一非空的唯一索引(NOT NULL and UNIQUE INDEX)作為聚簇索引,如果既沒有主鍵也找不到合適的非空索引薪寓,就會將DB_ROW_ID作為主鍵,自增,占6個字節(jié).
Read View 讀視圖也叫快照
(1) trx_ids 活躍事務(wù)ID列表
(2) low_limit_id 最大的事務(wù)ID+1
(3) 活躍事務(wù)列表trx_ids中最小的事務(wù)ID
Undo log日志
存儲的是老版本數(shù)據(jù)亡资,當一個事務(wù)需要讀取記錄行時,如果當前記錄行不可見向叉,可以順著undo log鏈找到滿足其可見性條件的記錄行版本锥腻。是通過回滾指針形成的鏈表結(jié)構(gòu)。
① insert undo log : 事務(wù)對insert新記錄時產(chǎn)生的undo log, 因為不存在正在對這行數(shù)據(jù)進行讀的事務(wù)母谎,所以這個日志只在事務(wù)回滾時需要, 所以在事務(wù)提交后就可以立即丟棄瘦黑。
② update undo log:: 事務(wù)對記錄進行delete和update操作時產(chǎn)生的undo log,不僅在事務(wù)回滾時需要奇唤,快照讀也需要幸斥,因為可能存在正在對這行數(shù)據(jù)進行讀的事務(wù),只有當數(shù)據(jù)庫所使用的快照中不涉及該日志記錄咬扇,對應(yīng)的回滾日志才會被purge線程刪除甲葬。
Purge線程
從前面的分析可以看出,為了實現(xiàn)InnoDB的MVCC機制懈贺,更新或者刪除操作都只是設(shè)置一下老記錄的deleted_bit经窖,并不真正將過時的記錄刪除。
為了節(jié)省磁盤空間梭灿,InnoDB有專門的purge線程來清理deleted_bit為true的記錄画侣。為了不影響MVCC的正常工作,purge線程自己也維護了一個read view(這個read view相當于系統(tǒng)中最老活躍事務(wù)的read view);如果某個記錄的deleted_bit為true堡妒,并且DB_TRX_ID相對于purge線程的read view可見配乱,那么這條記錄一定是可以被安全清除的。
Undo log的圖解
Mvcc工作的大致流程
在innodb中皮迟,創(chuàng)建一個新的事務(wù)新事務(wù)后搬泥,執(zhí)行第一個select語句的時候,innodb會創(chuàng)建一個快照(read view)万栅,快照中會保存系統(tǒng)當前不應(yīng)該被本事務(wù)看到的其他活躍事務(wù)id列表(即trx_ids)。當用戶在這個事務(wù)中要讀取某個記錄行的時候西疤,innodb會將該記錄行的DB_TRX_ID與該Read View中的一些變量進行比較烦粒,判斷是否滿足可見性條件
假設(shè)當前事務(wù)要讀取某一個記錄行,該記錄行的DB_TRX_ID(即最新修改該行的事務(wù)ID)為trx_id代赁,Read View的活躍事務(wù)列表trx_ids的上下界分別為 low_limit_id 和 up_limit_id.
具體的比較算法如下:
- 如果 trx_id < up_limit_id, 那么表明“最新修改該行的事務(wù)”在“當前事務(wù)”創(chuàng)建快照之前就提交了扰她,所以該記錄行的值對當前事務(wù)是可見的。直接標識為可見芭碍,返回true,
- 如果 trx_id >= low_limit_id, 那么表明“最新修改該行的事務(wù)”在“當前事務(wù)”創(chuàng)建快照之后才被創(chuàng)建且修改該行的徒役,所以該記錄行的值對當前事務(wù)不可見。應(yīng)該通過回滾指針找到上個記錄行版本窖壕,判斷是否可見忧勿。循環(huán)往復(fù)杉女,直到可見
-
如果 up_limit_id <= trx_id < low_limit_id, 那就得通過二分查找判斷trx_id 是否在trx_ids列表出現(xiàn)過,
① 如果出現(xiàn)過鸳吸,說明是當前read view 中某個活躍的事務(wù)提交了熏挎,那當然是不可見的,應(yīng)該通過回滾指針找到上個記錄行版本晌砾,判斷是否可見坎拐,循環(huán)往復(fù),直到可見
② 如果沒有出現(xiàn)過养匈,說明這個事務(wù)是已經(jīng)提交了的哼勇,標識為可見,返回true
4.RR和RC的Read View的實現(xiàn)過程區(qū)別
提交讀和可重復(fù)讀都是使用 MVCC 機制來實現(xiàn)的呕乎,但是實現(xiàn)過程略微有一些不同积担,
①在innodb中的Repeatable Read級別, 只有事務(wù)在begin之后,執(zhí)行第一條select(讀操作)時, 才會創(chuàng)建一個快照(read view)楣嘁,將當前系統(tǒng)中活躍的其他事務(wù)記錄起來磅轻;并且事務(wù)結(jié)束前都是使用的這個快照,不會重新創(chuàng)建逐虚,直到事務(wù)結(jié)束聋溜。
②在innodb中的Read Committed級別, 事務(wù)在begin之后,執(zhí)行每條select(讀操作)語句時叭爱,快照會被重置撮躁,即會重新創(chuàng)建一個快照(read view)。
所以這個實現(xiàn)的差別可以達到不同的隔離級別买雾,兩個隔離級別都是快照讀