1. 程式人生 > >聊一聊 MySQL 中的事務及其實現原理

聊一聊 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 相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有所錯誤之處,還望提出,謝謝。

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