如何理解MySQL的事務(wù)?
- 簡單來說就是要保證一組數(shù)據(jù)庫操作,要么全部成功,要么全部失敗。MySQL中的事務(wù)支持是在引擎層實現(xiàn)的。
- 事務(wù)具有原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)四個特性。
SQL標準的事務(wù)隔離級別包括:
- 讀未提交 read-uncommited
- 讀提交 read-commited
-
可重復(fù)讀 repeatable read
一個事務(wù)執(zhí)行過程中看到的數(shù)據(jù),總是和這個事務(wù)啟動時看到的數(shù)據(jù)是一致的。在可重復(fù)讀隔離級別下,未提交的變更對其他線程也是不可見的。 -
串行化 serializable
對同一行記錄,讀會加“讀鎖”,寫會加“寫鎖”,當(dāng)出現(xiàn)讀寫沖突時,前后訪問的事務(wù)必須等前一個事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。
查看隔離級別
- 在MySQL客戶端中輸入
show variables like 'transaction-isolation';,即可查看當(dāng)前的隔離級別。
MySQL中的2種“視圖”
-
一種是view,是一個用查詢語句定義的虛擬表,在調(diào)用的時候執(zhí)行查詢語句并生成結(jié)果,創(chuàng)建view的語法是
create view ...,它的查詢方法與表一樣。 - 另一種是consistent read view,它是InnoDB在實現(xiàn)MVCC時用到的一致性視圖,用于支持RC(讀提交)和RR(可重復(fù)讀)隔離級別的實現(xiàn)。
InnoDB隔離級別的實現(xiàn)
- 事務(wù)啟動時,數(shù)據(jù)庫里會創(chuàng)建一個視圖,訪問的時候以視圖的邏輯結(jié)果為準。
- 可重復(fù)讀隔離級別下,視圖是在事務(wù)啟動時創(chuàng)建的,整個事務(wù)存在期間都用這個視圖。
- 讀提交隔離級別下,視圖是在每個SQL語句開始執(zhí)行的時候創(chuàng)建的。
- 讀未提交隔離級別下,直接返回記錄上的最新值,沒有視圖。
- 串行化隔離級別下,直接用加鎖的方式避免并行訪問。
可重復(fù)讀隔離級別的實現(xiàn)
- 在可重復(fù)讀隔離級別下,MySQL中每條記錄在更新的時候都會記錄一條回滾操作。記錄上的最新值,通過回滾操作(undo log),都會得到前一狀態(tài)的值。不同時刻啟動的事務(wù)有不同的視圖,即同一條記錄在系統(tǒng)中可以存在多個版本,這就是數(shù)據(jù)庫的MVCC(多版本并發(fā)控制)。
- InnoDB里每個事務(wù)都有一個唯一的
transaction id,它是事務(wù)開始的時候向InnoDB事務(wù)系統(tǒng)申請的,按申請順序嚴格遞增。 - 而每行數(shù)據(jù)也都是有多個版本的。每次事務(wù)更新數(shù)據(jù)的時候,都會生成一個新的版本,并把本次事務(wù)的
transaction id賦值給這個數(shù)據(jù)版本的事務(wù)ID,記為row trx_id。同時舊的數(shù)據(jù)版本要保留,并且在新的數(shù)據(jù)版本中,能有信息(undo log)可以直接拿到它。也就是說數(shù)據(jù)表中的同一行記錄,可能存在多個版本(row),每個版本都有自己的row trx_id。 - MVCC的實現(xiàn),InnoDB為每個事務(wù)構(gòu)造一個數(shù)組,用來保存這個事務(wù)的啟動瞬間,當(dāng)前所有啟動了,但還沒提交的事務(wù)的ID。
- 一個數(shù)據(jù)版本,對于一個事務(wù)視圖來說,除了自己的更新總是可見的以外,還有3種情況:
1、版本未提交,不可見;
2、版本已提交,但是是在視圖創(chuàng)建后提交的,不可見;
3、版本已提交,而且是在視圖創(chuàng)建前提交的,可見。
https://blog.csdn.net/syilt/article/details/107647007
基于可重復(fù)讀隔離的實現(xiàn),說明為什么盡量不要使用長事務(wù)?
- 長事務(wù)意味著系統(tǒng)里面會存在很老的事務(wù)視圖,由于這些事務(wù)隨時可能訪問數(shù)據(jù)庫里面的任何數(shù)據(jù),所以這個事務(wù)提交前,數(shù)據(jù)庫里面它可能用到回滾記錄都必須保留,這就會導(dǎo)致大量占用存儲空間。
- MySQL5.5及以前版本,回滾日志(undo log)是和“數(shù)據(jù)字典”一起放在ibdata文件里的,即使長事務(wù)最終提交,回滾段被清理,ibdata文件也不會變小。
- 注意,長事務(wù)不是“長連接”。
MySQL事務(wù)的啟動方式
- 顯示啟動,
begin;或strat transaction;(并不是立即開啟,而是執(zhí)行第一條操作InnoDB表的語句時才是真正開啟,提這點主要想說明它影響視圖的出現(xiàn)時機;如果想馬上啟動一個事務(wù),獲得一個read view,可以使用start transaction with consistent snap shot;),配套的提交語句commit,回滾語句rollback,commit work and chain提交事務(wù)并自動開啟下一個事務(wù)(節(jié)省再次執(zhí)行begin語句的開銷),此時一般是參數(shù)autocommit=1的情況。 - 當(dāng)set autocommit=0的時候,線程不會自動提交。此時,若只執(zhí)行一個select語句,事務(wù)就已經(jīng)啟動了,并且不會自動提交。直到commit或rollback或斷開連接。
長事務(wù)查詢
- 查詢持續(xù)時間超過60s的事務(wù):
select * from information_schema.innodb_trx where TIME_TO_SET(timediff(now(),trx_started)) > 60;。
事務(wù)更新時的版本控制邏輯
- 更新數(shù)據(jù)都是先讀后寫,這個讀只能是當(dāng)前讀(current read),即讀當(dāng)前的值(不管當(dāng)前值得row trx_id是否大于本事務(wù),都能讀到);讀到后會將自己修改后的數(shù)據(jù)和自己的事務(wù)ID賦給新的“當(dāng)前值”。如果當(dāng)前記錄的行鎖被其他事務(wù)占用,就需要進入鎖等待狀態(tài)。
- 除update語句外,select語句如果加鎖,也是當(dāng)前讀。有讀鎖(S鎖,共享鎖)
lock in share mode;,寫鎖(X鎖,排它鎖)for update。
讀提交和可重復(fù)讀的主要區(qū)別
- 在可重復(fù)讀隔離級別下,只需要在事務(wù)開始的時候創(chuàng)建一致性視圖,之后事務(wù)里的其他查詢都公用這個一致性視圖。
- 在讀提交隔離級別下,每一個語句執(zhí)行前都會重新計算出一個新的視圖。