Mysql可重複讀(2) —— 快照真的就是快照嗎

原文:zhuanlan.zhihu.com/p/55872397
做者:柳樹html

上一講最後拋出了一個問題,Mysql可重複讀的「快照」究竟是啥?mysql

是對當前數據的全量拷貝嗎?每開啓一個事務,都要把當前數據庫的數據拷貝一份出來?sql

很明顯不是。數據庫

一方面,這樣作太消耗內存了,另外一方面,這樣會很慢。數組

那麼Mysql是如何實現「快照」的呢?session

咱們仍是用上一講的例子:併發

咱們已經知道,Session A在第二次select時,查詢到的結果和第一次select時同樣,也就是說,Session B的update,對Session A來講,不可見,Mysql是如何作到的呢?3d

很簡單,也很絕妙 —— 數據版本,也就是咱們常說的MVCC,多版本併發控制。cdn

下面講具體實現。htm

Innodb裏面,每行數據,均可以有多個版本,每一個版本都有一個字段trx_id,記錄生成這個版本的事務的ID。

假設一開始,id=1這行數據,只有一個版本,trx_id是90,意味着生成這個版本的事務ID是90:

這時候Session A開始了,從上一講,咱們已經知道,begin時並不會生成快照,快照在第一次select時纔會生成,那麼第一次select時,session A都作了什麼呢?

session A只須要作一件事:用一個數組,來記錄當前活躍的事務ID。

假設session A的事務ID是97,當前還有另外兩個事務,事務ID是9四、96,因此session A會生成一個[94,96,97]的數組。

這個數組有什麼用?後面你就知道了。

接着,session B執行了update語句,來更新id=1這一行數據,給這一行數據生成一個新的版本,假設session B的事務ID是98,所以這行數據就有了兩個版本:

這時候,session A又來select了,當前版本是session B生成的,那session A是如何找到以前的版本的呢?

這時候,session A一開始生成的事務數組就派上用場了,session A的事務數組是[94,96,97],最小事務ID是94,最大事務ID是97,因此,當它遇到一行數據時,會先判斷這行數據的版本號X:

  • 若是X大於97,那麼意味着這行數據,是在session A開始以後,才提交的,應該對session A不可見
  • 若是X小於97,那麼分兩種狀況:
    • 若是X在數組裏面,好比X是96,那麼意味着,當session A開始時,生成這個版本的數據的事務,還沒提交,所以這行數據對Session A不可見
    • 若是X不在數組裏面,好比X是95,那麼意味着,當session A開始時,生成這個版本的數據的事務,已經提交,所以這行數據對Session A可見

好,如今session A開始遍歷id=1這行數據的全部版本:

當前版本是98,大於97,因此不可見,繼續看上一個版本;

再往上,版本是90,小於94,可見,就它了,因此session A select出來的id=1的數據,c的值是1。
固然,這樣的人肉判斷實在太麻煩了,在《Mysql實戰45講》裏,丁奇給出了這樣一個**「等價判斷」可見性的原則**:

  • 版本未提交,不可見;
  • 版本已提交,可是是在快照建立後提交的,不可見;
  • 版本已提交,並且是在快照建立前提交的,可見。

這其實就是可重複讀的想要實現的效果。

最後再給一個複雜點的例子,你們運用上面的原則,來預測sql語句的查詢結果:

小結一下:

  • 「快照」不是全量拷貝,而是利用了數據多版本的特性,也就是MVCC
  • MVCC的核心在於每一個事務本身維護的一個事務ID數組
  • 能夠用「等價原則」來判斷數據版本的可見性

問題又來了,這些不一樣版本的數據,是物理存在於內存或者磁盤中的嗎?

參考

相關文章
相關標籤/搜索