なんらかの理由で MySQLにクソ重いクエリがたくさん流れてしまった場合、SHOW PROCESSLISTをみて、クエリをKILLするなんてことは、あったりなかったりします。
この時にクエリが数十個となると、手作業ではやりきれませんので、コマンドを駆使して対応することになります。
ちょっと前にやったのが以下のコマンド
/usr/bin/mysql --defaults-extra-file=/path/to/.my-other.cnf -NB -e 'SELECT GROUP_CONCAT(ID) FROM \ information_schema.PROCESSLIST WHERE STATE = "Creating sort index" AND TIME > 1000 AND INFO \ LIKE "select%example%"' | grep -v NULL | /usr/bin/xargs -r /usr/bin/mysqladmin --defaults-extra-file=/path/to/.my-other.cnff kill
mysqladmin killは複数個のidを受け取れるのでGROUP_CONCATが使うのがおしゃれですね
この手のコマンドでも使い慣れているし、その場にあわせて変更していけるという強みはあるのですが、流石になんとかしたいと思い、ちょっとしたコマンドを作りました。
myps
pgrep、pkillのようにprocesslistをgrepしてkillできることを目指しました。
ある程度作ってから、pt-kill ってのがあると教えてもらいました。そっちを使ってもいいかと思います。
使い方
まず grep
$ myps grep --duration 0 ID:142 USER:root HOST:localhost:59393 DB: COMMAND:Query TIME:57 STATE:User sleep INFO:select sleep(3600) ID:150 USER:root HOST:localhost:59814 DB: COMMAND:Sleep TIME:2 STATE: INFO: ID:145 USER:root HOST:localhost:59800 DB: COMMAND:Query TIME:13 STATE:User sleep INFO:select sleep(360)
select文のみにする
$ myps grep --duration 10 --info 'select%' ID:142 USER:root HOST:localhost:59393 DB: COMMAND:Query TIME:86 STATE:User sleep INFO:select sleep(3600) ID:145 USER:root HOST:localhost:59800 DB: COMMAND:Query TIME:42 STATE:User sleep INFO:select sleep(360)
grepの条件は
-t, --time= 時間 -u, --user= ユーザ -d, --db= DB -c, --command= コマンド -s, --state= 状態 -i, --info= info(クエリ)
が利用できます。時間以外にはワイルドカードとして %
と _
が使えます。
killする際は
$ myps kill --duration 10 --info 'select%' KILLED:142 USER:root HOST:localhost:59393 DB: COMMAND:Query TIME:129 STATE:User sleep INFO:select sleep(3600) KILLED:145 USER:root HOST:localhost:59800 DB: COMMAND:Query TIME:85 STATE:User sleep INFO:select sleep(360)
grepをkillに書き換えるとKILLクエリが発行されます。
出力はすべてltsvで、ターミナルでは色もつきます。見やすく、間違いにくくなるのではないかと思います。
実装
mypsは information_schema.PROCESSLIST
を使っています。ワイルドカードが使えるのは時間以外はすべて、LIKE文を使って検索しているからなのです。
SELECT /* SHOW PROCESSLIST */ ID, IFNULL(USER,"") USER, IFNULL(HOST,"") HOST, IFNULL(DB,"") DB, \ IFNULL(COMMAND,"") COMMAND, TIME, IFNULL(STATE,"") STATE, IFNULL(INFO,"") INFO \ FROM information_schema.PROCESSLIST WHERE ID != CONNECTION_ID() AND IFNULL(INFO,"") LIKE ?
NULLは空っぽ文字列として扱ってます。このあたり使い勝手に影響するか、まだこれから使ってみないとわからないところです。
まとめ
使う時が限られる便利ツールは、いざという時に思い出さず、使い慣れたgrep awk xargsなどを組み合わせることになりがちですが、全てのDBサーバにいれて、ブログも書いたので、いざという時に使おうと思います。