MySQL 5.7中sync
來自:
gaopengtttt,作者:高鵬(重慶八怪)
鏈接:https://www.jianshu.com/p/59c6ecb46fe5
本文為我的一些零散記錄供以後參考,本來知道已經很久了但是有朋友問到老是需要翻很久,這裡乾脆記錄下來,但是水平有限都不深入,如有誤導請見諒為什麼將他們放在一起討論因為他們都存在於同一個函數MYSQL_BIN_LOG::ordered_commit函數中。
代碼版本:percona 5.7.14
以下討論sync_binlog參數在5.7中的作用
一、sync_binlog參數設置在源碼中的表示
這個參數大家都知道控制著binlog的刷盤時機,但是在5.7中其還有另外一個功能,我這裡將解釋他的兩個功能。我摘取了源碼中說明問題的部分進行展示如下:
flush階段:
flush_error= process_flush_stage_queue(&total;_bytes, &do;_rotate,&wait;_queue); //進行binlog的從binlog buffer或者臨時文件寫入到binlog文件(注意是寫到kernel buffer還沒做fsync),同時觸發innodb的組提交邏輯,innodb組提交的邏輯代碼是阿里的印風兄寫的,我請教過他。 1 //sync_binlog參數 如果為1則為真如果不為1則為假 if //如果sync_binlog=1則 這裡不發信號給dump 如果不是1則發信號進行dump
其中get_sync_period()函數返回就是sync_binlog的設置,這裡能夠清晰看到如果sync_binlog != 1才會 在flush階段發送信號給dump線程。
sync階段
if 0 0 //這裡進行sync binlog, "before_sync_binlog_file" std bool bool
> result= sync_binlog_file(
false
); sync_error= result.first; }if
(update_binlog_end_pos_after_sync)//如果sync_binlog = 1 這裡才發送信號給dump線程通知進行發送binlog
{ THD *tmp_thd= final_queue;while
(tmp_thd->next_to_commit !=NULL
) tmp_thd= tmp_thd->next_to_commit;if
(flush_error ==0
&& sync_error ==0
) update_binlog_end_pos(tmp_thd->get_trans_pos()); }如果sync_binlog = 1 這裡才發送信號給dump線程通知進行發送binlog。
同時如果我們翻開sync_binlog_file函數的邏輯會發現這樣一個邏輯:
if || 0
很顯然這裡有一個計數器sync_counter,如果當sync_binlog>1的時候才,等到sync_counter大於你設置的sync_binlog的值的時候才會觸發fsync binlog(注意這裡是++sync_counter 先自增再比較),這裡也解釋了sync_binlog>1的時候代表的是什麼值,代表是組提交的次數。
二、sync_binlog參數在5.7中作用的總結
sync_binlog=0:binlog從不FSYNC刷盤,依賴於OS刷盤機制,同時dump線程會在flush階段後進行binlog傳輸
sync_binlog=1:binlog每次組提交進行FSYNC刷盤,同時dump線程會在sync階段後進行binlog傳輸
sync_binlog>1:binlog將在指定次數組提交後FSYNC刷盤,同時dump線程會在flush階段後進行binlog傳輸
三、為什麼這麼修改
這也是一個朋友問我的問題,如果主庫異常重啟後,從庫是否有比主庫多事物的風險,實際上這個問題就是到底在什麼階段後dump線程進行傳輸binlog的問題。實際上如果在flush階段過後傳輸確實可能出現這個問題,而在sync階段後傳輸這個時候binlog已經落盤了,就不會有這種風險了。如果出現這種錯誤會報錯如下,這個錯誤也是有朋友遇到過的:
ER_SLAVE_HAS_MORE_GTIDS_THAN_MASTER "Slave has more GTIDs than the master has, using the master"s SERVER_UUID. This may indicate that the end of binary log or last binary log
file
was lost, e.g.,after
apower
or
diskfailure
when
sync_binlog !=1.
Themaster
mayor
maynot
have rolled back transactions that were already replicated
to
the slave. Suggestto
replicate
any
transactions thatmaster
has rolled backfrom
slave
to
master
,and
/
or
commit
empty
transactionson
master
to
account
for
transactions that have been committedon
master
but
are
not
includedin
GTID_EXECUTED."
下面開始討論半同步中after_commit和after_sync的區別,這個問題一直也是大家討論的重點。
四、從一個問題出發討論
也是有很多朋友問我,其中一個問題如下:
半同步:after_sync模式測試:超時時間設置為 1 end
問為什麼其他session狀態不是等待ACK而是query end。如果是after_commit模式則全部是等待ACK狀態
實際上拿到這位朋友的pstack後大概就能確認大概是什麼問題如下:
等待ACK線程:
Thread 7 0 24897 #0 0 in 3.2 0 #1 0 in const local #2 0 in const #3 0 in #4 0 in #5 0 in #6 0 in #7 0 in #8 0 in
等待LOCK_commit mutex線程
Thread 6 0 25017 #0 0 in 0 #1 0 in 0 #2 0 in 0 #3 0 in #4 0 in #5 0 in #6 0 in #7 0 in
這裡就很明顯其他提交事物(這裡指的是 Thread 6)堵塞在了MYSQL_BIN_LOG::change_stage函數上,其作用正是獲取某個階段的Mutex。而本線程(這裡指的是 Thread 7)則是在ReplSemiSyncMaster::commitTrx上堵塞在某個Mutex上。
五、after_commit和after_sync的代碼位置和區別
這裡直接用代碼說明進行給出,當然我只是提取了說明問題的代碼片段:
commit階段:
1、 change_stage(thd, Stage_manager::COMMIT_STAGE,final_queue, leave_mutex_before_commit_stage,&LOCK;_commit))//持有LOCK_commit mutext進入 commit 2 commit 3 flush order commit 6 void commit
如果拋開代碼總結如下:
1、leader 持有LOCK_commit 鎖 進入 commit階段。
2、如果是設置after_sync,使用after sync 掛鉤來確認ack 。
3、進行引擎層提交,完成後解鎖LOCK_commit 鎖。
4、喚醒所有 follwer線程。
5、如果設置是after_commit,使用after commit 掛鉤來確認ack 。
這裡我們可以清楚的看到,他們的區別,實際上正如其名字一樣就是說到底在那個步驟進行日誌傳輸完成的確認,是在實際引擎層提交之前還是之後,如果是在之前則在mutex LOCK_commit的保護下,如果是在之後則不需要持有LOCK_commit mutex,這也是為什麼會出現上面那個堵塞案例的原因。在5.7中默認是after_sync設置為after_sync後顯然更加安全,如果是after_commit極端情況下可能引擎層已經提交完成,事物對主庫可見,但是從庫還沒有傳輸完成如果從庫奔潰可能出現少事物的情況。
結語
對於5.7中安全的設置應該盡量保證sync_binlog=1同時設置rpl_semi_sync_master_wait_point為after_sync,這實際上都是默認設置。
●編號403,輸入編號直達本文
●輸入m獲取文章
目錄
推薦↓↓↓
Web開發
更多推薦
《
18個技術類微信公眾號
》
涵蓋:程序人生、演算法與數據結構、黑客技術與網路安全、大數據技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。
推薦閱讀:
※黃河之水天上來,數據之源資料庫——SQL
※mac安裝mysql的兩種方法(含配置)
※mysql資料庫基礎操作大全(小白必看)
※MySQL 並行複製演進及 MySQL 8.0 中基於 WriteSet 的優化
※InnoDB 存儲引擎原理解析
TAG:MySQL |