在生產環境中,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,但是對於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自動化
如果每次查詢都需要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