您的位置:首页 > 数据库 > MySQL

MySQL打开的文件描述符限制

2015-09-09 17:46 666 查看



如果遇到如下错误:

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来实现文件描述符的限制。

转自:http://www.orczhou.com/index.php/2010/10/mysql-open-file-limit/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: