なからなLife

geekに憧れと敬意を抱きながら、SE、ITコンサル、商品企画、事業企画、管理会計、総務・情シス、再び受託でDB屋さんと流浪する人のブログです。

MySQLの「Communications link failure...」の解決方法を調べていて、よく見つけるヤツ

この記事はMySQL Casual Advent Calendar 2017 - Qiitaの5日目のエントリとなります。

みんなだいすきお困りの、アレです。

Oracleだと「ORA-03113:通信チャネルでファイルの終わりが検出されました。」っていう、酷い日本語*1有名なヤツがあるけど、MySQLだとコレが該当するんじゃないでしょうかね。


(1)interactive_timeoutを増やせ

https://ja.confluence.atlassian.com/confkb/attachment-upload-failed-with-communications-link-failure-during-commit-error-250609761.html
とかね。

このパターンに当てはまるのは、
1.トランザクション開始
2.なんかSQL発行して、正常終了
3.しばらく放置
4.Commit発行
で、3と4の間にinteractive_timeoutに設定した秒が経過したとき。


サーバ側:何もしてこないからブチっとしたよー
クライアント側:接続キレてるはずのコネクションオブジェクトを(プログラム内で)つかみっぱなし、そのオブジェクトのCommitメソッド発行

ってなると、接続キレてますよ、って怒られるパターン。


TomcatでConnection Pooling使ってる場合、定期的に検査用SQLを投げる設定とか入れられるので、これをうまく使いましょう。

(2)net_read_timeout/net_write_timeoutを増やせ

サーバーから見て、ネットワーク越しに送られてくるデータを読む(read)、ネットワーク越しにクライアントに返すデータを書く(write)際の時間上限設定です。
リクエスト受け取って処理している時間は含まないはず。


大量データの往来をやろうとすると、コレに引っかかるか、max_allow_packetに引っかかるか。
タイムアウト伸ばしていいなら、増やせばいいし、そうでないなら、メモリの利用状況を鑑みつつmax_allow_packet引き上げで対処。

(3)innodb_log_file_sizeを増やせ

http://fa11enprince.hatenablog.com/entry/2015/03/23/011206
とか。

日本語記事なので、そのまま読めばいいのですが、innodb_log_file_sizeが小さすぎるところに、短い時間で大量更新を行うと、ファイル溢れが起きるわけで、mysqldが死ぬことがあるようです。

そしたら、クライアントセッション全部切れるので、そのセッション持ってるオブジェクトをプログラム内で使い続けたら、、接続キレてますよ、って怒られるパターン。

MySQLのエラーログにかかれているはずのものなので、何かあったら最初にエラーログを確認することがいかに大事であるか伝わってきます。


(4)クライアント側(ex:jdbcドライバ)の「socketTimeout」を増やせ

記事ではあんまり出てこないけど

The last packet sent successfully to the server was XXXX milliseconds ago. The driver has not received any packets from the server.

とか

he last packet successfully received from the server was XXXX milliseconds ago. The last packet sent successfully to the server was XXXX milliseconds ago.

が出る時って、おそらくクライアントサイドのタイムアウト設定によって「諦めた」時。


Connector/Jの場合、この辺読んでおくと良いですね。
https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-configuration-properties.html


jdbcドライバで設定してない場合だと、OSレベルのTCPタイムアウトに引っかかっている可能性あり。


StackTraceを隠蔽(怒)せずにログにちゃんと吐いていれば、
Caused by: java.net.SocketTimeoutException: Read timed out
とか出るよね。


根本原因は、色々あったりします。
NW自体の遅延。元々の距離の問題とか、輻輳とか、中継機器の一時的なトラブルとか。
MySQLサーバ内の処理が「純粋に重い」。MySQLはそもそも複雑なSQL苦手だし、同じソースを通る処理の多重度が高いとセマフォで詰るし。
MySQLサーバで使用しているストレージ側のトラブル。RAIDコントローラとか。
意図せずSWAP使ってたりするときも、この手のエラー出るかも。


その他

まだMySQL 5.7はガチってないんだけど、SELECTを経過時間で強制終了させる
max_execution_time
もハマりそうな予感したので試してみましたが、ここは別のエラーになりますね。

プログラム側でうまいことエラーハンドリングしてあげてください。

execution_timeoutに絞った話は、MySQL Casual Advent Calendar 2017 - Qiitaの9日目の記事として書きました。
SELECT文をタイムアウト強制終了させる「MAX_EXECUTION_TIME」使ってる? - なからなLife




まとめ

  • 接続不良の原因、いろいろあるよね。
  • 対応するパラメータも色々あるよね。
  • いずれにせよ、コネクション切れたのに、コネクションオブジェクトを掴みっぱなしで使い続けようとすると起こるパターンが多いよね。
  • MySQLサーバ側のエラーログは最初に見よう。ここに出力がなければクライアント側メインで調整しつつ、タイムアウト設定確認の中で再びMySQLサーバに戻る感じで。
  • タイムアウト系の設定値、伸ばせば確かに発生頻度減るけど、設計的にそれでいいのかは別問題。業務・アプリ側の要件の次元。
  • アプリ実装に依存して引き起こされてることも多いし、一概にDBのせいにされても困る。


ここまでまとめたはいいけれど、目下、不定期に発生中の障害が解決できてないです。。。つらい。

MySQLトラブルシューティング

MySQLトラブルシューティング

*1:英語の時点でわりと酷い