案例
当数据库出现 Cannot open './kd_drp/t_occpic_rebatemonthly.ibd’这类错误时,一般都是文件损坏后无法打开表空间。但是今天分享案例是由于open files限制引起的。
2025-02-15T23:05:53.254309+08:00 4 [ERROR] [MY-012592] [InnoDB] Operating system error number 24 in a file operation.```
2025-02-15T23:05:53.254323+08:00 4 [ERROR] [MY-012596] [InnoDB] Error number 24 means 'Too many open files'
2025-02-15T23:05:53.254332+08:00 4 [Note] [MY-012597] [InnoDB] Refer to your operating system documentation for operating system error code information.
2025-02-15T23:05:53.254340+08:00 4 [Warning] [MY-012093] [InnoDB] Cannot open './kd_drp/t_occpic_rebatemonthly.ibd'. Have you deleted .ibd files under a running mysqld server?
复制
mysql error.log出现上面错误,实例无法启动。
1、报错原因
mysql配置文件中设置了open_files_limit参数,这个参数对mysql进程有效,所以mysql服务只能使用到65535个文件,文件数不够时出现上文报错。
[root@0i fd]# cat /data/mysql/conf/my.cnf |grep open_files_limit
open_files_limit = 65535
复制
2、解决方法
调大my.cnf中的open_files_limit参数的值后,故障恢复。如果my.cnf没有指定open_files_limit参数,则会通过其他方式获取。
建议:mysql进程获取limit限制参数的途径较多,为了保证其够用,遵循一个原则,同时调整大root用户、运行mysql服务的普通用户以及my.cnf中的open_files_limit。最终,mysql使用的limit参数值可查看/proc/pidof mysqld
/limits文件。
root@localhost: 14:14: [(none)]> show variables like 'open_files_limit';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| open_files_limit | 524288 |
+------------------+--------+
1 row in set (0.01 sec)
复制
实例启动后,mysql进程有一套自己的limit参数值,而非操作系统看到的limit.conf中的值。下面代码中的Max open files就是最大文件打开数,与数据库中查到的open_files_limit对应。
cat /proc/`pidof mysqld`/limits
Limit Soft Limit Hard Limit Units
Max cpu time unlimited unlimited seconds
Max file size unlimited unlimited bytes
Max data size unlimited unlimited bytes
Max stack size 8388608 unlimited bytes
Max core file size 0 unlimited bytes
Max resident set unlimited unlimited bytes
Max processes 524288 524288 processes
Max open files 524288 524288 files
Max locked memory 65536 65536 bytes
Max address space unlimited unlimited bytes
Max file locks unlimited unlimited locks
Max pending signals 127832 127832 signals
Max msgqueue size 819200 819200 bytes
复制
3、open_files_limit 赋值优先级
赋予open_files_limit的值时,不同启动方式下的结果不同,优先级顺序如下:
(这里分享的结论只供参考,如有问题,欢迎评论区交流。)
首先,my.cnf中open_files_limit优先级最高,只要加了这个参数,就会覆盖其他方式获取的值。
其次,当没有open_files_limit参数时,分mysqld和mysqld_safe两种启动方式:
- mysqld启动时,读取当前用户下limit.conf中的最大文件打开数。即root用户启动时,读取root用户下的limits.conf文件,mysql用户启动时,读取mysql用户下的limits.conf文件。
- mysqld_safe启动时,mysql会调用my_print_defaults方法计算一个值(具体计算规则见下文),然后使用ulimit -n修改当前bash,然后被数据库读取。因此,mysqld_safe会忽略当前用户下的limit.conf中的值。
下面是mysqld_safe脚本修改open files相关的两段代码。
# Get first arguments from the my.cnf file, groups [mysqld] and [mysqld_safe]
# and then merge with the command line arguments
if test -x "$MY_BASEDIR_VERSION/bin/my_print_defaults" ; then
print_defaults="$MY_BASEDIR_VERSION/bin/my_print_defaults"
elif test -x "/usr/local/mysql/bin/my_print_defaults" ; then
print_defaults="/usr/local/mysql/bin/my_print_defaults"
else
print_defaults="my_print_defaults"
fi
复制
USER_OPTION=""
if test -w / -o "$USER" = "root"
then
if test "$user" != "root" -o $SET_USER = 1
then
USER_OPTION="--user=$user"
fi
if test -n "$open_files"
then
ulimit -n "$open_files"
fi
fi
if test -n "$open_files"
then
append_arg_to_args "--open-files-limit=$open_files"
fi
复制
所以,open_file_limit的值来源较多,运维过程中需要关注。另外,在数据库升级过程中,mysql会打开所有表,所需文件数更多,需要预留一个较大的值。
my.cnf不设open_files_limit参数时,其计算规则如下,选择下面两种算法中较大的值:
- 10 + max_connections + (table_open_cache * 2)
- max_connections * 5
扩展:关于参数innodb_open_files
上文中的open_files_limit对整个实例有效,而innodb_open_files参数控制innodb层同时打开的文件数量(open),且文件数大于参数值也不会报错,它会通过LRU链表淘汰。这一点与open_files_limit是不一样的。
在生产中建议设置一个小于open_files_limit的值(大概少个10000左右吧,即innodb_open_files设置为open_files_limit-10000)。