MySQL更新數據時,日志(redo log、binlog)執行流程


1:背景

項目需要做Es和數據庫的同步,而手動在代碼中進行數據同步又是Es的一些不必要的數據同步操作和業務邏輯耦合,所以使用的了讀取mysql的binlog日志的方式進行同步Es的數據。

問題1:根據binlog同步數據的時候會不會出現業務邏輯利用事務操作數據的時候,當事務還沒有提交的時候,是否能夠讀到binlog,也就是binlog的寫入時機(是事務提交的之前寫,還是事務提交后寫)。
問題2:如果事務提交之前寫入binlog,那么事務提交之前,事務回滾,那么binlog又會出現什么情況?

首先我們做一下實驗(前提是必須要打開binlog),SQL初始化語句

create table user
(
  id     bigint     not null auto_increment
      primary key,
  name   varchar(64) null,
  status tinyint(1) null
);
insert into user (name,status)values ('張三',1);
set autocommit = 0;

 

2.1:事務提交對binlog的影響

我們先確定一下MySQL執行更新SQL語句之后,執行commit命令前后,binlog會有什么變化。

然后我們先使用 show master status 看一下binlog的位置

 

 

然后我們執行一下更新語句

update user set status = status + 1;

再次使用 show master status 查看發現並沒有Position並沒有變,執行commit命令之后,發現Position由141169變為了141506,說明從庫只有在主庫提交之后才能讀到主庫寫入的binlog日志。

 

 

2.2:事務回滾對binlog的影響

我們再確定一下MySQL執行更新SQL語句之后,執行rollback命令前后,binlog會有什么變化。

然后我們先使用 show master status 看一下binlog的位置

 

 

然后我們執行一下更新語句

update user set status = status + 1;

再次使用 show master status 查看發現並沒有Position並沒有變,執行rollback命令之后,發現Position仍然沒有變化,說明事務回滾之后binlog並不會寫入磁盤。

 

 

 

難道binlog是在事務提交之后才寫入磁盤的嘛?那redo log 又是什么時候寫入磁盤的呢?有上面的問題又引發一系列的問題,帶着這些問題,我們來進行mysql日志的深入學習。

3:MySQL更新數據的執行流程

首先我們要先了解一下當我們做一條數據的更新操作的時候,數據庫的底層到底是如何執行的?

MySQL更新數據執行流程:

1:判斷數據頁是否在內存中,若為否,則從磁盤讀取數據到內存中,返回數據行
2:若是數據頁在內存中,則直接返回數據行
3:執行數據更新操作
4:數據寫入內存,同時redolog寫入到內存
5:執行commit操作(此commit是SQL命令操作,而不是數據的commit狀態)
6:執行commit命令之后,則進行兩段提交操作。
6.1:寫入內存中的redolog到磁盤中,此時redolog處於prepare狀態
6.2:寫入binlog到磁盤
6.3:提交事務,此時事務處於commit狀態
7:結束。

注:以上操作為參數innodb_flush_log_at_trx_commit 為1和sync_binlog為1的時候。

對應MySQL的更新語句執行流程圖,如圖1-1。

 

 

redo log:被稱之為重做日志,是在數據庫發生意外時,進行數據恢復,redo log會備份是事務執行過程中的修改數據。

binlog: 被稱為歸檔日志,是一個二進制格式的文件,用於記錄用戶對數據庫更新的SQL語句信息,格式分為(statement、row、mixed)

redo log 和binlog的差異如下表:

redo log binlog
InnoDB引擎 MySQL Serve
物理日志 邏輯日志
循環寫入 追加寫入

MySQL執行commit命令之后是使用兩段提交的辦法來保證事物的原子行的,至於為什么使用兩段提交,而不是其他的提交方式,由於篇幅有限,不做多余解釋,請參考自行查詢資料。

到目前為止,關於上面binlog的問題也就迎刃而解了,

對於問題一:MySQL中binlog在事務提交之前會寫入redo log和binlog到內存中,在執行commit命令之后進行兩段提交操作,將redo log和binlog寫入磁盤,因此在事務提交之前不能讀取到binlog日志(前提binlog沒有進行被動刷盤)。

對於問題二:binlog是在執行commit命令之后進行的刷盤,但是是在事務在commit狀態之前寫入的磁盤。根據上面的實驗可以看出,事務回滾對於binlog並沒有什么影響。

小結:在執行commit命令前,執行更新數據到內存之后,那么就會寫入redo log和binlog到 redo log buffer和binlog buffer中,當執行了commit命令之后,就是進行兩段提交操作,然后進行redo log和binlog寫入磁盤操作。

關於MySQL的日志刷盤機制是由參數innodb_flush_log_at_trx_commit 和sync_binlog控制的,具體請參考下一篇文章。

 

4:commit命令和commit狀態區別解釋

我們上面說的commit命令是指MySQL語法中的commit命令,用於提交事務,一般跟 begin/start transaction 配對使用。

而我們圖中用到的這個“commit 步驟”,指的是事務提交過程中的一個小步驟,也是最后一步。當這個步驟執行完成后,這個事務就提交完成了。

“commit 命令”執行的時候,會包含“commit 步驟”。

 


免責聲明!

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



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