MySQL:雙主單寫 主庫偶爾出現大量延遲的原因
版本:5.7.29
水平有限有誤請諒解
一、問題來源
這是來自我們小米消金線上資料庫的一個問題。我們是雙主單寫,這裡約定寫入的庫為主庫,沒有寫入的庫為從庫。我們的falcon偶爾會進行報警如下(頻率很低):
這是非常奇怪的,按理說我是單寫的從庫沒有做任何操作(除了應用Event以外),主庫哪來的延遲,並且延遲這麼大。在我映像中有朋友問過這個問題,當時沒有細細研究。
二、延遲計算的規則
我們還是要看看主從計算延遲的虛擬碼:
/*
The pseudo code to compute Seconds_Behind_Master:
if (SQL thread is running)
//如果SQL執行緒啟動了
{
if (SQL thread processed all the available relay log)
//如果SQL執行緒已經應用完了所有的IO執行緒寫入的Event
{
if (IO thread is running)
//如果IO執行緒啟動了
print 0;
//設定延遲為0
else
print NULL;
//否則為空值
}
else
compute Seconds_Behind_Master;
//如果SQL執行緒沒有應用完所有的IO執行緒寫入的Event,那麼需要計算延遲。
}
else
print NULL;
//如果連SQL執行緒也沒有啟動則設定為空值
*/
計算延遲的公式為:
long time_diff= ((long)(time(0)
- mi->rli->last_master_timestamp)
- mi->clock_diff_with_master);
也就是:
伺服器當前時間-Event header中的timestamp - 主從伺服器時間差
出現延遲的必要條件:
-
如果SQL執行緒沒有應用完了所有的IO執行緒寫入的Event,也就是Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值。判定標準為
(mi->get_master_log_pos() == mi->rli->get_group_master_log_pos()) && (!strcmp(mi->get_master_log_name(), mi->rli->get_group_master_log_name()))
拋開檔名,也就是通過 IO執行緒讀取到主庫binary log的位置 和 SQL執行緒應用到的主庫binary log位置進行比較來進行 判斷,只要他們出現差值就會進入延遲計算環節。
-
伺服器當前時間-Event header中的timestamp - 主從伺服器時間差 這個公式必須出現差值。
好了接下來帶著這兩個產生延遲的必要條件來尋求原因。
關於更多延遲計算的細節參考:
https://www.jianshu.com/p/033f83314619
三、產生延遲的原因
1.主庫:首先主庫寫到從庫的Event,從庫會寫入到binlog(log_slave_updates 開啟),並且從庫的DUMP執行緒會傳送給主庫,但是主庫的IO執行緒通過SERVER_ID程式判定,將Event進行過濾,不寫入主庫的relay log,同時會更新主庫IO執行緒讀取的位置(Read_Master_Log_Pos),並且更新忽略到的位置(rli->ign_master_log_name_end[0])。程式碼如下:
if (!(s_id == ::server_id && !mi->rli->replicate_same_server_id) ||
(event_type != binary_log::FORMAT_DESCRIPTION_EVENT &&
event_type != binary_log::ROTATE_EVENT &&
event_type != binary_log::STOP_EVENT))
{
mi->set_master_log_pos(mi->get_master_log_pos() + inc_pos);//增加Read_Master_Log_Pos位點,為當前位置
memcpy(rli->ign_master_log_name_end, mi->get_master_log_name(), FN_REFLEN); //進行拷貝
DBUG_ASSERT(rli->ign_master_log_name_end[0]); //斷言存在
rli->ign_master_log_pos_end= mi->get_master_log_pos(); //忽略到位點
}
- 主庫:SQL執行緒會通過rli->ign_master_log_name_end[0]判定是否有需要跳過的Event,如果有則構建一個Rotate_log_event來跳過這個Event,程式碼如下:
if (rli->ign_master_log_name_end[0]) //如果跳過的Event存在
{
/* We generate and return a Rotate, to make our positions advance */
DBUG_PRINT("info",("seeing an ignored end segment"));
ev= new Rotate_log_event(rli->ign_master_log_name_end,
0, rli->ign_master_log_pos_end, exec_relay_log_event
Rotate_log_event::DUP_NAME); //構建一個Rotate Event,位置為
rli->ign_master_log_name_end[0]= 0; //rli->ign_master_log_pos_end,執行這個Event就可以
mysql_mutex_unlock(log_lock);exec_relay_log_event //來更新Exec_Master_Log_Pos位點
if (unlikely(!ev))
{
errmsg= "Slave SQL thread failed to create a Rotate event "
"(out of memory?), SHOW SLAVE STATUS may be inaccurate";
goto err;
}
ev->server_id= 0; // don't be ignored by slave SQL thread
DBUG_RETURN(ev);
}
好了到這裡我們知道了Event在主庫是如何跳過的,但是注意IO執行緒和SQL執行緒在處理Read_Master_Log_Pos和Exec_Master_Log_Pos的時候可能有一定的時間差,那麼Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值 的條件就可能會滿足,則進入延遲計算環節。
- 主庫的SQL執行緒平時並沒有讀取到Event,因為所有的Event都被IO執行緒過濾掉了。因此
Event的 header中的timestamp 不會更新(MTS)。但是如果從庫binlog切換的時候,從庫至少會傳送ROTATE_EVENT給主庫,這個時候主庫會拿到這個實際的Event,因此Event的 header中的timestamp 更新了。 如果剛好遇到主庫的IO執行緒的Read_Master_Log_Pos和Exec_Master_Log_Pos有差值,
那麼falcon去檢視延遲就會得到一個延遲很大的假象,延遲的計算公式就會變為如下:
- 主庫當前的時候 - 從庫上次binlog切換的時間 - 主從時間的差值
- MTS和單執行緒的不同
上面的第3點只適用於MTS,單SQL執行緒不同,會去將last_master_timestamp設定為0,程式碼如下:
if (!rli->is_parallel_exec())
rli->last_master_timestamp= 0;
言外之意單SQL執行緒計算延遲的公式為:
- 主庫當前的時間 - 1970年1月1日0點 - 主從時間的差值
因此看起來計算出來的延遲會更大。
- 最後需要注意的是實際上這種情況的延遲並沒有問題,完全是一種偶爾出現的計算上的問題,是一種假象,如果主庫的壓力越大出現這種情況的可能性就會越大,因為IO執行緒和SQL執行緒在處理Read_Master_Log_Pos和Exec_Master_Log_Pos的出現時間差的可能性就會越大。
四、MTS下的延遲debug
其實知道了原理就很容易debug了,因為我們可以將斷點放到主庫的show_slave_status_send_data函式上,那麼就能看出來了,做的操作如下:
- 從庫flush binary logs
- 主庫執行一些insert操作
- 主庫show slave status
這個時候我們可以跳過(Read_Master_Log_Pos和Exec_Master_Log_Pos存在一定的差值)這個條件,直接通過公式去計算,得到如下結果:
(gdb) p (long)(time(0)- mi->rli->last_master_timestamp)- mi->clock_diff_with_master
$6 = 37
延遲就是37秒,因此我們的理論得到了驗證。
下面一個debug結果是單SQL執行緒的,可以看到延遲更是大得離譜。
(gdb) p (long)(time(0)- mi->rli->last_master_timestamp)- mi->clock_diff_with_master
$7 = 1592672402
五、其他問題
額外的問題:
- 如果雙主雙寫
S1 | S2 |
---|---|
T1 | |
T2 | |
T3 |
如果按照上面的理論那麼T3的更新的位置可能會被,T2事務的位點重置。因為主庫的SQL執行緒通過構建的Rotate_log_event可能會出現Exec_Master_Log_Pos倒退的可能性,這顯然是不行的。但是程式碼中構建Rotate_log_event的邏輯包裹在如下邏輯下面。
if (!cur_log->error) /* EOF */ //當前relay log 已經讀取完了
{
/*
On a hot log, EOF means that there are no more updates to
process and we must block until I/O thread adds some and
signals us to continue
*/
if (hot_log) //如果是 當前relay log
我們可以看到只有在當前 relay log讀取完成後才會進行Rotate_log_event的構建。因此不存在此問題。
- 問題如上雖然不構建Rotate_log_event,但是如果rli->ign_master_log_name_end[0]如果一直保留那麼當relay log應用完成後,依舊會去構建Rotate_log_event導致Exec_Master_Log_Pos倒退,實際上這個問題也不會出現,因為在每次IO執行緒Event寫入到relay log後會重置,如下:
rli->ign_master_log_name_end[0]= 0; // last event is not ignored
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2699698/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySQL主從複製延遲原因及處理思路MySql
- mysql主從延遲複製MySql
- 如何避免MYSQL主從延遲帶來的讀寫問題?MySql
- Mysql 從庫如果有未提交的事務主庫ddl操作導致主從延遲MySql
- MySQL主從複製延遲解決方案MySql
- MySQL5.6升級5.7時,出現主從延遲問題排查過程MySql
- MySQL主從資料庫同步延遲問題怎麼解決MySql資料庫
- 主庫千萬級的資料更新後,STANDBY日誌應用大量延遲的問題處理
- 主從延遲調優思路
- 在Linux中,mysql 如何減少主從複製延遲?LinuxMySql
- Mysql實現主從複製(一主雙從)MySql
- mysql之 誤用SECONDS_BEHIND_MASTER衡量MYSQL主備的延遲時間MySqlAST
- MySQL 延遲從庫介紹MySql
- MySQL雙主雙從配置MySql
- mysql同步(複製)延遲的原因及解決方案MySql
- 面試官:我們們來聊一聊mysql主從延遲面試MySql
- MySQL運維16-雙主雙從讀寫分離MySql運維
- mysql雙主雙從 搭建配置MySql
- Mysql 非同步複製延遲的原因及解決方案MySql非同步
- mysql的主從複製延遲問題--看這一篇就夠了MySql
- 主從複製延遲推薦解決方案
- centos7上配置mysql8的雙主互寫CentOSMySql
- 【MySQL】六、常見slave 延遲原因以及解決方法MySql
- 【mysql】mysql的資料庫主從(一主一從)MySql資料庫
- MySQL 中讀寫分離資料延遲MySql
- 實現簡單延遲佇列和分散式延遲佇列佇列分散式
- 延時 (遲) 操作的 PHP 簡單實現PHP
- Linux實現MySql資料庫的主從複製(一主一從)LinuxMySql資料庫
- MySQL 缺少主鍵的表的效能下降的原因MySql
- dataguard主備延遲多長時間的2種查詢方法
- 伺服器延遲高的幾個原因伺服器
- 實時重新整理快取-處理mysql主從延遲的一些設計方案快取MySql
- mysql資料庫的主從複製和主主複製實踐MySql資料庫
- MacOS使用Docker建立MySQL主主資料庫MacDockerMySql資料庫
- mysql資料庫實現主從複製MySql資料庫
- MYSQL 主從不一致的原因分析MySql
- mysql雙寫造成主從資料不一致的實驗MySql
- 搭建MySQL主從實現Django讀寫分離MySqlDjango