LinuxのLVS(IPVS)でMySQLをロードバランスするときはtimeoutに注意

構成

[appサーバ] -> [lvs] -> [MySQL]群
  • DRでMySQLのスレーブ群にロードバランス
  • appサーバはDBコネクションのプーリング、永続化をしている

問題の現象

DBサーバ上ではmysqldへのコネクションが存在するのに、appサーバ上ではコネクションが存在しない。(netstat調べ)

→無用なコネクションが残留するせいで、MySQLのmax_connectionsに達してしまう。

原因

MySQLの世界の無通信時のコネクションのタイムアウトはデフォルトで 28800秒 (8時間)。一方、IPVSの世界の無通信時のタイムアウトはESTABLISHEDなコネクションで900秒 (15分)。

# ipvsadm -Ln --timeout
Timeout (tcp tcpfin udp): 900 120 300

なので、DBコネクションの永続化等でコネクションを張りっぱなし、かつ、SQLのやりとりがない(無通信状態)場合、IPVS的にタイムアウトを迎えてコネクションが切れるが、MySQL的にはまだタイムアウトになっていないのでコネクションが残ってしまう、というのが原因。

対応

いかのいずれかで。

当時自分は最後の「D. MySQLの世界のタイムアウトを短くする」を採りました。最近はAの都度接続してます。

A. コネクションのプーリング、永続化をやめて、都度接続にする
B. 定期的に通信する
  • プーリングの機構に、SELECT 1等のクエリを投げてコネクションのvalidationをする機能があれば、IPVSのタイムアウトより短い間隔で確認するようにする。
  • IPVSのタイムアウト以内にクエリが発せられるようなサービスにがんばって育てる。
C. IPVSの世界のタイムアウトを長くする
--set tcp tcpfin udp
       Change  the  timeout values used for IPVS connections. This com-
       mand always takes  3  parameters,   representing   the   timeout
       values (in seconds) for TCP sessions, TCP sessions after receiv-
       ing a  FIN packet, and  UDP  packets, respectively.   A  timeout
       value 0 means that the current timeout value of the  correspond-
       ing  entry  is preserved.

で設定可能。

D. MySQLの世界のタイムアウトを短くする

DBサーバのmy.cnfの[mysqld]セクションに以下のように設定する。設定値はIPVSのタイムアウト(デフォルト900秒)より小さい値にする。

interactive_timeout = 780
wait_timeout        = 780

もしかしたら、結果を返し始めるまでに780秒以上かかるクソ重いクエリはタイムアウトしちゃうかもれしれないので心配な人は確認してから設定したほうがいいですけど、そんなクエリ投げてるとイスが飛んでくるかもしれないので気をつけましょう。

謝辞

某IRCチャンネルのゆかいで頼れる仲間たち。