寫這個腳本的初衷是在使用阿里雲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:33665等指定需要kill thread的選項。 [id_db01] 則預設重複使用 [db_commkill] 的選項
db_comconfig 節設定 db_puser 為能檢視所有processlist的權限用戶,且在 settings.py 的DB_AUTH中已指定已指定於所有processlist的權限用戶,且在 settings.py 的DB_AUTH中已指定已指定為
kill線程,確認dry_run不等於0
Here we go!
設定項說明
mysqk.ini:
mail_config
郵件通知相關設定,smtp服務位址和認證資訊。
mail_receiver= 設定空,表示不發郵件
db_info
設定要檢查kill哪些資料庫實例.
格式:
在這裡出現的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條件的),檢查的頻率在裡面時,表示檢查次數滿後停止檢查
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:執行值的則認為超過異常。一般大於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=1dry_run=1, ,相當於運行在daemon模式,有慢查詢則郵件通知,並且記錄下當時的信息
當然你也可以dry_run=0,k_user=all,讓程式一直在後台跑並kill,但生產環境極不推薦。
有日誌和快照檔案可以查看。
設定檔範例
mysqlk.ini :
[mail_config]mail_host=smtp.exmail.qq.commail_user=xxx@ecqun.commail_pass=xxxmail_receiver=[db_info]crm0. 306crm2 =192.168.1.127:3306crm3=192.168.1.128:3306base=10.0.200.142:3306[db_commconfig]db_puser=ecuser; how many kill configs once time 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]