MySQL的事務實現

事務需要滿足四個特性,就是ACID。

  • 原子性(Atomicity,或稱不可分割性)
  • 一致性(Consistency)
  • 隔離性(Isolation)
  • 持久性(Durability)

開始

在很久很久之前,沒有數據庫,我創建了多個txt文件,每個txt文件存儲一些數據,我寫了一個工具類,進行CRUD操作。這個簡單的數據庫天然支持持久性(Durability)。

原子性的實現

有一個操作需要修改兩個txt文件,我希望同時成功或者失敗,於是新創建了個文件,叫做undo log,中文名叫回滾日誌,用於記錄數據被修改前的信息,每操作一個文件的一次我就記錄一下。undo log主要記錄的是數據的邏輯變化,爲了在發生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,然後在發生錯誤時纔可以回滾。我實現了原子性(Atomicity)的需求。

持久性的實現

  由於頻繁操作txt文件,每個txt文件所在的磁盤位置不一致,磁頭不停的轉,每次讀寫數據都需要磁盤IO,效率會很低。爲此,我(InnoDB引擎)提供了緩存(Buffer Pool),Buffer Pool中包含了磁盤中部分數據頁的映射,作爲訪問數據庫的緩衝:當從數據庫讀取數據時,會首先從Buffer Pool中讀取,如果Buffer Pool中沒有,則從磁盤讀取後放入Buffer Pool;當向數據庫寫入數據時,會首先寫入Buffer Pool,Buffer Pool中修改的數據會定期刷新到磁盤中(這一過程稱爲刷髒)。Buffer Pool的使用大大提高了讀寫數據的效率,但是也帶了新的問題:如果MySQL宕機,而此時Buffer Pool中修改的數據還沒有刷新到磁盤,就會導致數據的丟失,事務的持久性無法保證。
  於是,redo log被引入來解決這個問題:當數據修改時,除了修改Buffer Pool中的數據,還會在redo log記錄這次操作;當事務提交時,會調用fsync接口對redo log進行刷盤。如果MySQL宕機,重啓時可以讀取redo log中的數據,對數據庫進行恢復。redo log採用的是WAL(Write-ahead logging,預寫式日誌),所有修改先寫入日誌,再更新到Buffer Pool,保證了數據不會因MySQL宕機而丟失,從而滿足了持久性要求。
  既然redo log也需要在事務提交時將日誌寫入磁盤,爲什麼它比直接將Buffer Pool中修改的數據寫入磁盤(即刷髒)要快呢?主要有以下兩方面的原因:
(1)刷髒是隨機IO,因爲每次修改的數據位置隨機,但寫redo log是追加操作,屬於順序IO。
(2)刷髒是以數據頁(Page)爲單位的,MySQL默認頁大小是16KB,一個Page上一個小修改都要整頁寫入;而redo log中只包含真正需要寫入的部分,無效IO大大減少。

隔離性的實現

  假設事務不滿足隔離性,我們耳熟能詳的轉錢例子就會出問題。所以事務需要滿足隔離性。隔離性與原子性、持久性側重於研究事務本身不同,隔離性研究的是不同事務之間的相互影響。隔離性是指,事務內部的操作與其他事務是隔離的,併發執行的各個事務之間不能互相干擾。嚴格的隔離性,對應了事務隔離級別中的Serializable (可串行化),但實際應用中出於性能方面的考慮很少會使用可串行化。
  隔離性追求的是併發情形下事務之間互不干擾。簡單起見,我們僅考慮最簡單的讀操作和寫操作(暫時不考慮帶鎖讀for update等特殊操作)。如果沒有隔離性的需求,就不會有MVCC的存在。鎖的實現也會很簡單,沒有讀鎖一類的,只有一個寫鎖防止併發導致的錯誤就行了。

MySQL隔離級別有以下四種(級別由低到高):

  • READUNCOMMITED(未提交讀)
  • READCOMMITED(提交讀)
  • REPEATABLEREAD(可重複讀)
  • SERIALIZABLE (串行化)
1. READUNCOMMITED未提交讀

如何實現?

如果mysql 什麼也不做,默認就是這個這樣。

有什麼好處?

快,效率高

有什麼壞處?

有髒讀,不可重複讀,幻讀的問題。髒讀在實際應用中就是別人給你轉錢,還沒扣掉別人的,你查看賬戶的錢已經多了。

2. READCOMMITED提交讀

如何實現?

mysql引入了MVCC的概念

有什麼好處?

解決了髒讀,讀取到的數據是已經提交的數據。

有什麼壞處?

效率變慢,依舊有不可重複讀,幻讀的問題。不可重複讀就是你查詢出來的數據第一次看到的是V1,再次查詢時最新的值V2。這是對的,讀到最新的值沒有任何問題,但是在覈查對賬,主從備份的場景下,一個事務寫A表和B表(兩個表有業務關係),當我們在事務寫完A表,沒有寫完B表的時候讀取A表和B表數據,我們就會導致對賬失敗。

3.REPEATABLEREAD(可重複讀)

如何實現?

mysql引入了MVCC的概念

有什麼好處?

解決了不可重複讀,一次事務中多次讀取到的數據是相同的。

有什麼壞處?

效率變慢,依舊有幻讀的問題。解決了不可重複讀的問題,但是沒有解決幻讀,不可重複讀主要是刪除和更新的影響,我們能夠屏蔽掉,但是我們無法屏幕掉insert的影響,insert的影響就是幻讀。在實際應用中就是你在一個方法中分頁查詢數據,後插入的數據如果在前面,會導致頁碼亂掉,你用這個數據打印還是做其他作用都不是一個合法的數據。

4.SERIALIZABLE (串行化)

如何實現?

mysql引入了讀寫鎖的概念。對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。

有什麼好處?

解決了幻讀。

有什麼壞處?

效率變慢

一致性的實現

一致性是事務追求的最終目標:前面提到的原子性、持久性和隔離性,都是爲了保證數據庫狀態的一致性。此外,除了數據庫層面的保障,一致性的實現也需要應用層面進行保障。

實現一致性的措施包括:

  • 保證原子性、持久性和隔離性,如果這些特性無法保證,事務的一致性也無法保證
  • 數據庫本身提供保障,例如不允許向整形列插入字符串值、字符串長度不能超過列的限制等
  • 應用層面進行保障,例如如果轉賬操作只扣除轉賬者的餘額,而沒有增加接收者的餘額,無論數據庫實現的多麼完美,也無法保證狀態的一致

其實幻讀和不可重複讀的區別是更新和新增

https://www.toutiao.com/a6777338939360412171/

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章