カテゴリー
SugiBlog Webエンジニアのためのお役立ちTips

PDOのタイムアウト設定

この記事は最終更新日から1年以上経過しています。

MySQL接続にPDOを使用している場合のタイムアウト設定についてです。

PDOにはPDO::ATTR_TIMEOUTというオプションを設定することが出来ます。
設定する値は数値で、単位は秒です。

設定方法としてはPDOのインスタンスを生成するときに設定するか、後から設定する方法の2通りあります。

インスタンス生成のときに設定する

$dbh = new PDO('mysql:host=localhost;dbname=test', 
    $user, 
    $pass, 
    array(PDO::ATTR_TIMEOUT => 30)
);

後から設定する

PDO::setAttribute(PDO::ATTR_TIMEOUT, 30);

ここで1つ注意点ですが、あくまでこのタイムアウト設定は「接続のタイムアウト」であるという点です。
データ量の多いデータベースから大量の結果が返されるクエリ実行に対してはタイムアウトは反映されません。

クエリ実行に対してタイムアウトを設定したい場合は他の設定方法が必要です。

PDOでの設定方法が見つからない…

色々調べていたのですが、mysqlndやMySQLiでの設定方法は見つかりました。
が、PDOでの設定方法が見つかりませんでした。

そこで、私が対応した一例をご紹介します。

まず公式のマニュアルのPDO::ATTR_TIMEOUTを読んでみました。
するとこんな注意書きが…

注意:
すべてのドライバがこのオプションに対応しているわけではありません。 また、ドライバによっては意味が異なることがあります。 たとえば SQLite は書き込み可能なロックを確保するのをこの秒数まで待ち続けますが、 他のドライバでは、 この秒数を接続時のタイムアウトや読み込みのタイムアウトとして扱うことがあります。

これを読んで「ドライバ」という言葉が気になりました。
そこで、phpinfo()の内容の中でPDOを探し、pdo_mysqlのPDO Driver for MySQLを見つけます。
そのClient API versionを確認すると「mysqlnd…」と書いてありました。

ということはmysqlndのタイムアウト設定をしてやればいいんじゃないか?と思い設定してみました。

ini_set('mysqlnd.net_read_timeout', 30);

ドンピシャです。
クエリの実行をタイムアウトさせることが出来ました。

包括的に設定してしまいたい場合はphp.iniに設定しましょう。

; Timeout for network requests in seconds.
;mysqlnd.net_read_timeout = 31536000

こちらをコメント解除して秒数を設定してください。

; Timeout for network requests in seconds.
mysqlnd.net_read_timeout = 30

この31536000という数値はPHP 7.2.0より前のバージョンでのデフォルト値です。

大規模なデータベースになってくるとこういった対応も必要になってくるケースもあるかと思います。
どなたかのお役に立てば幸いです。

あとがき

ちなみにmysqlコマンドで

show global variables like '%timeout%';

と打てば、次のようにシステム変数のtimeoutに関する設定値がリストで表示されます。

mysql > show global variables like '%timeout%';
+---------------------------------------+----------+
| Variable_name                         | Value    |
+---------------------------------------+----------+
| connect_timeout                       | 10       |
| deadlock_timeout_long                 | 50000000 |
| deadlock_timeout_short                | 10000    |
| delayed_insert_timeout                | 300      |
| idle_readonly_transaction_timeout     | 0        |
| idle_transaction_timeout              | 0        |
| idle_write_transaction_timeout        | 0        |
| innodb_flush_log_at_timeout           | 1        |
| innodb_lock_wait_timeout              | 50       |
| innodb_rollback_on_timeout            | OFF      |
| interactive_timeout                   | 28800    |
| lock_wait_timeout                     | 86400    |
| net_read_timeout                      | 30       |
| net_write_timeout                     | 60       |
| rpl_semi_sync_master_timeout          | 10000    |
| rpl_semi_sync_slave_kill_conn_timeout | 5        |
| slave_net_timeout                     | 60       |
| thread_pool_idle_timeout              | 60       |
| wait_timeout                          | 28800    |
+---------------------------------------+----------+
19 rows in set (0.002 sec)

この結果を見るとnet_read_timeoutは30(デフォルト値)となっており、元々30なら設定を変更する必要がないのでは?と思うかもしれません。
しかしながらこちらの設定はおそらくmysqlの設定なので関係ありません。
実際はnet_read_timeoutではなくmysqlnd.net_read_timeoutが使用されているからです。

mysqlnd.net_read_timeoutのデフォルト値は86400(24時間)です。
どこで確認するかというとphpinfo()です。

24時間…長過ぎですね。
Ajaxなど、非同期通信なんかでタイムアウトを設定している場合はその時間と合わせるようにしましょう。

この記事がお役に立ちましたらシェアお願いします
5,100 views

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です