天天看點

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

現象

某個資料庫經常在某個時間點,比如淩晨 2 點或者白天某些時間段發出如下報警:

[Critical][prod][mysql] - 超200 kill SQL/分鐘[P0][PROBLEM][all(#2) db_data.Com_kill db=XXXX[m]:3306 10.53333>=3.3][O1 2019-11-01 03:40:00]
           

報警的意思是每分鐘超過 200 個 SQL 被 kill,是一個嚴重告警級别,會打電話給 DBA。大半夜報警的确令人不爽,那麼如何解決這個問題呢?

通過檢查日志,我們發現被 kill 的 SQL 都是 delete 語句。業務方其實會定時的跑删除任務,這個任務涉及到 N 多個表,删除任務持續時間比較長,是以白天和晚上都有一定機率會觸發 sql-killer,然後報警。

在有贊的資料庫運維體系中,每個執行個體都會配置一個 sql-killer 的實時工具,用于 kill query 超過指定門檻值的 SQL 請求(類似 pt-killer)。

初步分析

在之前的案例分析過程中,碰到過因為長事務導緻特定表上面的查詢耗時增加的問題。經分析發現,這次被 kill 的 SQL 是分布在各個表上面,而且查詢發現并不存在長事務。

分析問題發生時候的資料庫快照資訊,QPS 都很低,除了差不多 10 TPS 的 delete 和幾十的 select,沒有發現有問題的 SQL。

分析當時的 show engine innodb status 的資訊,發現每次出問題的時候都會出現一些 latch 的等待,如下圖所示。

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

找到對應的代碼行數

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

看代碼鎖位置像是在等待各種 Buffer Pool 的各種 latch。為啥會等待在這裡呢,又沒有 DDL 相關的 SQL,于是百思不得其解。

問題診斷一時間陷入困境。

抽絲剝繭

由于等待和 Buffer Pool 的各種 latch 相關,而且 delete 操作本身會産生大量髒資料,那會不會跟刷髒頁操作相關呢?

我們看下 SQL 被 kill 的量和刷髒頁的量之間的關系

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生
sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

發現每秒刷髒頁的量和 SQL 被 kill 的量的曲線有點相近,看着刷髒頁的量挺大的,但是每秒 delete 的 TPS 又不是很高,為啥這麼低的 TPS 會讓刷髒頁頻率抖動以及 SQL 執行變慢呢?

曾經換過不同批次的機器,發現問題依舊,并沒有改善,說明并不是機器本身的問題。

繼續浏覽 buffer pool 相關的監控名額,像是發現新大陸一樣的發現了一個異常名額

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

髒頁比例達到了快 90% !!!太吓人了!!!

為啥髒頁比例會達到 90% 呢,無非就是刷髒頁的速度跟不上産生的速度,要麼就是 IO 能力不行,要麼就是産生髒頁的速度過快,要麼就是記憶體池太小,導緻 Buffer Pool 被髒頁占滿。

那麼這個髒頁比例達到快 90% 會有什麼問題呢?

MySQL 有兩個關于髒頁的參數

select  * from  tab where a=1 and b=2;
           

我們檢視下官方定義

innodb_max_dirty_pages_pct:

InnoDB tries to flush data from the buffer pool so that the percentage of dirty pages does not exceed this value. The default value is 75.

innodb_max_dirty_pages_pct 是為了避免髒頁比例大于 75%,改變該參數不會影響刷髒頁的速度。

innodb_max_dirty_pages_pct_lwm:

Defines a low water mark representing the percentage of dirty pages at which preflushing is enabled to control the dirty page ratio. The default of 0 disables the pre-flushing behavior entirely.

innodb_max_dirty_pages_pct_lwm 表示的是當髒頁比例達到該參數表示的低水位時候,刷髒線程就開始預刷髒來控制髒頁比例,避免達到innodb_max_dirty_pages_pct 。刷髒頁的最大 IO 能力是受 innodb_io_capacity 和 innodb_io_capacity_max 控制。

生産上我們将 innodb_max_dirty_pages_pct_lwm 設定成了50

當髒頁比例大于 innodb_max_dirty_pages_pct 時候,InnoDB 會進行非常激烈的刷髒頁操作,但是由于 DELETE 操作還是在進行,髒頁産生的速度還是非常快,刷髒頁的速度還是跟不上髒頁産生的速度。為了避免髒頁比例進一步擴大,更新将會被堵塞,進而導緻 DELETE 執行變慢,直至被 KILL。

發現問題之後,根據我們之前的假設,有三種解決方案:

1. 調大 io_capacity ,但是由于主機是多執行個體部署,IO 占用已經比較高,PASS。

2. 降低髒頁産生速度,也就是調低 DELETE 速度,因為資料産生的速度很快,為了避免删除跟不上插入的速度,也被 PASS。

3. 調大 Buffer Pool,可以容納更多的髒頁。

說幹就幹,得益于 MySQL 5.7 的線上調整 Buffer Pool,立馬将 Buffer Pool Size 擴了一倍,效果非常顯著

sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生
sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生
sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生
sql 占比_當每分鐘超過 200 個 SQL 被 kill時-愛可生

髒頁比例立馬下降,被 kill 的 SQL 也下降了。平均 SQL rt 下降很多。

總結

得益于 MySQL 的開源,很多錯誤都可以直接确認到對應的代碼,大緻定位到問題發生的地方,給問題排查帶來了很多友善。同時對 MySQL buffer pool 的命中率以及髒頁比例也要多多關注,對 SQL 的性能都有很大的影響。