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

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

面試官:"事務的四大特性是什麼?"
我:"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)spa

  • 讀未提交:一個事務還沒提交時,它作的變動就能被別的事務看到。
  • 讀提交:一個事務提交以後,它作的變動纔會被其餘事務看到。
  • 可重複讀: 一個事務執行過程當中看到的數據,老是跟這個事務在啓動時看到的數據是一致的。固然在可重複讀隔離級別下,未提交變動對其餘事務也是不可見的。
  • 串行化: 顧名思義是對於同一行記錄,「寫」會加「寫鎖」,「讀」會加「讀鎖」。當出現讀寫鎖衝突的時候,後訪問的事務必須等前一個事務執行完成,才能繼續執行。

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 相關文章,若有雷同,請多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有所錯誤之處,還望提出,謝謝。

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

平頭哥的技術博文
相關文章
相關標籤/搜索