從庫延遲案例分析

来源:https://www.cnblogs.com/greatsql/p/18130730
-Advertisement-
Play Games

背景介紹 近來一套業務系統,從庫一直處於延遲狀態,無法追上主庫,導致業務風險較大。從資源上看,從庫的CPU、IO、網路使用率較低,不存在伺服器壓力過高導致回放慢的情況;從庫開啟了並行回放;在從庫上執行show processlist看到沒有回放線程阻塞,回放一直在持續;解析relay-log日誌文件 ...


背景介紹

近來一套業務系統,從庫一直處於延遲狀態,無法追上主庫,導致業務風險較大。從資源上看,從庫的CPU、IO、網路使用率較低,不存在伺服器壓力過高導致回放慢的情況;從庫開啟了並行回放;在從庫上執行show processlist看到沒有回放線程阻塞,回放一直在持續;解析relay-log日誌文件,發現其中並沒大事務回放。

過程分析

現象確認

收到運維同事的反饋,有一套從庫延遲的非常厲害,提供了show slave status延遲的截圖信息

file

持續觀察了一陣show slave status的變化,發現pos點位信息在不停的變化,Seconds_Behind_master也是不停的變化的,總體趨勢還在不停的變大。

資源使用

觀察了伺服器資源使用情況,可以看到占用非常低

file

觀察從庫進程情況,基本上只能看到有一個線程在回放工作

file

並行回放參數說明

在主庫設置了binlog_transaction_dependency_tracking=WRITESET

在從庫設置了slave_parallel_type=LOGICAL_CLOCKslave_parallel_workers=64

error log日誌對比

從error log中取並行回放的日誌進行分析

$ grep 010559 100werror3306.log | tail -n 3
2024-01-31T14:07:50.172007+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3318582273; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348754579743300 waited (count) when Workers occupied = 34529247 waited when Workers occupied = 76847369713200

2024-01-31T14:09:50.078829+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3319256065; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348851330164000 waited (count) when Workers occupied = 34535857 waited when Workers occupied = 76866419841900

2024-01-31T14:11:50.060510+08:00 6806 [Note] [MY-010559] [Repl] Multi-threaded slave statistics for channel 'cluster': seconds elapsed = 120; events assigned = 3319894017; worker queues filled over overrun level = 207029; waite
d due a Worker queue full = 238; waited due the total size = 0; waited at clock conflicts = 348943740455400 waited (count) when Workers occupied = 34542790 waited when Workers occupied = 76890229805500

上述信息的詳細解釋,可以參考MTS性能監控你知道多少

去掉了發生次數比較少的統計,顯示了一些關鍵數據的對比

file

可以發現自然時間120,回放的協調線程有90多秒由於無法並行回放而進入等待,有近20秒是由於沒有空閑的work線程進入等待,折算下來協調線程工作的時間只有10秒左右。

並行度統計

眾所周知,mysql從庫並行回放主要依賴於binlog中的last_commmitted來做判斷,如果事務的last_committed相同,則基本上可以認為這些事務可以並行回放,下麵從環境中獲取一個relay log進行並行回放的大概統計

$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>=1 && $2 <11){sum+=$2}} END {print sum}' 
235703
$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>10){sum+=$2}} END {print sum}'
314694

上述第一條命令,是統計last_committed相同的事務數量在1-10個,即並行回放程度較低或者是無法並行回放,這些事務總數量為235703,占43%,詳細解析並行回放度比較低的事務分佈,可以看出這部分last_committed基本上都是單條的,都需要等待先序事務回放完成後,自己才能進行回放,這就會造成前面日誌中觀察到的協調線程等待無法並行回放而進入等待的時間比較長的情況

$ mysqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>=1 && $2 <11) {print $2}}' | sort | uniq -c
 200863 1
  17236 2
     98 3
     13 4
      3 5
      1 7

第二條命令統計last_committed相同的事務數量超過10個的總事務數,其數量為314694,占57%,詳細解析了這些並行回放度比較高的事務,可以看到每一組是在6500~9000個事務數間

$ mysqlsqlbinlog --no-defaults 046638 |grep -o 'last_committed.*' | sed 's/=/ /g' | awk '{print $2}' |sort -n | uniq -c |awk 'BEGIN {print "last_commited group_count Percentage"} {count[$2]=$1
; sum+=$1} END {for (i in count) printf "%d %d %.2f%%\n", i, count[i], (count[i]/sum)*100|"sort -k 1,1n"}' | awk '{if($2>11){print $0}}' | column -t
last_commited  group_count  Percentage
1              7340         1.33%
11938          7226         1.31%
23558          7249         1.32%
35248          6848         1.24%
46421          7720         1.40%
59128          7481         1.36%
70789          7598         1.38%
82474          6538         1.19%
93366          6988         1.27%
104628         7968         1.45%
116890         7190         1.31%
128034         6750         1.23%
138849         7513         1.37%
150522         6966         1.27%
161989         7972         1.45%
175599         8315         1.51%
189320         8235         1.50%
202845         8415         1.53%
218077         8690         1.58%
234248         8623         1.57%
249647         8551         1.55%
264860         8958         1.63%
280962         8900         1.62%
297724         8768         1.59%
313092         8620         1.57%
327972         9179         1.67%
344435         8416         1.53%
359580         8924         1.62%
375314         8160         1.48%
390564         9333         1.70%
407106         8637         1.57%
422777         8493         1.54%
438500         8046         1.46%
453607         8948         1.63%
470939         8553         1.55%
486706         8339         1.52%
503562         8385         1.52%
520179         8313         1.51%
535929         7546         1.37%

last_committed機制介紹

主庫的參數binlog_transaction_dependency_tracking用於指定如何生成其寫入二進位日誌的依賴信息,以幫助從庫確定哪些事務可以並行執行,即通過該參數控制last_committed的生成機制,參數可選值有COMMIT_ORDER、WRITESET、SESSION_WRITESET。
從下麵這段代碼,很容易看出來三種參數關係:

  1. 基礎演算法為COMMIT_ORDER
  2. WRITESET演算法是在COMMIT_ORDER基礎上再計算一次
  3. SESSION_WRITESET演算法是在WRITESET基礎上再計算一次

file

由於我的實例設置的是WRITESET,因此關註COMMIT_ORDER演算法和的WRITESET演算法即可。

COMMIT_ORDER

COMMIT_ORDER計算規則:如果兩個事務在主節點上是同時提交的,說明兩個事務的數據之間沒有衝突,那麼一定也是可以在從節點上並行執行的,理想中的典型案例如下麵的例子

session-1 session-2
BEGIN BEGIN
INSERT t1 values(1)
INSERT t2 values(2)
commit (group_commit) commit (group_commit)

但對於MySQL來說,group_commit是內部行為,只要session-1和session-2是同時執行commit,不管內部是否合併為group_commit,兩個事務的數據本質上都是沒有衝突的;再退一步來講,只要session-1執行commit之後,session-2沒有新的數據寫入,兩個事務依舊沒有數據衝突,依然可以並行複製。

session-1 session-2
BEGIN BEGIN
INSERT t1 values(1)
INSERT t2 values(2)
commit
commit

對於更多併發線程的場景,可能這些線程不能同時並行複製,但部分事務卻可以。以如下一個執行順序來說,在session-3提交之後,session-2沒有新的寫入,那麼這兩個事務是可以並行複製的;而session-3提交後,session-1又插入了一條新的數據,此時無法判定數據衝突,所以session-3和session-1的事務無法並行複製;但session-2提交後,session-1之後沒有新數據寫入,所以session-2和session-1又可以並行複製。因此,這個場景中,session-2分別可以和session-1,session-3並行複製,但3個事務無法同時並行複製。

session-1 session-2 session-3
BEGIN BEGIN BEGIN
INSERT t1 values(1) INSERT t2 values(1) INSERT t3 values(1)
INSERT t1 values(2) INSERT t2 values(2)
commit
INSERT t1 values(3)
commit
commit

WRITESET

實際上是commit_order+writeset的組合,會先通過commit_order計算出一個last_committed值,然後再通過writeset計算一個新值,最後取兩者間的小值作為最終事務gtid的last_committed。

在MySQL中,writeset本質上是對 schema_name + table_name + primary_key/unique_key 計算的hash值,在DML執行語句過程中,通過binlog_log_row生成row_event之前,會將DML語句中所有的主鍵/唯一鍵都單獨計算hash值,並加入到事務本身的writeset列表中。而如果存在無主鍵/唯一索引的表,還會對事務設置has_missing_keys=true。

參數設置為WRITESET,但是並不一定就能使用上,其限制如下

  1. 非DDL語句或者表具有主鍵或者唯一鍵或者空事務
  2. 當前session使用的hash演算法與hash map中的一致
  3. 未使用外鍵
  4. hash map的容量未超過binlog_transaction_dependency_history_size的設置
    以上4個條件均滿足時,則可以使用WRITESET演算法,如果有任意一個條件不滿足,則會退化為COMMIT_ORDER計算方式

file

具體WRITESET演算法如下,事務提交時:

  1. last_committed設置為m_writeset_history_start,此值為m_writeset_history列表中最小的sequence_number

  2. 遍歷事務的writeset列表

    a 如果某個writeset在全局m_writeset_history中不存在,構建一個pair<writeset, 當前事務的sequence_number>對象,插入到全局m_writeset_history列表中

    b. 如果存在,那麼last_committed=max(last_committed, 歷史writeset的sequence_number值),並同時更新m_writeset_history中該writeset對應的sequence_number為當前事務值

  3. 如果has_missing_keys=false,即事務所有數據表均包含主鍵或者唯一索引,則最後取commit_order和writeset兩種方式計算的最小值作為最終的last_committed值

file

TIPS:基於上面WRITESET規則,就會出現後提交的事務的last_committed比先提交的事務還小的情況

結論分析

結論描述

根據WRITESET的使用限制,對relay-log及事務中涉及到的表結構進行了對比,分析單last_committed的事務組成發現如下兩種情況:

  1. 單last_committed的事務中涉及到的數據和sequence_number存在數據衝突
  2. 單last_committed的事務中涉及到的表存在無主鍵的情況,而且這種事務特別多

從上面的分析中可以得出結論:無主鍵表的事務太多,導致WRITESET退化為COMMIT_ORDER,而由於資料庫為TP應用,事務都快速提交,多個事務提交無法保證在一個commit周期內,導致COMMIT_ORDER機制產生的last_committed重覆讀很低。從庫也就只能串列回放這些事務,引起回放延遲。

優化措施

  1. 從業務側對錶做改造,在允許的情況下給相關表都添加上主鍵。
  2. 嘗試調大參數binlog_group_commit_sync_delay、binlog_group_commit_sync_no_delay_count從0修改為10000,由於特殊環境限制,該調整並未生效,不同的場景可能會有不同的表現。

Enjoy GreatSQL

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 威聯通NAS VirtualizationStation 安裝ubuntu配置SSH遠程訪問,解決虛擬機記憶體分配和Linux SSH穿透後遠程連接的問題 ...
  • VS studio上查看標準cout輸出 網上的方法 在解決方案管理器中,單擊選中項目後,點擊菜單【視圖】->【屬性頁】 在生成事件->生成後事件->命令行(Build Events->Post-Build Event->Command) Line)中增加$(OutDir)$(ProjectName ...
  • 本文介紹筆記本電腦出現No Bootable Device錯誤提示,且無法開機的多種解決辦法。 1 問題產生 最近,筆記本電腦正在正常使用時,突然藍屏,出現你的設備遇到問題,需要重啟。的提示;最下方的終止代碼具體是CRITICAL_PROCESS_DIED還是SYSTEM_SERVICE_EXCEP ...
  • 大家好,我是呼嚕嚕,在上一篇文章聊聊x86電腦啟動發生的事?我們瞭解了x86電腦啟動過程,MBR、0x7c00是什麼?其中當bios引導結束後,操作系統接過電腦的控制權後,發生了哪些事?本文將揭開迷霧的序章-Bootsect.S 回顧電腦啟動過程 我們先來回顧一下,上古時期電腦按下電源鍵的 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是使能i.MXRT1050, 1060 Hab簽名或加密啟動時App鏈接在片內SRAM的限制。 最近有客戶反饋,在 RT1060 上測試 Non-XIP 程式啟動,如果程式體部分鏈接進 0x20280000 地址之後的片內 OCRAM 區 ...
  • 我的之前的博客《利用顯卡的SR-IOV虛擬GPU技術,實現一臺電腦當七台用》介紹了 Proxmox VE 7.x 上啟用核顯虛擬化的方法。 並給出了兩個腳本,快速啟用核顯的SR-IOV。該腳本在 Promox VE 7.x 和 8.x 都做了測試。 近期重新在 Proxmox VE 8.1 上部署, ...
  • 本次按照目前最新版本Sqlserver2022進行記錄 先決條件 任何受支持的 Linux 發行版上的 Docker 引擎 1.8 及更高版本。 有關詳細信息,請參閱 Install Docker(安裝 Docker)。 有關硬體要求和處理器支持的詳細信息,請參閱SQL Server 2022:硬體 ...
  • 點擊查看代碼 丐版sqlserver集群 之前試過docker的,k8s的,然後發現,還是最朴素的是最簡單的,希望有大佬能夠漢化,他媽的,那些英文看得人要發癲啊。 前置準備,參照丐版pxc集群: https://www.cnblogs.com/zwnfdswww/p/18112077 如果不關防火牆 ...
一周排行
    -Advertisement-
    Play Games
  • C#TMS系統代碼-基礎頁面BaseCity學習 本人純新手,剛進公司跟領導報道,我說我是java全棧,他問我會不會C#,我說大學學過,他說這個TMS系統就給你來管了。外包已經把代碼給我了,這幾天先把增刪改查的代碼背一下,說不定後面就要趕鴨子上架了 Service頁面 //using => impo ...
  • 委托與事件 委托 委托的定義 委托是C#中的一種類型,用於存儲對方法的引用。它允許將方法作為參數傳遞給其他方法,實現回調、事件處理和動態調用等功能。通俗來講,就是委托包含方法的記憶體地址,方法匹配與委托相同的簽名,因此通過使用正確的參數類型來調用方法。 委托的特性 引用方法:委托允許存儲對方法的引用, ...
  • 前言 這幾天閑來沒事看看ABP vNext的文檔和源碼,關於關於依賴註入(屬性註入)這塊兒產生了興趣。 我們都知道。Volo.ABP 依賴註入容器使用了第三方組件Autofac實現的。有三種註入方式,構造函數註入和方法註入和屬性註入。 ABP的屬性註入原則參考如下: 這時候我就開始疑惑了,因為我知道 ...
  • C#TMS系統代碼-業務頁面ShippingNotice學習 學一個業務頁面,ok,領導開完會就被裁掉了,很突然啊,他收拾東西的時候我還以為他要旅游提前請假了,還在尋思為什麼回家連自己買的幾箱飲料都要叫跑腿帶走,怕被偷嗎?還好我在他開會之前拿了兩瓶芬達 感覺感覺前面的BaseCity差不太多,這邊的 ...
  • 概述:在C#中,通過`Expression`類、`AndAlso`和`OrElse`方法可組合兩個`Expression<Func<T, bool>>`,實現多條件動態查詢。通過創建表達式樹,可輕鬆構建複雜的查詢條件。 在C#中,可以使用AndAlso和OrElse方法組合兩個Expression< ...
  • 閑來無聊在我的Biwen.QuickApi中實現一下極簡的事件匯流排,其實代碼還是蠻簡單的,對於初學者可能有些幫助 就貼出來,有什麼不足的地方也歡迎板磚交流~ 首先定義一個事件約定的空介面 public interface IEvent{} 然後定義事件訂閱者介面 public interface I ...
  • 1. 案例 成某三甲醫預約系統, 該項目在2024年初進行上線測試,在正常運行了兩天後,業務系統報錯:The connection pool has been exhausted, either raise MaxPoolSize (currently 800) or Timeout (curren ...
  • 背景 我們有些工具在 Web 版中已經有了很好的實踐,而在 WPF 中重新開發也是一種費時費力的操作,那麼直接集成則是最省事省力的方法了。 思路解釋 為什麼要使用 WPF?莫問為什麼,老 C# 開發的堅持,另外因為 Windows 上已經裝了 Webview2/edge 整體打包比 electron ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...