MySQLはリレーショナルデータベース管理システムの一つです。プログラムではしばしばMySQLにデータを保存して永続化し、適宜読み出します。この読み書きの命令文(以下クエリ)が遅いとユーザーが不便な思いをしてしまいます。また極端に遅い場合は運用にも悪影響を与えます。この様な遅いクエリはスロークエリと呼ばれ、そもそもスロークエリが起きないように処理したり、スロークエリを適切にハンドリングするべきであったりします。そういった処理を作る際、スロークエリが再現できないと適切なプログラムが作れているのか怪しくなるためスロークエリを再現する需要があります。この記事ではスロークエリを再現するために使える関数である SLEEP 関数を紹介します。
MySQLのSLEEP関数は指定した秒数だけ処理を停止します。プログラミング言語などでもよくある関数です。これを使うことで意図的にクエリの実行を遅延させることが可能です。開発環境、テスト環境でスロークエリ発生時の動作を確認したい場合などに便利です。
MySQL :: MySQL 8.0 リファレンスマニュアル :: 12.24 その他の関数#function_sleep
SLEEP関数は例えば次の様に使えます。
SELECT SLEEP(5);
リファレンスにもあるシンプルな例です。これは 5 秒きっかり止まり、その後正常に動きます。
期待通りに動かないよくある書き方の例が次です。
SELECT pref_kanji, address_kanji, SLEEP(5) AS sleep # 結果行の数だけSLEEPを繰り返してしまう FROM x_ken_all LIMIT 10; SELECT pref_kanji, address_kanji FROM x_ken_all # 行がWHERE条件を満たすかチェックするたびにSLEEPしてしまう WHERE SLEEP(5) LIMIT 10;
コメントにある通りSLEEP関数が繰り返し呼ばれることによって引数の秒数をはるかに超えた秒数クエリを止めてしまいます。これが特に厄介なところは何回SLEEPが実行されるのかわからない点です。WHEREは言わずもがなですし、SELECTとLIMITの組み合わせでもLIMIT未満の行数しか取れない場合があります。そこで一度だけ実行される様な工夫が欲しくなります。これは次の様にできます。
SELECT pref_kanji, address_kanji FROM x_ken_all # CROSS JOIN は一度だけSLEEPするための工夫 # 一度だけSLEEPして1行1列のsleep_result_tableを完成させる。 # あとは sleep_result_table をCROSS JOINすることで、余分な行を増やしたり、複数回SLEEPすることもなく処理できる。 CROSS JOIN (SELECT SLEEP(5) as sleep) sleep_result_table LIMIT 10;
コメントにある様に一度だけSLEEPし、SLEEPの結果を元の結果を壊さない様に処理することで期待通りに一度だけSLEEPする様にクエリを改造できます。
こんな感じで任意のクエリをスロークエリにすることができます。これを利用してスロークエリ発生時のロギング、タイムアウトなどのテストができます。