聊一聊 MySQL 中的事務及其實現原理


說到數據庫,那就一定會聊到事務,事務也是面試中常問的問題,我們先來一個面試場景:

面試官:"事務的四大特性是什么?"
我:"ACID,即原子性(Atomicity)、隔離性(Isolation)、持久性(Durability)、一致性(Consistency)!"
面試官:"在 MySQL 數據庫的 InnoDB 引擎是怎么實現這四大特性的?"
我:"這個...這個....,還真沒有了解過哎"
面試官:"那我們就先這個吧,先回去吧,我們會通知你的~"

這可能是比較常見的面試場景了,你也許回答到了事務的四大特性,但是不一定知道他的實現原理。今天我們就來一起打卡事務的四大特性和實現原理,對於原理的實現,這篇文章只是粗略的介紹一下,更多的細節可以關注我后續的文章。

數據庫的事務有四大特性:原子性、隔離性、永久性、一致性,下面將介紹這四大特性的定義和在 InnoDB 引擎中是怎么實現的。

原子性

定義

一次操作是不可分割的,要么全部成功,要么全部失敗。比如我們的轉賬操作,不允許出款方成功,收款方失敗這種情況,要么都成功,要么多失敗,不可能出現中間狀態。

實現

InnoDB 引擎使用 undo log(歸滾日志)來保證原子性操作,你對數據庫的每一條數據的改動(INSERT、DELETE、UPDATE)都會被記錄到 undo log 中,比如以下這些操作:

  • 你插入一條記錄時,至少要把這條記錄的主鍵值記下來,之后回滾的時候只需要把這個主鍵值對應的記錄刪掉就好了。
  • 你刪除了一條記錄,至少要把這條記錄中的內容都記下來,這樣之后回滾時再把由這些內容組成的記錄插入到表中就好了。
  • 你修改了一條記錄,至少要把修改這條記錄前的舊值都記錄下來,這樣之后回滾時再把這條記錄更新為舊值就好了。

當事務執行失敗或者調用了 rollback 方法時,就會觸發回滾事件,利用 undo log 中記錄將數據回滾到修改之前的樣子。

更多關於 undo log 的信息,后面再單獨開一篇文章打卡。

隔離性

定義

多個事務並發執行的時候,事務內部的操作與其他事務是隔離的,並發執行的各個事務之間不能互相干擾。

實現

隔離性可能會引入臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)等問題,為了解決這些問題就引入了“隔離級別”的概念。

SQL 標准的事務隔離級別包括:讀未提交(read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(serializable)

  • 讀未提交:一個事務還沒提交時,它做的變更就能被別的事務看到。
  • 讀提交:一個事務提交之后,它做的變更才會被其他事務看到。
  • 可重復讀: 一個事務執行過程中看到的數據,總是跟這個事務在啟動時看到的數據是一致的。當然在可重復讀隔離級別下,未提交變更對其他事務也是不可見的。
  • 串行化: 顧名思義是對於同一行記錄,“寫”會加“寫鎖”,“讀”會加“讀鎖”。當出現讀寫鎖沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行。

SQL標准中規定,針對不同的隔離級別,並發事務可以發生不同嚴重程度的問題,具體情況如下:

隔離級別 臟讀 不可重復讀 幻讀
讀未提交 可能 可能 可能
讀提交 不可能 可能 可能
可重復讀 不可能 不可能 可能
串行化 不可能 不可能 不可能

上面就是幾種隔離級別可能出現的並發問題,但是有必要說一下,你隔離得越嚴實,效率就會越低。

InnoDB 引擎是如何保證隔離性的?利用鎖和 MVCC 機制。這里簡單的介紹一下 MVCC 機制,也叫多版本並發控制,在使用 READ COMMITTD、REPEATABLE READ 這兩種隔離級別的事務下,每條記錄在更新的時候都會同時記錄一條回滾操作,就會形成一個版本鏈,在執行普通的 SELECT 操作時訪問記錄的版本鏈的過程,這樣子可以使不同事務的讀-寫、寫-讀操作並發執行,從而提升系統性能。

持久性

定義

事務一旦提交,它對數據庫的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

實現

要保證持久性很簡單,就是每次事務提交的時候,都將數據刷磁盤上,這樣一定保證了安全性,但是要知道如果每次事務提交都將數據寫入到磁盤的話,頻繁的 IO 操作,成本太高,數據庫的性能極低,所以這種方式不可取。

InnoDB 引擎是怎么解決的?InnoDB 引擎引入了一個中間層來解決這個持久性的問題,我們把這個叫做 redo log(歸檔日子)

為什么要引入 redo log?redo log 可以保證持久化又可以保證數據庫的性能,相比於直接刷盤,redo log 有以下兩個優勢:

  • redo log體積小,畢竟只記錄了哪一頁修改了啥,因此體積小,刷盤快。
  • redo log是一直往末尾進行追加,屬於順序IO。效率顯然比隨機IO來的快。

InnoDB 引擎是怎么做的?當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log 里面,並更新內存,這個時候更新就算完成了。當數據庫宕機重啟的時候,會將 redo log 中的內容恢復到數據庫中,再根據 undo log和 binlog 內容決定回滾數據還是提交數據。

更多 redo log,后面我打算單獨寫一篇文章。

一致性

定義

一致性簡單一點說就是數據執行前后都要處於一種合法的狀態,比如身份證號不能重復,性別只能是男或者女,高考的分數只能在0~750之間,紅綠燈只有3種顏色,房價不能為負的等等, 只有符合這些約束的數據才是有效的,比如有個小孩兒跟你說他高考考了1000分,你一聽就知道他胡扯呢。數據庫世界只是現實世界的一個映射,現實世界中存在的約束當然也要在數據庫世界中有所體現。如果數據庫中的數據全部符合現實世界中的約束(all defined rules),我們說這些數據就是一致的,或者說符合一致性的。

實現

要保證數據庫的數據一致性,要在以下兩個方面做努力:

  • 利用數據庫的一些特性來保證部分一致性需求:比如聲明某個列為NOT NULL 來拒絕NULL值得插入等。
  • 絕大部分還是需要我們程序員在編寫業務代碼得時候來保證

以上就是我今天要分享的內容,希望這篇文章對你的學習或者工作有所幫助,感謝您的閱讀,如果您覺得文章不錯歡迎點贊+轉發,感謝。

最后

目前互聯網上很多大佬都有 MySQL 相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。

平頭哥的技術博文


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM