自動kill慢查詢


     在生產環境中,DB服務器經常會被並發的慢查詢壓掛,因此事前進行sql審核避免爛SQL很重要。萬一不小心慢sql還是跑到線上,並且並發還不小,這是dba肯定會收到告警。dba上線處理第一時間是定位並kill慢查詢,避免慢查詢其他正常的事務。本文主要圍繞kill展開並附帶介紹幾種相關的timeout參數和實現機制。

kill指令

      kill的語法如下:kill [connection|query] thread_id,通過kill命令可以kill一個查詢或kill一個連接。一般而言,每個用戶只能查看和終止自己用戶的連接和查詢,若用戶具有process權限,則可以查看所有線程,具有super權限,可以查看和終止所有用戶的連接和查詢。假設有兩個會話A和B,A的thread_id為xxx,A會話執行查詢,B會話分別執行kill query xxx和kill xxx,會話A會分別收到1317和2013錯誤。

mysql> select count(*) from test_slow where 1=1; ERROR 1317 (70100): Query execution was interrupted mysql> select count(*) from test_slow where 1=1; ERROR 2013 (HY000): Lost connection to MySQL server during query   

      通過mysql客戶端進行操作,除了發送kill指令,還可以通過ctrl+c來終結自己。這里的原理很簡單,mysql客戶端里面有一個信號捕獲線程,監聽到SIGINT后,創建一個連接,然后發送kill命令到服務端,將自己終結。通過gdb調試時,為了避免gdb對信號的影響,通過命令設置即可,命令如下:

handle SIGINT nostop print pass

kill實現原理

      mysqld收到kill命令后,會將對應的線程實例thd設置為kill狀態,若為kill_connection,會主動關閉socket。無論是kill_connection還是kill query,查詢可能不能立即結束,因為查詢可能正在運行過程中。因此,在mysqld的代碼中的關鍵節點都會調用trx_is_interrupted函數判斷自身的狀態是否為killed,若是,則報錯返回,終止執行。到底mysqld在哪些地方會進行檢查,這里列出官方的文檔,說明如下:

• In SELECT, ORDER BY and GROUP BY loops, the flag is checked after reading a block of rows. If the kill flag is set, the statement is aborted.

• During ALTER TABLE, the kill flag is checked before each block of rows are read from the original table. If the kill flag was set, the statement is aborted and the temporary table is deleted.

• During UPDATE or DELETE operations, the kill flag is checked after each block read and after each updated or deleted row. If the kill flag is set, the statement is aborted. Note that if you are not using transactions, the changes are not rolled back.

     如果空閑連接被kill,那么這個連接何時被關閉呢?執行kill connection xxx時,會首先將thd的狀態打標,然后會調用對應thd的vio_cancel來關閉socket連接,進而產生一個網路事件,監聽線程捕獲到對應連接的event,交給worker線程處理,worker線程檢查thd的狀態為killed,則結束連接,將其從global_thread_list中摘除。另外,如果被kill的連接正在等一個鎖(行鎖,表鎖),則調用引擎層的kill接口,對於innodb是innobase_kill_connection,將請求等待的鎖釋放,並將其喚醒,被喚醒的線程發現自己的狀態我killed,則退出。這里有點類似於死鎖的處理,加鎖時進行死鎖檢查,如果發現導致循環依賴,則會根據權重(undo數+上鎖記錄等)來找一個事務進行回滾,如果需要回滾的是當前事務,則當前事務釋放鎖,並回滾;如果需要回滾的是其它事務,釋放持有的鎖,設置其錯誤碼為deadlock,並將其喚醒,被喚醒的線程發現自己錯誤碼,則出錯返回。

kill自動化

     如果每次查詢都需要dba人工去觸發,那么遇到問題時,可能處理沒那么及時,如果能自動化的kill慢查詢則能將影響降到最低。在jdbc中通過接口Statement.setQueryTimeout(int) 即可實現。這個原理其實與mysql客戶端通過ctrl+c kill查詢類似,只不過這種方式是通過超時實現。設置一個定時器,有一個線程定時不停地判斷定時器事件有沒有觸發,達到觸發點后,再發送kill查詢到服務端,實現kill查詢的目的。這種方式雖然能自動化,但這個接口調用控制權還是在開發手中,而且一般情況下,開發一定都認為自己的查詢時沒有問題的,肯定不會超時,所以這個接口是否被調用無法控制。那么遇到問題后,DB還是一樣會掛,還是需要dba人工處理。因此在alimysql內核也做了一套類似的功能,通過參數max_statement_time可以控制當前會話和所有會話的超時時間。只要超時,mysql內部會調用接口awake接口將對應的thd狀態設置為killed,達到kill query或kill connection的目的。

幾種超時參數

除了我們上面提到的statement timeout,jdbc中常見的超時參數還有connectTimeout和socket timeout。

connectTimeout:和數據庫服務器建立socket連接時的超時,單位:毫秒。

socket timeout: socket操作(讀寫)超時

      具體而言,jdbc通過設置socket的屬性來實現超時目的,不同的JDBC驅動其配置方式會有所不同。 socket連接時的timeout:通過Socket.connect(SocketAddress endpoint, int timeout)設置;socket讀寫時的timeout:通過Socket.setSoTimeout(int timeout)設置,采用阻塞IO模型,如果不設置socket timeout或connect timeout,應用多數情況下是無法發現網絡錯誤的。因此,當網絡錯誤發生后,在連接重新連接成功或成功接收到數據之前,應用會無限制地等下去。當然,操作系統層面,也會有socket timeout設置。相關的配置參數如下:

tcp_keepalive_time:TCP連接在idle指定時間后,內核才發起probe

tcp_keepalive_probes:TCP發送keepalive探測以確定該連接已經斷開的次數

tcp_keepalive_intvl:探測消息發送的頻率

因此,探測(tcp_keepalive_time + tcp_keepalive_intvl * tcp_keepalive_probes)時間后,若還不能連上,則斷開連接。 

這些參數可以修改:

/proc/sys/net/ipv4/tcp_keepalive_time
/proc/sys/net/ipv4/tcp_keepalive_intvl
/proc/sys/net/ipv4/tcp_keepalive_probes

參考文獻

http://astar.baidu.com/forum/forum.php?mod=viewthread&tid=363

http://dev.mysql.com/doc/refman/5.1/en/kill.html

http://m.blog.csdn.net/blog/xieyuooo/39898449

http://blog.sina.com.cn/s/blog_a2d4803001013hrk.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM