本文介紹了資料庫發生故障時的快速判斷方法和解決辦法。
如何定位系統瓶頸是否在資料庫上
- 通過Processlist來判斷 執行以下語句,顯示上所有正在執行的SQL語句。
SHOW PROCESSLIST WHERE INFO IS NOT NULL一般情況下,語句堆積會伴隨著資料庫卡慢一起出現,因此如果該語句的顯示結果中沒有大量執行時間大於0的語句,則基本可以斷定問題不在資料庫層面,反之,則說明資料庫可能存在瓶頸。
- 通過堆棧資訊來判斷 應用與資料庫之間通過TCP協議進行互動,如果資料庫層出現瓶頸,則會產生應用將請求通過socket發送給了資料庫,但是資料庫不返回結果的情況,此時socket會阻塞在read方法上。因此我們可以通過應用當前的堆棧資訊來判斷是否在資料庫層面發生了阻塞。本文以Java應用為例說明:
- 通過jstack命令dump堆棧資訊。
- 在dump出的資訊中搜尋mysql驅動等待請求返回的堆棧,內容如下:
at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:101) at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:144) at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:174) - locked <0x00000002eb8f2d98> (a com.mysql.jdbc.util.ReadAheadInputStream) at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3183) at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3659) at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3649) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4090) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:972) at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2497) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2870) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2806)
如果有大量的線程的堆棧情況如上例所示,則代表大量線程阻塞在等待資料庫返回,說明瓶頸可能在資料庫層面,反之,則應重點排查應用本身是否存在瓶頸。
資料庫問題快速處置
在通過上述方法判斷資料庫存在瓶頸之後,推薦依次使用以下方法進行快速恢複。
方法一:KILL所有語句如果Processlist中顯示堆積了很多SQL,建議立即KILL掉所有正在執行的語句,提供了如下指令進行這個操作:
KILL "ALL"該語句會KILL掉計算節點與資料節點之間的每一個串連,從而達到結束掉所有語句的效果。
方法二:重啟應用執行方法一後,等待一段時間如果再次產生語句堆積,建議重啟應用,避免應用因為處於某種錯誤的狀態,不斷的重試高代價的SQL。
方法三:SQL限流方法2依然無法解決問題之後,建議使用的CCL_RULES(限流功能)。
- 執行
SHOW FULL PROCESSLIST命令,找到佔比比較高的SQL的模板ID。+----+---------------+-----------------+----------+-------------------------------+------+-------+-----------------------+-----------------+ | ID | USER | HOST | DB | COMMAND | TIME | STATE | INFO | SQL_TEMPLATE_ID | +----+---------------+-----------------+----------+-------------------------------+------+-------+-----------------------+-----------------+ | 2 | polardbx_root | ***.*.*.*:62787 | polardbx | Query | 0 | | show full processlist | NULL | | 1 | polardbx_root | ***.*.*.*:62775 | polardbx | Query(Waiting-selectrulereal) | 12 | | select 1 | 9037e5e2 | +----+---------------+-----------------+----------+-------------------------------+------+-------+-----------------------+-----------------+ 2 rows in set (0.08 sec) - 通過模板ID對該類型的SQL進行限流,例如:
CREATE CCL_RULE IF NOT EXISTS `test` ON *.* TO 'ccltest'@'%' FOR SELECT FILTER BY TEMPLATE('9037e5e2') WITH MAX_CONCURRENCY=10;
以上方法都無效的情況下,請重啟資料庫。