new takyam();

Qiitaぽい話はQiitaに書いていくことにする気がする http://qiita.com/takyam

PHPのMySQLとの接続・切断のタイミング

ウェブオペレーションエンジニアはリリース前のソースコードのココを見ているッ!
http://blog.nomadscafe.jp/2012/12/post-15.html

こちらの記事、アプリケーションチューニングの初期にとるべき行動の勘所がまとまってて、非常に勉強になりました。

データベースというとすぐにSQLの話になってしまいますが、その前にアプリケーションからどのようにデータベースへ接続しているのかを確認します。TCPのハンドシェイクのコストはそれなり高いので、適切な範囲で接続を維持するようにして効率性をあげます。イベントベースで動くmemcachedはコネクション維持のコストが小さいのでできるだけ長いプロセス単位での接続、MySQLは1コネクション1スレッドと少しコストがあがるのでリクエスト単位で接続を落とすようにしています
(本文から引用。強調は引用時に強調)

というわけで、MySQLのコネクションをリクエスト単位で維持して、
レスポンスと同時にコネクションを破棄するようにしておきたいと思いました。

ていうか普通そうなってるんじゃねーの・・・?と思いはしたものの、
明確にPHPのそのへんの設定って細かく調べた事なかったので、コレを機会に調べてみました。

構成: ngix + php-fpm + mysql

まず、persistentがoffの場合、スレッドはレスポンス時に開放されているようでした。

<?php
//persistent off
$connection = mysqli_connect('localhost', 'root', '', 'php_connection_test');
for($i=0;$i<10;$i++){
    $rs = mysqli_query($connection, 'SELECT * FROM test');
}
var_dump(mysqli_fetch_row($rs));

persistentがonの場合は、自分の環境だとphp-fpmが2つの接続をmysqlに対して維持してました。

<?php
//persistent on
$connection = mysqli_connect('p:localhost', 'root', '', 'php_connection_test');
for($i=0;$i<10;$i++){
    $rs = mysqli_query($connection, 'SELECT * FROM test');
}
var_dump(mysqli_fetch_row($rs));

というわけで、何も考えないでやっても、persistentがoffだと、リクエスト単位でconnectionを切断してるし、persistentがonの場合も、最大スレッド数に限界があるようなので、コスト的には問題なさそうな気がします。

どっちかっつーと、スレッド数が2個までしか増えない方がまずいけど、いったい何がアレなのやら。。。

php.iniの設定は、

; Maximum number of persistent links.  -1 means no limit.
; http://php.net/mysqli.max-persistent
mysqli.max_persistent = -1

となっていて、たぶんphp的にはconnection pool数に限界なさそうなので、MySQLの設定なのかな?
でもMySQLのMaxConnectionsはたっぷり(450)設定してあるし。。。

で、答えとしては、php-fpmのspare-serverが2個までしか起動しないようになってたので、process2個に対して、接続2個という事で、ちゃんと維持されてました。spare-serverを増やすと、接続のプールも増えました。

個人的には、コネクションの維持よりコネクションの作成のほうがコストかかりそうな気がするのですがどうなんでしょうかね?
たしかに超大量かつ同時コネクション数が大量だと高コストっぽい気はしますが。。。

なんだかケースバイケースな気がしてきました。

Apacheとphp-fpmによっても違いそうだし、preforkかどうかでも違いそうだし(ふんいき)、気が向いたらもう少し調査してみたいと思います。

「M1:S1構成はやめようね(^ρ^)」はなるほど!と昔思ったのに忘れてたので、改めて心に刻むことにします。

最後にMySQLのプロセス数の計測方法をメモ

1. show processlist;

[~]$ mysql -u root -e 'show processlist;'                                                                                                                  [1.9.3-p194] 
+----+------+-----------+---------------------+---------+------+-------+------------------+
| Id | User | Host      | db                  | Command | Time | State | Info             |
+----+------+-----------+---------------------+---------+------+-------+------------------+
|  2 | root | localhost | NULL                | Sleep   |    1 |       | NULL             |
| 46 | root | localhost | NULL                | Sleep   |  281 |       | NULL             |
| 60 | root | localhost | php_connection_test | Sleep   |    2 |       | NULL             |
| 61 | root | localhost | php_connection_test | Sleep   |    2 |       | NULL             |
| 62 | root | localhost | php_connection_test | Sleep   |    2 |       | NULL             |
| 63 | root | localhost | php_connection_test | Sleep   |    2 |       | NULL             |
| 64 | root | localhost | NULL                | Query   |    0 | NULL  | show processlist |
+----+------+-----------+---------------------+---------+------+-------+------------------+

2. mysqladumin

[~]$ mysqladmin -u root extended-status | grep -E 'Max|Threads|Connection'                                                                                 [1.9.3-p194] 
| Connections                              | 67          |
| Max_used_connections                     | 7           |
| Threads_cached                           | 0           |
| Threads_connected                        | 7           |
| Threads_created                          | 66          |
| Threads_running                          | 1           |

3. innotop

コンソールの右上にコレでる

$ innotop --user root --host localhost -d 1
localhost, 21:34.901, 1.98 QPS, 6/1/0 con/run/cac thds, 5.5.25a