この記事はMySQL Casual Advent Calendar 2017 - Qiitaの9日目のエントリとなります。
実行が長引いたSELECT文を強制終了させるヤーツ
MySQL5.6まで、正常に処理が進んでいて遅いSELECTをタイムアウトさせるシステム変数はありませんでした。
正常に処理が進んでいない時のパラメータだと
がありました。
正常に処理が進んでいるけど、厳密には「処理中」ではないときに効くパラメータだと
- net_read_timeout:クライアントからサーバに送り込んだデータの読み込み時間
- net_write_timeout:サーバからクライアントへのデータの書き戻し時間
がありました。
他にも、アイドルタイムアウト系で
- interactive_timeout
- wait_timeout
などもありました。
しかし、MySQLさん、ちょっと重いSQL投げると延々帰ってこないことがあったりするのに、それをタイムアウトさせるサーバー側の設定がなく、クライアントサイドで対応する必要がありました。
例えば、Javaプログラムの場合、JDBCドライバ「Connector/J」に対して「SocketTimeout」で「クライアントから見て、リクエストがnミリ秒帰ってこなかったら諦めるよ」って形をとる必要がありました。
使用している言語がJavaじゃなかったら、、、わかりません。ドキュメント漁ってください。
ザクッと読んだ限り、connection_timeout、つまり「新規接続時にサーバーが見つからない/返事がない」ヤツっぽいタイムアウトと、コネクションの確立後に投げたリクエストの返答までの時間が間延びしてるタイムアウトが読み分けにくい。。。
Connector/.NETの「MySqlCommand.CommandTimeout Property」
なんかはイケそうな感じだし、これ以外は。。。。
シェルスクリプトでmysql -e "SQL文"とかやってたら、、、mysqlプログラムのオプションにはSocketTimeOut相当のパラメータはありません。。。
しかし、MySQL 5.7以降(もちろん8.0も)、表題に上げた「MAX_EXECUTION_TIME」によって、実行自体が長引くSELECT文にミリ秒指定でタイムアウト設定をすることが可能になっています。
MAX_EXECUTION_TIMEの使い方
GLOBAL/SESSION VARIABLEで指定する
my.cnfで指定するなり、SET句でしていするなり、今まで通りのサーバーシステム変数の扱いと同じです。
これに限った話ではありませんが、SETでGLOBALだけ設定しても、現在セッションには影響ありません。
GLOBALにSETしたあとにログインするか、SESSIONレベルでSETしましょう。
以下、100万件超、普通に投げると20秒弱かかるSELECT文に対して、「MAX_EXECUTION_TIME」で1000ミリ秒を指定したケースです。
「ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded」で強制終了されますが、これが出るまでに何秒かかったかの表示はないです。。。
mysql> selectcount(*)from test_a; +----------+ | count(*) | +----------+ | 1048576 | +----------+ 1rowin set (19.07 sec) mysql> set global max_execution_time=1000; Query OK, 0rows affected (0.00 sec) mysql> select@@global.max_execution_time,@@session.max_execution_time; +-----------------------------+------------------------------+ | @@global.max_execution_time | @@session.max_execution_time | +-----------------------------+------------------------------+ | 1000 | 0 | +-----------------------------+------------------------------+ 1rowin set (0.00 sec) mysql> selectcount(*)from test_a; +----------+ | count(*) | +----------+ | 1048576 | +----------+ 1rowin set (18.97 sec) mysql> exit Bye [root@mysql57 ~]# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands endwith ; or \g. Your MySQL connection id is5 Server version: 5.7.20 MySQL Community Server (GPL) Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type'help;'or'\h'for help. Type'\c'to clear the current input statement. mysql> select@@global.max_execution_time,@@session.max_execution_time; +-----------------------------+------------------------------+ | @@global.max_execution_time | @@session.max_execution_time | +-----------------------------+------------------------------+ | 1000 | 1000 | +-----------------------------+------------------------------+ 1rowin set (0.00 sec) mysql> selectcount(*)from test_a; ERROR 1046 (3D000): Nodatabase selected mysql> use exe_test; Reading table information for completion of tableandcolumn names You can turn off this feature to get a quicker startup with -A Database changed mysql> selectcount(*)from test_a; ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded
オプティマイザ・ヒントで指定する
オプティマイザ・ヒント自体がMySQL 5.7からの新機能で、「/*+ XXXXXXXXX */」の形式で、BKAJやMRRといったアルゴリズム採用をヒント句の形で与えられるものです。
その中で、ちょっと異色なヒントとして、MAX_EXECUTION_TIMEが指定できます。
そのSQLだけに指定できるので、セッションレベルよりも細かい制御ができて便利ですね。
気をつけたいのは、システム変数との違いです。
MAX_EXECUTION_TIMEは「=nミリ秒」ではなく、「(nミリ秒)」で指定します。
mysql> select/*+ MAX_EXECUTION_TIME(1000) */count(*)from test_a; ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded
なお、オプティマイザ・ヒントの文言を間違うと、ふつーにSELECTの処理が実行され、warningが検出されます。
以下、MAX_EXECUTON_TIMEのスペルミス(Iがない)をした例です。
mysql> select/*+ MAX_EXECUTON_TIME(1000) */count(*)from test_a; +----------+ | count(*) | +----------+ | 1048576 | +----------+ 1rowin set, 1 warning (18.93 sec)
注意?点
MAX_EXECUTION_TIMEは
- 読み取り専用SELECTにしか効きません
- ストアドプログラムには効きません
The execution timeout for SELECT statements, in milliseconds. If the value is 0, timeouts are not enabled.
max_execution_time applies as follows:
- The global max_execution_time value provides the default for the session value for new connections. The session value applies to SELECT executions executed within the session that include no MAX_EXECUTION_TIME(N) optimizer hint or for which N is 0.
- max_execution_time applies to read-only SELECT statements. Statements that are not read only are those that invoke a stored function that modifies data as a side effect.
- max_execution_time is ignored for SELECT statements in stored programs.
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_execution_time
ということで。
まとめ
- MySQL 5.7以降、SELECTの実行時間を「MAX_EXECUTION_TIME」でタイムアウト制御することができる
- システム変数だけじゃなく、オプティマイザ・ヒント(5.7新機能)により、SELECT文単位でもタイムアウト指定できる。
- あくまで、「読み取り専用SELECT文だけ」が対象。
- もともとMySQLの持ってるタイムアウト設定パラメータ、多い&名前が直感的じゃないの、ツラい。
MySQL5.7、GAしてから早2年ですよ。
ネット界隈はMySQL8.0で一喜一憂してますが、レイトマジョリティ以降の現場でも、さすがにMySQL5.7には手を出していいよね。
詳解MySQL 5.7 止まらぬ進化に乗り遅れないためのテクニカルガイド (NEXT ONE)
- 作者:奥野幹也
- 出版社/メーカー:翔泳社
- 発売日: 2016/08/26
- メディア:単行本(ソフトカバー)
- この商品を含むブログを見る
ところで
OracleとかPostgreSQLってどうだっけ?