自動記錄MySQL慢查詢快照腳本

寫這個腳本的初衷是在使用阿里云rds的過程中,數據庫出現異常,需要快速恢復。網上有許多類似的kill腳本,都是通過 mysqladmin 實現的。然而 ali-rds 環境有以下限制:

不提供 SUPER 權限的用戶,也就是用戶只能 kill 自己的線程

當連接數暴增時,外部用戶無法登陸,包括控制臺

為了解決上午2大問題,該 python 腳本通過在db實例上,使用多線程的方式,為每個用戶保留一個連接,并實時讀取指令配置文件?mysqk.ini,發現有 kill 需求時,利用對應用戶已有連接找到?information_schema.processlist?中符合條件的線程,并 kill 。

說明:該腳本在9月份做過一次重寫,7月份的版本(分支 old_0.5.0)是每實例每用戶,對應一個線程,db實例一多線程數也太多,看得始終不太優雅,于是改成了一個db實例一個線程,維護同時維護多個用戶的會話。同時新版也加入了更多的功能,如按時間窗口檢查,包含或排除特定連接,郵件通知,配置項覆蓋。

特性

始終通過 mysql ping 維持一個長連接,并有斷開自動重來機制,解決沒有連接可用的尷尬局面

每個db實例有自己的線程,避免需要單獨登陸個別用戶去kill的繁復操作。如果你具有 SUPER 權限,也可以簡化配置做到兼容

能夠分開應對需要殺死線程的場景:

長時間運行超過 N 秒的

Sleep 狀態的事務 (一般不建議,但有時候kill它,可以快速釋放連接給管理員使用)

排除一些線程不能kill,如 Binlog dump

包含特定關鍵字的線程要kill

出現符合條件的線程時,會對當時的processlist, engine status,lock_wait 做一個快照,并郵件發出

有試運行dry_run模式,即執行所有的檢查過程但不真正kill

支持只在時間窗口內運行,考慮到晚上一些長任務不檢查

密碼加密

快速使用

需要pip安裝MySQL-python和pycrypto兩個庫,只在python 2.7上有測試。

在?settings.py?里面設置連接的用戶名和密碼信息。這里假設同一批db的要check的認證信息是一樣的,指定的用戶既用于登錄認證,也用于告知腳本哪些用戶需要被檢查。
密碼要通過?prpcryptec.py?加密,加密的密鑰需寫入腳本本身的?KEY_DB_AUTH變量。(擔心泄露的話,把mysqk.py編譯成 pyc 來跑)

在?mysqk.ini?主配置文件里面

db_info?節設置需要被檢查的數據庫地址,如?db01=10.0.200.100:3306

可分別?db01等指定需要kill thread的選項。[id_db01]?則默認復用?[db_commkill]?的選項

db_comconfig?節設置?db_puser?為能查看到所有processlist的權限用戶,且在?settings.py?的DB_AUTH中已指定

只想執行檢查,并不想真正kill異常線程,確認 dry_run不等于0

Here we go!

配置項說明

mysqk.ini:

mail_config

郵件通知相關設置,smtp服務地址和認證信息。
mail_receiver=?設置空,表示不發郵件

db_info

設置要檢查kill哪些數據庫實例.
格式:=:,dbid是唯一表示db實例的,后面設置各db需要被kill的選項,小節配置名就是?id_;端口必需指定。

在這里出現的db實例都會被執行檢查,可用 ; 注釋,但需要重啟腳本。

db_comconfig

檢查用公共配置,實時生效。

db_puser:指定一個用戶名用于 show processlist,需要的權限:PROCESS、information_schema庫查看。可以認為是一個代表用戶,檢查異常thread,把結果提供給有該thread殺掉權限用戶。

run_max_count:執行檢查的次數,是一個全局控制開關。每次修改這個值都會重新開始檢查,即一個 clean start,讓剛修改的配置生效。

為 0 表示腳本不進行任何檢查,只簡單維護與數據庫的連接存活。存活檢查頻率在?settings.py?由?CHECK_CONFIG_INTERVAL × CHECK_PING_MULTI決定

為 999 表示會在后臺一致檢查連接線程(但不一定有符合kill條件的),檢查的頻率在?settings.py?里面?CHECK_CONFIG_INTERVAL?指定

為其它值時,表示檢查次數滿后停止檢查

dry_run:是否開啟試運行模式,為0表示真實kill,為1或其它值表示試運行。試運行模式可用于監控慢查詢并告警。注意同一會話線程ID只告警一次

run_time_window:運行的檢查的時間窗口,格式如?08:00-22:00,在這個時間以外不執行檢查,留空表示不限制。主要考慮晚上一些統計任務可能出現“異常”線程。

db_commkill

kill用公共配置,實時生效,會被?id_?節的選項覆蓋。

k_user:很關鍵的一個選項,表示你要檢查并kill哪些數據庫用戶,多個用逗號分隔(不要帶引號)。?
為?all?時,表示要檢查?settings.py?里 DB_AUTH 指定的所有用戶

為?none?時,表示不kill任何異常線程,效果與設置了 dry_run 模式相當

k_longtime:執行超過設定值的sql則認為異常。一般大于 CHECK_CONFIG_INTERVAL

k_sleep:Sleep超過設定秒的sql則認為異常,為 0 表示不殺掉sleep狀態的線程

k_exclude:排除掉那些特定關鍵字的線程,比如復制線程、管理員的連接等

k_include:包含這些特定關鍵字的線程,需要被kill。注意,它作用在滿足 k_user 和 k_exclude 的前提之下。?
k_exclude與k_include 的值是支持python re模塊正則的格式,不要帶引號

id_dbid

這部分區域的配置項與 db_commconfig 相同,用于針對個別db的kill選項。

使用建議

兩種組合模式:

設置?dry_run=0,默認?k_user=none,當數據庫出現異常時,主動修改對應db的k_user值,動態kill

設置?dry_run=1,默認?k_user=all,相當于運行在daemon模式,有慢查詢則郵件通知,并且記錄下當時的信息

當然你也可以dry_run=0,k_user=all,讓程序一直在后臺跑并kill,但生產環境極不推薦。

有日志和快照文件可以查看。

配置文件示例

mysqlk.ini :

[mail_config]mail_host=smtp.exmail.qq.commail_user=xxx@ecqun.commail_pass=xxxxxxmail_receiver=[db_info]crm0=192.168.1.125:3306crm1=192.168.1.126:3306crm2=192.168.1.127:3306crm3=192.168.1.128:3306base=10.0.200.142:3306[db_commconfig]db_puser=ecuser; how many kill times once this config file changed; 0: DISABLE all kill; 999: always kill threads that meet kill conditions; default: 1; can not be inheritrun_max_count=999dry_run=1

[mail_config]  mail_host=smtp.exmail.qq.com  mail_user=xxx@ecqun.com  mail_pass=xxxxxx    mail_receiver=    [db_info]  crm0=192.168.1.125:3306  crm1=192.168.1.126:3306  crm2=192.168.1.127:3306  crm3=192.168.1.128:3306  base=10.0.200.142:3306    [db_commconfig]  db_puser=ecuser    ;?how?many?kill?times?once?this?config?file?changed  ;?0:?DISABLE?all?kill  ;?999:?always?kill?threads?that?meet?kill?conditions  ;?default:?1  ;?can?not?be?inherit  run_max_count=999  dry_run=1  run_time_window=08:00-22:00      [db_commkill]  k_user=all  k_longtime=10  k_lock=1  k_sleep=0    k_exclude=Binlog|ecdba|Daemon  k_include=select?sleep(17)      [id_crm0]  ;?k_user:?who's?threads?to?be?killed.?use?comma?to?separate  ;?????????none:?do?not?kill?anyone's?threads  ;?????????all:?kill?all?user's?threads?(with?other?where?conditions)  ;?default:?none  k_user=all    ;?k_longtime:?filter?the?threads?who's?running?time?is?longer?than?this  ;?????????????0:?ignore?the?time?>?x??condition  ;?default:?10  k_longtime=10    ;?k_sleep:?whether?kill?sleepd?threads?or?not  ;??????????0:?do?not?kill?command='Sleep'?threads?from?processlist  ;??????????when?it?set?to?1,?usually?it's?subset?of?k_longtime?condition  ;?default:?0  k_sleep=0    [id_crm1]  k_user=ecuser  k_longtime=10  k_sleep=0    [id_crm2]  k_user=all  k_longtime=10  k_sleep=0    [id_crm3]
? 版權聲明
THE END
喜歡就支持一下吧
點贊10 分享