動かざることバグの如し

近づきたいよ 君の理想に

MySQLのCould not increase number of max_open_filesエラーとLimitNOFILE

環境

  • MySQL 8.0

発端

journalctl -u mysqld でログを眺めていたら、以下の警告が繰り返し出ているのに気づいた。

[Warning] Could not increase number of max_open_files to more than 65536 (request: 100005)
[Warning] Could not increase number of max_open_files to more than 65536 (request: 100005)

「65536以上に引き上げられなかった」という警告で、放置するとアクセス増加時に Too many open files エラーが発生してMySQLが停止するリスクがある。

65536の数値は自分が設定したもので、デフォルトの1024だと少なすぎるのでsystemdの設定を変更してMySQLのLimitNOFILEを65536に指定していた。

[Service]
LimitNOFILE=65536

また my.cnf でも65535を指定していた。

max_allowed_packet=64MB
open_files_limit=65535
key_cache_segments=32

65536でも足りないのか。。?あと100005という中途半端な値を要求している点はなんだ?

解決策

原因はMySQLとsystemdの設定値の乖離である。 MySQLが 100005 のファイルディスクリプタを必要としているのに対し、systemdの LimitNOFILE のデフォルト上限が 65536 に制限されているため、要求が通らずに警告となっている。

systemd 側の制限を引き上げれば解消できる。

systemctl edit mysqld

オーバーライド用のファイルが開くので、以下を追記して保存する。

[Service]
LimitNOFILE=100005

保存後、設定を反映させる。

systemctl daemon-reload
systemctl restart mysqld

再起動後に journalctl -u mysqld を確認して警告が出なくなっていれば完了だ。

ソースは?

my.cnfopen_files_limit=65535 と書いているのに、なぜMySQLは request: 100005 という値を要求したのか。

MySQLは open_files_limit の設定値をそのまま使うわけではない。起動時に以下の計算を行い、最大の値をOSに要求する仕様になっている。

  • 10 + max_connections + (table_open_cache × 2)
  • max_connections × 5
  • OSの制限値(正の値の場合)
  • 起動時に open_files_limit の指定がない場合は 5000

これはMySQL 8.4の公式リファレンスマニュアルの open_files_limit の項目に明記されている。

dev.mysql.com

The effective open_files_limit value is based on the value specified at system startup (if any) and the values of max_connections and table_open_cache, using these formulas:
- 10 + max_connections + (table_open_cache * 2)
- max_connections * 5
- operating system limit (if positive)
- if operating system limit is infinity: open_files_limit value specified at startup, 5000 if none
The server attempts to obtain the number of file descriptors using the maximum of those four values.

今回の 100005 という値も、max_connections が 20000 前後に設定されていたとすれば 20000 × 5 = 100000 となり、内部的なオーバーヘッドを加えて 100005 が算出される。つまり my.cnfopen_files_limit 設定が計算結果を下回ったため、MySQLが自動的に大きい方の値をOSに要求したわけだ。