項目需要做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 步驟”。