mysql事務

事務的四大特性(簡稱ACID)

數據庫如果支持事務的操作,那么就具備以下四個特性:

1、原子性(Atomicity)

  • 事務是數據庫的邏輯工作單位,事務中包括的諸操作要么全做,要么全不做。

2、一致性(Consistency)

  • 事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

3、隔離性(Isolation)

  • 一個事務的執行不能被其他事務干擾。

4、持續性/永久性(Durability)

  • 一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。

事務隔離級別

事務不隔離可能導致的問題有:

  • 臟讀: 臟讀就是指當一個事務正在訪問數據,并且對數據進行了修改,而這種修改還沒有提交到數據庫中,這時,另外一個事務也訪問這個數據,然后使用了這個數據。
  • 幻讀:幻讀:第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的全部數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那么,以后就會發生操作第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺一樣。
  • 不可重復讀:是指在一個事務內,多次讀同一數據。在這個事務還沒有結束時,另外一個事務也訪問該同一數據。那么,在第一個事務中的兩次讀數據之間,由于第二個事務的修改,那么第一個事務兩次讀到的的數據可能是不一樣的。這樣就發生了在一個事務內兩次讀到的數據是不一樣的,因此稱為是不可重復讀。
  • 可重復讀:可重復讀(REPEATABLE READS),由于提交讀隔離級別會產生不可重復讀的讀現象。所以,比提交讀更高一個級別的隔離級別就可以解決不可重復讀的問題。這種隔離級別就叫可重復讀

隔離級別:

  • 未提交讀(Read Uncommitted):允許臟讀,也就是可能讀取到其他會話中未提交事務修改的數據

  • 提交讀(Read Committed):只能讀取到已經提交的數據。Oracle等多數數據庫默認都是該級別 (不重復讀)

  • 可重復讀(Repeated Read):可重復讀。在同一個事務內的查詢都是事務開始時刻一致的,InnoDB默認級別。在SQL標準中,該隔離級別消除了不可重復讀,但是還存在幻象讀

  • 串行讀(Serializable):完全串行化的讀,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞

    針對上面各種情況,數據庫在解決上面的問題時定義不同的級別,用戶可以根據實際需求在性能和錯誤之間進行平衡配置。不同的數據庫的默認隔離級別是不一樣的。

mysql事務實現原理

事務實現有兩種方式:一種是通過鎖的方式來實現,另外一種是通過MVCC版本進行控制

如果有鎖的方式來實現,innodb的鎖的類型有:record,gap,Next-Key lock

Record lock

單條索引記錄上加鎖,record lock鎖住的永遠是索引,而非記錄本身,即使該表上沒有任何索引,那么innodb會在后臺創建一個隱藏的聚集主鍵索引,那么鎖住的就是這個隱藏的聚集主鍵索引。所以說當一條sql沒有走任何索引時,那么將會在每一條聚集索引后面加X鎖,這個類似于表鎖,但原理上和表鎖應該是完全不同的。

Gap lock

在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄之前或者之后加鎖,并不包括該索引記錄本身。gap lock的機制主要是解決可重復讀模式下的幻讀問題,關于幻讀的演示和gap鎖如何解決了幻讀。關于這一塊,先給出幾個定義

  • 快照讀:
    簡單的select操作,沒有lock in share mode或for update,快照讀不會加任何的鎖,而且由于mysql的一致性非鎖定讀的機制存在,任何快照讀也不會被阻塞。但是如果事務的隔離級別是SERIALIZABLE的話,那么快照讀也會被加上共享的next-key鎖,本文不對SERIALIZABLE隔離級別做敘述。

  • 當前讀:
    官方文檔的術語叫locking read,也就是insert,update,delete,select..in share mode和select..for update,當前讀會在所有掃描到的索引記錄上加鎖,不管它后面的where條件到底有沒有命中對應的行記錄。當前讀可能會引起死鎖。

  • 意向鎖:
    innodb的意向鎖主要用戶多粒度的鎖并存的情況。比如事務A要在一個表上加S鎖,如果表中的一行已被事務B加了X鎖,那么該鎖的申請也應被阻塞。如果表中的數據很多,逐行檢查鎖標志的開銷將很大,系統的性能將會受到影響。為了解決這個問題,可以在表級上引入新的鎖類型來表示其所屬行的加鎖情況,這就引出了“意向鎖”的概念。舉個例子,如果表中記錄1億,事務A把其中有幾條記錄上了行鎖了,這時事務B需要給這個表加表級鎖,如果沒有意向鎖的話,那就要去表中查找這一億條記錄是否上鎖了。如果存在意向鎖,那么假如事務A在更新一條記錄之前,先加意向鎖,再加X鎖,事務B先檢查該表上是否存在意向鎖,存在的意向鎖是否與自己準備加的鎖沖突,如果有沖突,則等待直到事務A釋放,而無須逐條記錄去檢測。事務B更新表時,其實無須知道到底哪一行被鎖了,它只要知道反正有一行被鎖了就行了。
    說白了意向鎖的主要作用是處理行鎖和表鎖之間的矛盾,能夠顯示“某個事務正在某一行上持有了鎖,或者準備去持有鎖”

  • 不可重復讀:
    指的是在同一個事務中,連續幾次快照讀,讀取的記錄應該是一樣的

  • 幻讀:
    指的是在一個事務A中執行了一個當前讀操作,而另外一個事務B在事務A的影響區間內insert了一條記錄,這時事務A再執行一個當前讀操作時,出現了幻行。這和不可重復讀的主要區別就在與事務A中一個是快照讀,一個當前讀;并且事務B中一個是任何的dml操作,一個只是insert。比如在A中select * from test where id<10 lock in share mode結果集為(1,2,3),這時在B中對test表插入了一條記錄4,這時在A中重新查詢結果集就是(1,2,3,4),和事務A在第一次查詢出來的結果集不一致,這里的4就是幻行。

MVCC版本控制

多版本控制: 指的是一種提高并發的技術。最早的數據庫系統,只有讀讀之間可以并發,讀寫,寫讀,寫寫都要阻塞。引入多版本之后,只有寫寫之間相互阻塞,其他三種操作都可以并行,這樣大幅度提高了InnoDB的并發度。在內部實現中,與Postgres在數據行上實現多版本不同,InnoDB是在undolog中實現的,通過undolog可以找回數據的歷史版本。找回的數據歷史版本可以提供給用戶讀(按照隔離級別的定義,有些讀請求只能看到比較老的數據版本),也可以在回滾的時候覆蓋數據頁上的數據。在InnoDB內部中,會記錄一個全局的活躍讀寫事務數組,其主要用來判斷事務的可見性。

定義:

1.read view, 快照snapshot

淘寶數據庫內核月報/2017/10/01/
此文雖然是以PostgreSQL進行的說明, 但并不影響理解, 在"事務快照的實現"該部分有細節需要注意:
事務快照是用來存儲數據庫的事務運行情況。一個事務快照的創建過程可以概括為:
查看當前所有的未提交并活躍的事務,存儲在數組中
選取未提交并活躍的事務中最小的XID,記錄在快照的xmin中
選取所有已提交事務中最大的XID,加1后記錄在xmax中
注意: 上文中在PostgreSQL中snapshot的概念, 對應MySQL中, 其實就是你在網上看到的read view,快照這些概念;
比如何登成就有關于Read view的介紹;
而 此文 卻仍是使用快照來介紹;

2.read view 主要是用來做可見性判斷的, 比較普遍的解釋便是"本事務不可見的當前其他活躍事務", 但正是該解釋, 可能會造成一節理解上的誤區, 所以此處提供兩個參考, 供給大家避開理解誤區:

read view中的高水位low_limit_id可以參考 https://github.com/zhangyachen/zhangyachen.github.io/issues/68, https://www.zhihu.com/question/66320138
其實上面第1點中加粗部分也是相關高水位的介紹( 注意進行了+1 )
3.另外, 對于read view快照的生成時機, 也非常關鍵, 正是因為生成時機的不同, 造成了RC,RR兩種隔離級別的不同可見性;

在innodb中(默認repeatable read級別), 事務在begin/start transaction之后的第一條select讀操作后, 會創建一個快照(read view), 將當前系統中活躍的其他事務記錄記錄起來;
在innodb中(默認repeatable committed級別), 事務中每條select語句都會創建一個快照(read view);

參考
With REPEATABLE READ isolation level, the snapshot is based on the time when the first read operation is performed.
使用REPEATABLE READ隔離級別,快照是基于執行第一個讀操作的時間。
With READ COMMITTED isolation level, the snapshot is reset to the time of each consistent read operation.
使用READ COMMITTED隔離級別,快照被重置為每個一致的讀取操作的時間。

4.undo-log

Undo log是InnoDB MVCC事務特性的重要組成部分。當我們對記錄做了變更操作時就會產生undo記錄,Undo記錄默認被記錄到系統表空間(ibdata)中,但從5.6開始,也可以使用獨立的Undo 表空間。
Undo記錄中存儲的是老版本數據,當一個舊的事務需要讀取數據時,為了能讀取到老版本的數據,需要順著undo鏈找到滿足其可見性的記錄。當版本鏈很長時,通??梢哉J為這是個比較耗時的操作(例如bug#69812)。
大多數對數據的變更操作包括INSERT/DELETE/UPDATE,其中INSERT操作在事務提交前只對當前事務可見,因此產生的Undo日志可以在事務提交后直接刪除(誰會對剛插入的數據有可見性需求呢!?。?,而對于UPDATE/DELETE則需要維護多版本信息,在InnoDB里,UPDATE和DELETE操作產生的Undo日志被歸成一類,即update_undo
另外, 在回滾段中的undo logs分為: insert undo log 和 update undo log

insert undo log : 事務對insert新記錄時產生的undolog, 只在事務回滾時需要, 并且在事務提交后就可以立即丟棄。
update undo log : 事務對記錄進行delete和update操作時產生的undo log, 不僅在事務回滾時需要, 一致性讀也需要,所以不能隨便刪除,只有當數據庫所使用的快照中不涉及該日志記錄,對應的回滾日志才會被purge線程刪除。

5.InnoDB存儲引擎在數據庫每行數據的后面添加了三個字段

  • 6字節的事務ID(DB_TRX_ID)字段: 用來標識最近一次對本行記錄做修改(insert|update)的事務的標識符, 即最后一次修改(insert|update)本行記錄的事務id。
    至于delete操作,在innodb看來也不過是一次update操作,更新行中的一個特殊位將行表示為deleted, 并非真正刪除。
  • 7字節的回滾指針(DB_ROLL_PTR)字段: 指寫入回滾段(rollback segment)的 undo log record (撤銷日志記錄記錄)。
    如果一行記錄被更新, 則 undo log record 包含 '重建該行記錄被更新之前內容' 所必須的信息。
  • 6字節的DB_ROW_ID字段: 包含一個隨著新行插入而單調遞增的行ID, 當由innodb自動產生聚集索引時,聚集索引會包括這個行ID的值,否則這個行ID不會出現在任何索引中。
    結合聚簇索引的相關知識點, 我的理解是, 如果我們的表中沒有主鍵或合適的唯一索引, 也就是無法生成聚簇索引的時候, InnoDB會幫我們自動生成聚集索引, 但聚簇索引會使用DB_ROW_ID的值來作為主鍵; 如果我們有自己的主鍵或者合適的唯一索引, 那么聚簇索引中也就不會包含 DB_ROW_ID 了 。
    關于聚簇索引, 《高性能MySQL》中的篇幅對我來說已經夠用了, 稍后會整理一下以前的學習筆記, 然后更新上來。

6.可見性比較算法(這里每個比較算法后面的描述是建立在rr級別下,rc級別也是使用該比較算法,此處未做描述)

設要讀取的行的最后提交事務id(即當前數據行的穩定事務id)為 trx_id_current
當前新開事務id為 new_id
當前新開事務創建的快照read view 中最早的事務id為up_limit_id, 最遲的事務id為low_limit_id(注意這個low_limit_id=未開啟的事務id=當前最大事務id+1)

比較:

  • 1.trx_id_current < up_limit_id, 這種情況比較好理解, 表示, 新事務在讀取該行記錄時, 該行記錄的穩定事務ID是小于, 系統當前所有活躍的事務, 所以當前行穩定數據對新事務可見, 跳到步驟5.
  • 2.trx_id_current >= trx_id_last, 這種情況也比較好理解, 表示, 該行記錄的穩定事務id是在本次新事務創建之后才開啟的, 但是卻在本次新事務執行第二個select前就commit了,所以該行記錄的當前值不可見, 跳到步驟4。
  • 3.trx_id_current <= trx_id_current <= trx_id_last, 表示: 該行記錄所在事務在本次新事務創建的時候處于活動狀態,從up_limit_id到low_limit_id進行遍歷,如果trx_id_current等于他們之中的某個事務id的話,那么不可見, 調到步驟4,否則表示可見。
  • 4.從該行記錄的 DB_ROLL_PTR 指針所指向的回滾段中取出最新的undo-log的版本號, 將它賦值該 trx_id_current,然后跳到步驟1重新開始判斷。
  • 5.將該可見行的值返回。

參考

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • 事務處理 事務處理是數據庫中的一個大塊頭,涉及到數據的完整性與一致性問題,由于mysql存在多種數據存儲引擎提供給...
    tanghomvee閱讀 753評論 0 0
  • 事務的定義 事務由單獨單元的一個或多個SQL語句組成,在這個單元中,每個MySQL語句是相互依賴的。而整個單獨單元...
    諸葛堅強閱讀 1,100評論 0 3
  • 零.MyISAM和InnoDB關于鎖的區別 ①MyISAM默認用的是表級鎖,不支持行級鎖。 ②InnoDB默認用的...
    一條路上的咸魚閱讀 669評論 0 5
  • 前言:我們都知道事務的幾種性質,數據庫為了維護這些性質,尤其是一致性和隔離性,一般使用加鎖這種方式。同時數據庫又是...
    bbe9e62bc5ba閱讀 755評論 0 2
  • 人到了一定年紀就會覺得一個人寂寞,孤獨總在歇隙侵蝕著靈魂。有時頂著所有挫敗壓力感覺特別委屈! 還得倔強著一個人,寧...
    Adear吳閱讀 112評論 0 0