直接改内核参数fs.file-max不能解决mysql的too many open files问题,真正瓶颈是进程级文件描述符限制;必须同步配置systemd的limitnofile和my.cnf中的open_files_limit,并通过/proc/pid/limits与show variables双重验证生效。

直接改内核参数 fs.file-max 不能解决 MySQL 的 Too many open files 问题——它只管全局系统级上限,不控制单个进程能用多少 fd。真正卡住 MySQL 的,是它自己进程的软硬限制,必须从 systemd 服务配置和 MySQL 启动行为两头堵。
先确认是不是真到了进程级上限
别一上来就改内核,先看 MySQL 进程实际开了多少 fd、系统给它划了多少额度:
-
lsof -p $(pgrep mysqld) | wc -l—— 统计当前打开的文件描述符总数 -
cat /proc/$(pgrep mysqld)/limits | grep "Max open files"—— 查该进程实际生效的软硬限制(注意:不是ulimit -n当前终端的值)
如果前者接近后者(比如 64200 / 65536),说明是进程级瓶颈;如果前者才几百、后者却是 1024,那问题出在启动环境没继承到足够限制,跟内核参数 fs.file-max 毫无关系。
systemd 必须显式配 LimitNOFILE
现代 Linux(RHEL/CentOS 7+、Ubuntu 16.04+)下,/etc/security/limits.conf 默认对 systemd 管理的服务无效。MySQL 启动时根本看不到你给 mysql 用户设的 nofile 值。
- 编辑服务单元文件:
/etc/systemd/system/mysqld.service.d/limits.conf(目录不存在就新建) - 写入:
[Service] LimitNOFILE=65535
- 执行
sudo systemctl daemon-reload && sudo systemctl restart mysqld - 重启后立刻验证:
cat /proc/$(pgrep mysqld)/limits | grep "Max open files"输出必须匹配你设的值
漏掉 daemon-reload 或只 restart 不 reload,配置完全不生效。
my.cnf 里的 open_files_limit 要设对且留余量
这个值不是“指令”,而是 MySQL 启动时向系统申请的期望值,最终取 min(系统允许最大值, 配置值)。设得比 LimitNOFILE 高,MySQL 会静默下调,并在 error log 写 warning。
- 在
[mysqld]段写:open_files_limit = 55000(建议设为LimitNOFILE的 80%~90%,留空间给 error log、slow log、socket、临时表等) - 不能带单位(如
65535K),必须是纯整数 - 重启后进 MySQL 执行:
SHOW VARIABLES LIKE 'open_files_limit';,结果应 ≥ 你写的值
如果仍显示 1024 或远低于预期,说明 LimitNOFILE 没生效,或者被其他同名 service 文件覆盖了(用 systemctl cat mysqld 确认实际加载的是哪个 unit)。
Docker、XtraBackup、DTS 场景要单独处理
这些环境容易漏掉关键环节:
- Docker 运行 MySQL:宿主机的
ulimit不传递给容器,必须在docker run里加--ulimit nofile=65535:65535,或在docker-compose.yml的ulimits字段配 - XtraBackup:它直连 MySQL 数据文件,同样受
mysqld进程的 fd 限制约束,不是调大 xtrabackup 自身 ulimit 就行 - 大量表 + DTS 全库校验:7 万张表时,单事务批量打开所有
.frm文件,open_files_limit即使设到 65535 也可能不够——这时得调高innodb_open_files并考虑分批校验
真正生效的标志只有两个:/proc/PID/limits 显示的值,和 SHOW VARIABLES LIKE 'open_files_limit' 返回的值,必须一致。中间任何一环断开,MySQL 就会回退到默认 1024。











