你知道MySQL的原子性是怎麼保證的嗎?
誰都知道在事務裡邊原子性的意思:”一個事務包含多個操作,這些操作要麼全部執行,要麼全都不執行“
事務保證需要用到一下幾個常見 log
-
undo log
-
binlog
-
redo log
一、什麼是binlog
binlog
其實在日常的開發中是聽得很多的,因為很多時候資料的更新就依賴着
binlog
。
舉個很簡單的例子:我們的資料是儲存在資料庫裡邊的嘛,現在我們對某個商品的某個字段的内容改了(資料庫變更),而使用者檢索的出來資料是走搜尋引擎的。為了讓使用者能搜到最新的資料,我們需要把引擎的資料也改掉。
一句話:資料庫的變更,搜尋引擎的資料也需要變更。
于是,我們就會監聽
binlog
的變更,如果
binlog
有變更了,那我們就需要将變更寫到對應的資料源。
什麼是 binlog
?
binlog
記錄了資料庫表結構和表資料變更,比如
update/delete/insert/truncate/create
。它不會記錄
select
(因為這沒有對表沒有進行變更)
binlog
長什麼樣?
binlog
我們可以簡單了解為:存儲着每條變更的
SQL
語句(當然從下面的圖看來看,不止SQL,還有XID「事務Id」等等)

binlog
一般用來做什麼
主要有兩個作用:複制和恢複資料
- MySQL在公司使用的時候往往都是一主多從結構的,從伺服器需要與主伺服器的資料保持一緻,這就是通過
來實作的binlog
- 資料庫的資料被幹掉了,我們可以通過
來對資料進行恢複。binlog
因為
binlog
記錄了資料庫表的變更,是以我們可以用
binlog
進行複制(主從複制)和恢複資料。
二、什麼是redo log
假設我們有一條sql語句:
update user_table set name='java3y' where id = '3'
MySQL執行這條SQL語句,肯定是先把
id=3
的這條記錄查出來,然後将
name
字段給改掉。這沒問題吧?
實際上Mysql的基本存儲結構是頁(記錄都存在頁裡邊),是以MySQL是先把這條記錄所在的頁找到,然後把該頁加載到記憶體中,将對應記錄進行修改。
現在就可能存在一個問題:如果在記憶體中把資料改了,還沒來得及落磁盤,而此時的資料庫挂了怎麼辦?顯然這次更改就丢了。
如果每個請求都需要将資料立馬落磁盤之後,那速度會很慢,MySQL可能也頂不住。是以MySQL是怎麼做的呢?
MySQL引入了
redo log
,記憶體寫完了,然後會寫一份
redo log
,這份
redo log
記載着這次在某個頁上做了什麼修改。
其實寫
redo log
的時候,也會有
buffer
,是先寫
buffer
,再真正落到磁盤中的。至于從
buffer
什麼時候落磁盤,會有配置供我們配置。
寫
redo log
也是需要寫磁盤的,但它的好處就是
順序IO
(我們都知道順序IO比随機IO快非常多)。
是以,
redo log
的存在為了:當我們修改的時候,寫完記憶體了,但資料還沒真正寫到磁盤的時候。此時我們的資料庫挂了,我們可以根據
redo log
來對資料進行恢複。因為
redo log
是順序IO,是以寫入的速度很快,并且
redo log
記載的是實體變化(xxxx頁做了xxx修改),檔案的體積很小,恢複速度很快。
三、binlog和redo log
看到這裡,你可能會想:
binlog
和
redo log
這倆也太像了吧,都是用作”恢複“的。
其實他倆除了"恢複"這塊是相似的,很多都不一樣,下面看我列一下。
存儲的内容
binlog
記載的是
update/delete/insert
這樣的SQL語句,而
redo log
記載的是實體修改的内容(xxxx頁修改了xxx)。
是以在搜尋資料的時候會有這樣的說法:
redo log
記錄的是資料的實體變化,
binlog
記錄的是資料的邏輯變化
功能
redo log
的作用是為持久化而生的。寫完記憶體,如果資料庫挂了,那我們可以通過
redo log
來恢複記憶體還沒來得及刷到磁盤的資料,将
redo log
加載到記憶體裡邊,那記憶體就能恢複到挂掉之前的資料了。
binlog
的作用是複制和恢複而生的。
- 主從伺服器需要保持資料的一緻性,通過
來同步資料。binlog
- 如果整個資料庫的資料都被删除了,
存儲着所有的資料變更情況,那麼可以通過binlog
binlog
又看到這裡,你會想:”如果整個資料庫的資料都被删除了,那我可以用
redo log
的記錄來恢複嗎?“不能
因為功能的不同,
redo log
存儲的是實體資料的變更,如果我們記憶體的資料已經刷到了磁盤了,那
redo log
的資料就無效了。是以
redo log
不會存儲着曆史所有資料的變更,檔案的内容會被覆寫的。
binlog和redo log 寫入的細節
redo log
是MySQL的InnoDB引擎所産生的。
binlog
無論MySQL用什麼引擎,都會有的。
InnoDB是有事務的,事務的四大特性之一:持久性就是靠
redo log
來實作的(如果寫入記憶體成功,但資料還沒真正刷到磁盤,如果此時的資料庫挂了,我們可以靠
redo log
來恢複記憶體的資料,這就實作了持久性)。
上面也提到,在修改的資料的時候,
binlog
會記載着變更的類容,
redo log
也會記載着變更的内容。(隻不過一個存儲的是實體變化,一個存儲的是邏輯變化)。那他們的寫入順序是什麼樣的呢?
redo log
事務開始的時候,就開始記錄每次的變更資訊,而
binlog
是在事務送出的時候才記錄。
于是新有的問題又出現了:我寫其中的某一個
log
,失敗了,那會怎麼辦?現在我們的前提是先寫
redo log
,再寫
binlog
,我們來看看:
- 如果寫
失敗了,那我們就認為這次事務有問題,復原,不再寫redo log
binlog
-
成功了,寫redo log
,寫binlog
寫一半了,但失敗了怎麼辦?我們還是會對這次的事務復原,将無效的binlog
給删除(因為binlog
會影響從庫的資料,是以需要做删除操作)binlog
-
redo log
都成功了,那這次算是事務才會真正成功。binlog
簡單來說:MySQL需要保證
redo log
binlog
的資料是一緻的,如果不一緻,那就亂套了。
- 如果
寫失敗了,而redo log
寫成功了。那假設記憶體的資料還沒來得及落磁盤,機器就挂掉了。那主從伺服器的資料就不一緻了。(從伺服器通過binlog
得到最新的資料,而主伺服器由于binlog
沒有記載,沒法恢複資料)redo log
-
寫成功了,而redo log
寫失敗了。那從伺服器就拿不到最新的資料了。binlog
MySQL通過兩階段送出來保證
redo log
binlog
的資料是一緻的。
過程:
- 階段1:InnoDB
寫盤,InnoDB 事務進入redo log
狀态prepare
- 階段2:
寫盤,InooDB 事務進入binlog
commit
- 每個事務
的末尾,會記錄一個binlog
,标志着事務是否送出成功,也就是說,恢複過程中,XID event
最後一個 XID event 之後的内容都應該被 purge。binlog
四、什麼是undo log
undo log
有什麼用?
undo log
主要有兩個作用:復原和多版本控制(MVCC)
在資料修改的時候,不僅記錄了
redo log
,還記錄
undo log
,如果因為某些原因導緻事務失敗或復原了,可以用
undo log
進行復原
undo log
主要存儲的也是邏輯日志,比如我們要
insert
一條資料了,那
undo log
會記錄的一條對應的
delete
日志。我們要
update
一條記錄時,它會記錄一條對應相反的update記錄。
這也應該容易了解,畢竟復原嘛,跟需要修改的操作相反就好,這樣就能達到復原的目的。因為支援復原操作,是以我們就能保證:“一個事務包含多個操作,這些操作要麼全部執行,要麼全都不執行”。【原子性】
undo log
存儲着修改之前的資料,相當于一個前版本,MVCC實作的是讀寫不阻塞,讀的時候隻要傳回前一個版本的資料就行了。
最後
這篇文章把
binlog
/
redo log
/
undo log
最核心的知識給講了,還有一些細節性的東西可以自行去補充(比如
binlog
有幾種的模式,以及文章提到的刷盤政策等等)
參考資料:
- https://www.jianshu.com/p/4bcfffb27ed5
- https://yq.aliyun.com/articles/617335
- MySQL的ACID原理!
- MySQL 是如何實作 ACID 中的 D 的?
- https://www.cnblogs.com/myseries/p/10728533.html