數據庫

MySQL是怎么保證數據一致性的

廣告
廣告

在《寫數據庫同時發mq消息事務一致性的一種解決方案》一文的方案中把分布式事務巧妙轉成了數據庫事務。我們都知道關系型數據庫事務能保證數據一致性,那數據庫到底是怎么設計事務這一特性的呢?

一、MySQL事務模型ACID

MySQL是一個多引擎數據庫,其中InnoDB支持數據庫事務,也是最常用的引擎。下邊就介紹InnoDB的事務模型

MySQL官方文檔對事務是這么描述的“事務是可以提交或回滾的原子工作單元。當事務對數據庫進行多個更改時,要么提交事務時所有更改都成功,要么回滾事務時撤消所有更改。”

“ACID模型是一組數據庫設計原則,強調業務數據和關鍵應用程序的可靠性很重要。MySQL包含與ACID模型緊密結合的innodb存儲引擎組件,確保數據不會被破壞,結果不會被軟件崩潰和硬件故障等異常情況所篡改。當您依賴ACID的特性,就不再需要重新發明一致性檢查和崩潰恢復機制。”

ACID模型按照字母拆解分為四大特性

A : atomicity 原子性。原子性是我們對事務最直觀的理解:事務就是一系列的操作,要么全部都執行,要么全部都不執行。

C : consistency 一致性。數據庫事務不能破壞關系數據的完整性以及業務邏輯上的一致性。例如對銀行轉帳事務,不管事務成功還是失敗,應該保證事務結束后ACCOUNTS表中Tom和Jack的存款和不變。

I : isolation 隔離性。在并發環境中,當不同的事務同時操縱相同的數據時,每個事務都有各自的完整數據空間。

D : durability 持久性。只要事務成功結束,它對數據庫所做的更新就必須永久保存下來。即使發生系統崩潰,重新啟動數據庫系統后,數據庫還能恢復到事務成功結束時的狀態。

二、InnoDB存儲引擎架構

下邊這張圖是InnoDB的架構,包括兩大部分,內存結構(In-Memory Structures)和磁盤上的結構(On-Disk Structures)。

在這張圖中,尤其要關注“Redo Log”和“Undo Tablespaces”這兩個區域,它們跟事務息息相關。 

內存結構(In-Memory Structures)更多的目的是在提高性能,因此本文不會過多關注。如果感興趣,可以訪問MySQL的官方網站www.mysql.com

“Undo Tablespaces”包含Undo Log(撤消日志),Undo Log是撤消日志記錄的集合,其中包含如何撤消事務對聚集索引記錄的最新更改的信息。Undo Log存在于撤消日志段中,這些日志段包含在回滾段中。

MySQL事務的四個特性中ACD三個特性是通過Redo Log(重做日志)和Undo Log 實現的,而 I(隔離性)是通過Lock(鎖)來實現。

三、普及個概念MVCC

MVCC,Multi-Version Concurrency Control,多版本并發控制。這項技術使得InnoDB的事務隔離級別下執行一致性讀操作有了保證,換言之,就是為了查詢一些正在被另一個事務更新的行,并且可以看到它們被更新之前的值。這是一個可以用來增強并發性的強大技術,查詢不用等待另一個事務釋放鎖。這項技術廣泛應用于數據庫,例如Oracle,PostgreSQL。當然也有一些數據庫產品以及mysql的其它存儲引擎不支持它。

看一看MVCC機制的示意圖,圖下邊會給出文字解釋 

圖中底部橫軸是時間,縱向的箭頭用來標記增、刪、改、查發生的時刻。尤其注意時間軸上方兩條色塊,代表數據的兩個版本V1、V2。為了醒目,我把V1、V2用紅色方框圈了起來(多版本的體現)。從左向右解讀這張圖

1、T1事務插入數據a=3,然后提交,生成了數據對應的V1版本

2、T2事務開始讀取a數據,讀取會持續一段時間,由于開始讀取的時刻,只有V1版本,所以最終T2讀到a=3

3、T2讀取過程中,T3對數據a進行修改,a=4,生成a數據的V2版本,但此時并未提交,因此生效的是V1版本數據。

4、T3修改提交之前,T4讀取a數據,由于此時V1版本數據生效,因此,T4讀到a=3

5、T3提交a=4的修改,V1版本數據失效,V2生效。a的值變為4

6、T5讀取a的值,讀到V2版本,a=4

至此,MVCC的概念就搞明白了,那么MySQL是怎么實現的呢?

四、InnoDB多版本的實現

1、三個隱藏字段

在內部,InnoDB向數據庫中存儲的每一行數據添加三個字段。

(1)DB_TRX_ID字段,6字節。表示插入或更新行的最后一個事務的事務標識符。此外,刪除在內部被視為更新,其中行中的特殊位被設置為將其標記為已刪除。

(2)DB_ROLL_PTR字段,7字節,叫做回滾指針(roll pointer)。回滾指針指向寫入回滾段的撤消日志(Undo Log)。如果行已更新,則撤消日志包含重建更新前該行內容所需的信息。

(3)DB_ROW_ID字段,6字節。包含一個隨著新行插入而單調增加的行ID,如果innodb自動生成聚集索引,則該索引包含行ID值。否則,DB_ROW_ID列不會出現在任何索引中。

2、多版本產生過程

以新增一條記錄并對該記錄進行2次修改來說明具體實現 

這條記錄有3個隱含字段(前面已經介紹),分別應對行的ID、事務號和回滾指針。

當插入的是一條新數據時,記錄上對應的回滾段指針為NULL 

這個過程做了以下幾件事

  • 用排他鎖鎖定該行
  • 把該行修改前的值拷貝到Undo Log中
  • 修改當前行的值,填寫事務編號,使回滾指針指向Undo Log中的修改前的行
  • 記錄Redo Log,包括Undo Log中的變化 

多次更新后,回滾指針會把不同版本的記錄串在一起。在InnoDB中存在purge線程,它會查詢那些比現在最老的活動事務還早的Undo Log,并刪除它們,從而保證Undo Log文件不至于無限增長。

3、提交與回滾

當事務正常提交時,InnoDB只需要更改事務狀態為commit即可,不需要做其他額外的工作

回滾(rollback)需要根據當前回滾指針從Undo Log中找出事務修改前的版本,并恢復。如果事務影響的行非常多,回滾則可能會很慢,根據經驗值沒提交的事務行數在1000~10000之間,InnoDB效率還是非常高的(唐成-數據庫多版本實現內幕)。

commit效率高,rollback代價大

4、可見性

事務隔離是數據庫處理的基礎之一,隔離是縮寫ACID中的I。隔離級別是當多個事務同時進行更改和執行查詢時,微調性能、可靠性、一致性和結果再現性之間的平衡的設置。

InnoDB提供SQL1992標準定義的四個隔離級別,READ UNCOMMITTED(未提交讀), READ COMMITTED(已提交讀), REPEATABLE READ(可重復讀), and SERIALIZABLE(可串行化)。默認的是REPEATABLE READ

每種隔離級別具體的意義可以百度查到,實現原理深入進去比較復雜。注意到每條數據隱藏的事務ID字段DB_TRX_ID有時序性,理論上可以根據一些策略,借助這個字段來實現與隔離級別相關的功能。事實上InnoDB也是這么做的。當然這個功能還涉及很多鎖的問題,這里不再展開。

MySQL官方文檔在“鎖和事務模型”這一章節開始就介紹了InnoDB的鎖,截個目錄,感興趣可以去讀一下。 

我還沒有學會寫個人說明!

從ResNet的誕生講起:美公司在北京的AI研究所出了成果,中美究竟誰受益更多?

上一篇

Linux服務器為什么被黑?

下一篇

你也可能喜歡

MySQL是怎么保證數據一致性的

長按儲存圖像,分享給朋友

ITPUB 每周精要將以郵件的形式發放至您的郵箱


微信掃一掃

微信掃一掃
双色球常规走势图 排球比分网即时比分 好股票配资平台 青海11选5走势图和策略 陕西快乐10分6选5一注多少钱 北京单场sp值即时指数 体彩云南十一选五奖金 北京十一选五5开奖 007篮球比分直播 单机大众麻将下载 新疆18选7走势图2元网 成都麻将拿牌打牌规则 澳洲幸运5计算方法贴吧