您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關MySQL中怎么實現ACID,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
假設你執行了一條 sql 語句:
update user set age = 18 where user_id = 345981
MySQL 會直接去磁盤修改數據嗎?
明顯不會,磁盤IO太慢了,如果每個請求過來 MySQL 都要寫磁盤,磁盤肯定扛不住。
那就寫內存?把數據從磁盤load到內存,然后修改內存里的數據。
也不行,萬一掉電了,內存就沒了,數據就再也找不回來了。
這其實是很多中間件都會遇到的問題,一個中間件做的再怎么分布式,怎么高可靠,都會遇到這個問題:
數據來了,寫磁盤,還是寫內存?
寫磁盤,嫌太慢?寫內存,又不安全?
MySQL 的解決方案是:既寫磁盤又寫內存。
數據寫內存,另外再往磁盤寫 redo log.
redo log 是什么
在執行上面這條 sql 語句時,MySQL 會判斷內存中有沒有 user_id = 345981 的數據,沒有,則去磁盤找到這條數據所在的「頁」,把整頁數據都加載到內存,然后找到 user_id = 345981 的 row 數據,把內存中這行數據的 age 設置為 18。
這時,內存的數據是新的、正確的,而磁盤數據是舊的、過時的,所以我們稱這時的磁盤對應的頁數據,為「臟頁」。
這里補充一個知識點:MySQL 是按頁為單位來讀取數據的,一個頁里面有很多行記錄,從內存刷數據到磁盤,也是以頁為單位來刷。
這時候如果掉電了,數據就沒了,于是 MySQL 把你對頁修改了什么內容,記錄了下來,保存到磁盤,也就是redo log。
寫完 redo log,MySQL 就認為事務提交成功了,數據持久化了(ACID的D),然后在空閑的時候,再把內存的數據刷到磁盤。
如果在內存數據刷到磁盤之前,MySQL 掉電了,怎么辦?
這時只需要在重啟后,把「臟頁」load 到內存,然后應用 redo log,臟頁就變成「干凈頁」了。
你會說,萬一我寫內存成功,但是把 redo log 寫到磁盤失敗了呢?這點后面我們在討論「兩階段提交」時再討論。
你還會說,redo log 還是要寫磁盤,那不還是很慢?
并不是,把 redo log 寫到磁盤,比一般的寫磁盤要快,原因有:
一般我們寫磁盤,都是「隨機寫」,而 redo log,是「順序寫」
MySQL 在寫 redo log 上做了優化,比如「組提交」
這些后面我們會陸續展開。
redo log 怎么存儲
MySQL 官方文檔有幾句話:
the redo log encodes requests to change table data that result from SQL statements or low-level API calls.
The redo log is a disk-based data structure used during crash recovery to correct data written by incomplete transactions.
By default, the redo log is physically represented on disk by two files named ib_logfile0 and ib_logfile1.
MySQL writes to the redo log files in a circular fashion.
從這幾句話,我們大致可以get到:
redo log 記錄了 sql 語句以及其他 api,對表數據產生的變化,也就是說,redo log 記錄的是數據的物理變化,這也是它和后面要講的 binlog 一個最大的區別,binlog 記錄的是數據的邏輯變化,這也是為什么 redo log 可以用來 crash recovery,而 binlog 不能的原因之一
redo log 是存儲在磁盤的,用于 crash recovery 后修正數據的,也就是我們常說的故障恢復,比如掉電,宕機等等
redo log 默認有兩個文件
redo log 是循環寫的(circular)
另外還有兩個參數:
innodb_log_file_size:設置每個 redo log 文件的大小,默認是 50331648 byte,也就是 48 MB
innodb_log_files_in_group:設置 redo log 文件的數量,默認是 2,最大值是 100
我們常說事務具有 ACID 四個特性,其中 D(durability),數據持久性,意味著,一旦事務提交,它的狀態就必須保持提交,不能回滾,哪怕你系統宕機了、奔潰了,你也要想辦法把事務做到提交,把數據給我保存進去:
Durability guarantees that once a transaction has been committed, it will remain committed even in the case of a system failure (e.g., power outage or crash).
這確實很嚴格,但看起來也很好實現,就是每次都把數據寫到磁盤,然后再告訴客戶端,事務提交成功了。
但如果我要追求高性能呢?我要把數據寫到內存呢?
所以我們說,innodb 在實現高性能寫數據的同時,利用 redo log,實現了事務的持久性。
binlog
講完了 redo log,我們再來聊聊 binlog。
還是這一條 update 語句:
update user set age = 18 where user_id = 345981
在這條 update 語句執行的時候,除了生成 redo log,還會生成 binlog。
binlog 和 redo log 有很多不同,有一點是一定要知道的,就是 redo log 只是 innodb 存儲引擎的功能,而 binlog 是 MySQL server 層的功能,也就是說,redo log 只在使用了 innodb 作為存儲引擎的 MySQL 上才有,而 binlog,只要你是 MySQL,就會有。
mysql 架構
binlog 里頭記錄了什么呢?上面有提到,和 redo log 記錄數據的物理變化不同,binlog 記錄的是數據的邏輯變化,比如上面這條 update 語句,你可以簡單的認為,binlog 里頭就是記錄了這樣一條 sql 語句,當然,它還會記錄當前是在哪個數據庫下的等等。
binlog 有三種格式,statement/row/mixed,有興趣同學可以深入了解下:Binary Logging Formats
binlog 有什么作用呢?
MySQL 之所以把 binlog 放在了 server 層,說明 binlog 提供了一些通用的能力,比如:數據還原。
DBA 總說,他能把 MySQL 的數據還原到任意時刻,怎么還原?
假設你在周三晚上八點的時候,不小心把一張表的數據都清空了,怎么辦?
這時候 DBA 就會找到最近的一次「全量備份」,然后重放從最近一次全量備份,到周三晚上八點,這段時間的 binlog,于是你的數據就還原回來了。
binlog 還有另一個作用:主從復制,主庫把 binlog 發給從庫,從庫把 binlog 保存了下來,然后去執行它,這樣就實現了主從同步。
當然,我們還能讓自己的業務應用,去監聽主庫的 binlog,當數據庫的數據發生變動時,去做特定的事情,比如進行數據實時統計。
兩階段提交
最后,那么當我執行一條 update 語句時,redo log 和 binlog 是在什么時候被寫入的呢?這就有了我們常說的「兩階段提交」:
寫入:redo log(prepare)
寫入:binlog
寫入:redo log(commit)
為什么 redo log 要分兩個階段: prepare 和 commit ?redo log 就不能一次寫入嗎?
我們分兩種情況討論:
先寫 redo log,再寫 binlog
先寫 binlog,再寫 redo log
1、先寫 redo log,再寫 binlog
這樣會出現 redo log 寫入到磁盤了,但是 binlog 還沒寫入磁盤,于是當發生 crash recovery 時,恢復后,主庫會應用 redo log,恢復數據,但是由于沒有 binlog,從庫就不會同步這些數據,主庫比從庫“新”,造成主從不一致
2、先寫 binlog,再寫 redo log
跟上一種情況類似,很容易知道,這樣會反過來,造成從庫比主庫“新”,也會造成主從不一致
而兩階段提交,就解決這個問題,crash recovery 時:
如果 redo log 已經 commit,那毫不猶豫的,把事務提交
如果 redo log 處于 prepare,則去判斷事務對應的 binlog 是不是完整的
是,則把事務提交
否,則事務回滾
兩階段提交,其實是為了保證 redo log 和 binlog 的邏輯一致性。
以上就是MySQL中怎么實現ACID,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。