应先查MySQL进程实际生效的ulimit值:cat /proc/$(pgrep mysqld)/limits | grep "Max open files",而非依赖当前终端ulimit -n;若显示Soft/Hard Limit仅1024,说明systemd未正确配置LimitNOFILE,需通过systemctl edit修改并重启验证。

确认 MySQL 进程实际生效的 ulimit 值
别信你当前终端的 ulimit -n 输出——MySQL 启动时继承的是它父进程(通常是 systemd)的限制,不是你登录 shell 的值。直接查进程本身才准。
执行:cat /proc/$(pgrep mysqld)/limits | grep "Max open files",看 Soft Limit 和 Hard Limit 是否远低于预期(比如只有 1024)。如果这里卡在 1024,哪怕你手动改过 ulimit -n 65535 也没用,因为 MySQL 根本没继承到。
- 若 MySQL 没在跑,先用
sudo systemctl show mysqld | grep LimitNOFILE看 systemd 当前配置 - 临时验证:用
sudo -u mysql sh -c 'ulimit -n; exec mysqld --defaults-file=/etc/my.cnf --console'启动,观察是否报错 - 注意服务名可能是
mysql或mariadb,得对应替换pgrep和systemctl中的名称
区分是真耗尽还是假报警
“Too many open files” 不一定代表句柄真的用光了,也可能是 MySQL 自己打开太多但没释放,或者内部参数压低了上限。
先统计真实占用:lsof -p $(pgrep mysqld) | wc -l,再对比上一步看到的 Max open files 值:
- 如果占用数接近或等于上限 → 确实被 ulimit 卡住
- 如果占用数只有几百,但 error log 还在报错 → 可能是
open_files_limit在 my.cnf 里设得太小,或table_open_cache不足导致频繁开闭表文件 - 运行
SHOW GLOBAL STATUS LIKE 'Open_tables';和SHOW GLOBAL STATUS LIKE 'Opened_tables';,若后者远大于前者,说明缓存太小,不是 ulimit 问题
检查 systemd 服务配置是否真正生效
90% 的线上 MySQL 由 systemd 管理,/etc/security/limits.conf 对它基本无效——systemd daemon 不走 PAM session 流程,改 limits.conf 是白忙。
必须通过 systemd unit 覆盖配置:
- 执行
sudo systemctl edit mysql(或mysqld),写入:[Service] LimitNOFILE=65535
- 保存后执行
sudo systemctl daemon-reload && sudo systemctl restart mysql - 重启完立刻验证:
cat /proc/$(pgrep mysqld)/limits | grep "Max open files",确保两列数字都变成 65535 - 注意:
LimitNOFILE=infinity在某些内核版本会 fallback 到 4096,别用
同步校准 MySQL 内部的 open_files_limit
MySQL 启动时会向上申请句柄数,但它不会自动拿满系统允许值。如果 my.cnf 里 open_files_limit 设得太小,MySQL 就会自我限流,哪怕 ulimit 是 65535,它也只敢开几千个文件。
在 [mysqld] 段加一行:
open_files_limit = 65535
- 这个值不能超过进程 ulimit,否则启动时会被 MySQL 自动下调
- 建议设为 ulimit 值的 70% 左右(如 ulimit=65535,则设 40000),给 binlog、socket、临时表留余量
- 改完记得
sudo systemctl restart mysql,并检查 error log 是否还有 “Could not increase number of max_open_files” 提示
真正难的不是改数字,而是确认每一层限制都对齐:systemd 的 LimitNOFILE、MySQL 的 open_files_limit、内核的 fs.file-max,三者缺一不可。漏掉任意一层,MySQL 都可能在某个时刻突然哑火。


















