MySQL打开的文件描述符限制

Server 林涛 8989℃ 0评论

 

如果遇到如下错误:

Can't open file: '.\test\mytable.frm' (errno: 24)
shell> perror 24
OS error code 24: Too many open files

这就是MySQL的文件描述不够用了。先说解决办法,再说背后的原因吧。

1. 如何解决

第一步:设置OS参数(如果你有权限的话):

文件/etc/security/limits.conf新增如下行:

mysql soft nofile 65535
mysql hard nofile 65535

上面的配置,是OS限制各个用户能够打开的文件描述符限制(hard soft区别参看man ulimit),新增上面两行,表示mysql用户能够打开65535个文件描述符(可以使用lsof -u mysql|wc -l查看当前打开了多少个文件描述符)。

第二步:修改MySQL参数:
在MySQL配置文件my.cnf中新增下面的行

open_files_limit = 65535
innodb_open_files=65535

然后重启你的MySQL一般问题就解决了。

2. 背后的问题


上面的办法一般就能解决问题了。不过在实践中发现,在my.cnf中设置的参数open_files_limit值是无效的,即MySQL启动后open_files_limit始终以OS的文件描述符为准。(版本MySQL5.1.45 RHEL5.4)


 

root@(none) 11:27:07>show global variables like "%open_files_limit%"; +——————+——-+ | Variable_name | Value | +——————+——-+ | open_files_limit | 20000 | +——————+——-+ 1 row in set (0.01 sec) root@(none) 11:27:09>system ulimit -n 20000

那my.cnf参数open_files_limit是否真的是没用呢?接下来会是一篇很长、很蛋疼的关于该问题的研究,如果不是很有时间,不建议看下去。

3. 源代码中如何设置open_files_limit

3.1 实验验证

配置文件中配置:open_files_limit = 10000;$ulimit -n 20000;启动数据库,观察:


 

root@(none) 11:48:57>show global variables like "%open_files_limit%"; +——————+——-+ | Variable_name | Value | +——————+——-+ | open_files_limit | 20000 | +——————+——-+

看到参数open_files_limit确实没有作用(已经实验了很多次了)。

3.1 代码追踪watch

首先使用gdb/watch观察open_files_limit变量值,


 

Old value = 0 New value = 10000 0x000000000086a6f2 in setval () Old value = 10000 New value = 20000 0x00000000005ba159 in init_common_variables ()

看到,配置文件my.cnf中的10000,又没有生效。

3.2 梦境:更多的代码细节


 

wanted_files= 10+max_connections+table_cache_size*2; max_open_files= max(max(wanted_files, max_connections*5), open_files_limit); files= my_set_max_open_files(max_open_files);

这一段可以看到,首先会比较open_files_limit,max_connections*5和10+max_connections+table_cache_size*2中最大值,并调用函数my_set_max_open_files设置文件描述符限制。

继续跟踪函数my_set_max_open_files,在文件mysys/my_file.c中:


 

uint my_set_max_open_files(uint files) { struct st_my_file_info *tmp; DBUG_ENTER("my_set_max_open_files"); DBUG_PRINT("enter",("files: %u my_file_limit: %u", files, my_file_limit)); files= set_max_open_files(min(files, OS_FILE_LIMIT)); ## #define OS_FILE_LIMIT 65535 ##

这里继续,调用了set_max_open_files,仍然在文件mysys/my_file.c:


 

static uint set_max_open_files(uint max_file_limit) { struct rlimit rlimit; uint old_cur; DBUG_ENTER("set_max_open_files"); DBUG_PRINT("enter",("files: %u", max_file_limit)); if (!getrlimit(RLIMIT_NOFILE,&rlimit)) { old_cur= (uint) rlimit.rlim_cur; DBUG_PRINT("info", ("rlim_cur: %u rlim_max: %u", (uint) rlimit.rlim_cur, (uint) rlimit.rlim_max)); if (rlimit.rlim_cur == RLIM_INFINITY) rlimit.rlim_cur = max_file_limit; if (rlimit.rlim_cur >= max_file_limit) DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */ rlimit.rlim_cur= rlimit.rlim_max= max_file_limit; if (setrlimit(RLIMIT_NOFILE, &rlimit)) max_file_limit= old_cur; /* Use original value */ else { rlimit.rlim_cur= 0; /* Safety if next call fails */ (void) getrlimit(RLIMIT_NOFILE,&rlimit); DBUG_PRINT("info", ("rlim_cur: %u", (uint) rlimit.rlim_cur)); if (rlimit.rlim_cur) /* If call didn't fail */ max_file_limit= (uint) rlimit.rlim_cur; } } DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit)); DBUG_RETURN(max_file_limit); }

上面的代码中:


 

if (rlimit.rlim_cur >= max_file_limit) DBUG_RETURN(rlimit.rlim_cur); /* purecov: inspected */

看到,如果当前的OS文件描述符(rlimit.rlim_cur)限制大于配置文件的参数max_file_limit,则函数返回不再对使用描述符做任何限制。

所以,当配置文件设置的open_files_limit值小于OS文件描述符限制时,将不会调用setrlimit,即文件描述符限制是OS的限制值。

这里代码还可以看到,如果open_files_limit的值大于OS文件描述符限制时,将会尝试调用setrlimit(RLIMIT_NOFILE, &rlimit)将描述符设置open_files_limit,事实上,setrlimit会调用失败。

所以,当配置文件设置的open_files_limit值大于OS文件描述符限制时,会调用setrlimit,但是会失败,即文件描述符限制仍然是OS的限制值。

即,无论如何,MySQL打开的文件描述符限制都是OS的文件描述符限制,和配置文件中open_files_limit的设置没有关系。

(如果看到这,说明你已经成功进入第二层梦境了:my_set_max_open_files和set_max_open_files)

4 代码外的代码

估计看到这,看客已经晕了吧。因为这个问题并没有那么重要,解决办法也很简单,所以确实也不用追究的这么细。不过都到这儿了,我打算继续。

在如果想设置open-files-limit也有成功的时候,那就是使用root帐号,运行mysqld_safe脚本启动MySQL(或者使用mysql.server启动),如果使用–open-files-limit是可以成功设置的:


 

./mysqld_safe –open-files-limit=25000 & root@(none) 02:50:54>show variables like "%open_files_limit%"; +——————+——-+ | Variable_name | Value | +——————+——-+ | open_files_limit | 25000 | +——————+——-+

这是因为在mysqld_safe中做了如下处理:


 

if test -w / -o "$USER" = "root" then …… if test -n "$open_files" then ulimit -n $open_files append_arg_to_args "–open-files-limit=$open_files" fi fi

可以看到,直接或间接使用mysqld_safe启动MySQL时,其实是在启动mysqld程序之前,调用了ulimit -n $open_files来实现文件描述符的限制。

参考:

1. MySQL Source Code

2. mysql如何计算打开文件数

3. MySQL Manual

如需转载请注明: 转载自26点的博客

本文链接地址: MySQL打开的文件描述符限制

转载请注明:26点的博客 » MySQL打开的文件描述符限制

喜欢 (0)
发表我的评论
取消评论

表情
(2)个小伙伴在吐槽
  1. 看过必回,人品超好!
    美莲2015-12-02 16:59 回复
  2. 经验之谈,谢谢博主了,请继续努力
    惠茜2015-11-29 18:47 回复