前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >[MYSQL] open_files_limit 和 innodb_open_files 参数设置为啥不生效?

[MYSQL] open_files_limit 和 innodb_open_files 参数设置为啥不生效?

原创
作者头像
大大刺猬
发布2024-03-29 14:14:01
发布2024-03-29 14:14:01
6770
举报
文章被收录于专栏:大大刺猬大大刺猬

环境

mysql版本: 5.7.x

/etc/security/limits.conf

代码语言:conf
复制
* soft nproc 65535
* hard nproc 65535
* soft nofile 65536
* hard nofile 65536

mysql参数文件:

代码语言:conf
复制
open_files_limit = 63000
table_open_cache = 16000
innodb_open_files = 65535
max_connections = 2000

先别管这参数是否合理.(合理的话就不会遇到这个问题了-_-)

问题

注:mysql的启动参数是有加 --user=mysql

  1. mysql使用root启动, innodb_open_files 无效
  2. mysql使用mysql启动, open_files_limit 无效

问题1

使用 /ect/rc.local 设置的开机自启.(未配置服务).

mysql开机自启后,使用 show global variables like '%open%'; 查询open_file相关的参数

注: 开机自启是使用root启动的, mysqld_safe的用户是root 是正常的, mysqld的用户是mysql也是正常.

代码语言:cnf
复制
innodb_open_files = 16000  不正常 我们设置的是63000
open_files_limit = 63000   正常

问题2

我们使用 mysql 重启mysqld进程后 再次查看参数

代码语言:cnf
复制
innodb_open_files = 65535  正常(其实也不正常)
open_files_limit = 65536   不正常(我们设置的63000)

分析思路

innodb_open_files 是在同一时刻能打开的innodb文件最大数量

open_files_limit 是mysql打开的最大文件数限制

以前有分析过 mysql启动流程: https://cloud.tencent.com/developer/article/2123456 所以我们可以快速锁定文件 sql/mysqld.cc

如果没看过的话, 可以使用 grep -r keyword mysqld_source_dir 来找相关函数. 然后使用gdb之类的来验证.

open_files_limit

我们查看源码文件 sql/mysqld.ccvoid adjust_open_files_limit(ulong *requested_open_files)中有如下记录

代码语言:c++
复制
limit_1= 10 + max_connections + table_cache_size * 2;
limit_2= max_connections * 5;
limit_3= open_files_limit ? open_files_limit : 5000;

request_open_files= max<ulong>(max<ulong>(limit_1, limit_2), limit_3);
effective_open_files= my_set_max_open_files(request_open_files);

请求的打开文件数量为 那三个值中的最大值, 即 request_open_files = 63000

有效打开文件 来自my_set_max_open_files函数. 我们查看源码 mysys/my_file.c

注:

include/my_global.h:#define MY_FILE_MIN 2048

/usr/include/limits.h #define UINT_MAX 4294967295U

include/my_global.h:#define OS_FILE_LIMIT UINT_MAX

代码语言:c++
复制
uint my_set_max_open_files(uint files)
{
...
files+= MY_FILE_MIN;
files= set_max_open_files(MY_MIN(files, OS_FILE_LIMIT));
...
}

files 取set_max_open_files的值, 那我们就再看下mysys/my_file.c set_max_open_files吧 (各种套娃...)

代码语言:c++
复制
#if defined(HAVE_GETRLIMIT)
static uint set_max_open_files(uint 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_t) 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;
    }


}
#else
static uint set_max_open_files(uint max_file_limit)
{   
  /* We don't know the limit. Return best guess */
  return MY_MIN(max_file_limit, OS_FILE_LIMIT);
}   
#endif

看起来有点长, 没关系, 我们一点点看

如果为做资源限制, 就返回OS_FILE_LIMIT和max_file_limit的最小值. 我们的环境 OS_FILE_LIMIT (4294967295) 是大于MY_MIN(files, OS_FILE_LIMIT) 2048的 所以返回2048

如果有做资源限制, 那就复杂了, 先获取资源限制值, 可以用Python模拟

还是看代码吧

先保存 资源软限制rlimit.rlim_cur 后面要对这个值做修改.

我们主要看setrlimit(RLIMIT_NOFILE, &rlimit) 如果执行成功(root), 那么max_file_limit 就是修改前的软限制. 如果执行失败(mysql)就是当前的软限制

也就是如果是root账号启动 就是以my.cnf文件里面的值为准, 如果是mysql启动的 就可能是 ulimit -n 看到的那个值

innodb_open_files

现在再来看看innodb_open_files吧. 这个就简单多了.

storage/innobase/handler/ha_innodb.cc

代码语言:c++
复制
        if (innobase_open_files < 10) {
                innobase_open_files = 300;
                if (srv_file_per_table && table_cache_size > 300) {
                        innobase_open_files = table_cache_size;
                }
        }

        if (innobase_open_files > (long) open_files_limit) {
                ib::warn() << "innodb_open_files should not be greater"
                       " than the open_files_limit.\n";
                if (innobase_open_files > (long) table_cache_size) {
                        innobase_open_files = table_cache_size;
                }
        }

如果小于10 ,就取300

如果 大于 open_files_limit 并且大于table_cache_size 就告警. 并取table_cache_size

那个告警也能再error_log里面找到

验证

感兴趣的可以使用gdb去调试下, 参考mysql启动流程的那个gdb过程就行. 我这里就直接验证了.

root启动: open_files_limit 取 my.cnf文件的63000 但innodb_open_files是65535 大于 63000 且大于 16000 所以innodb_open_files取值为 16000 均符合要求

mysql启动: open_files_limit 取软限制65536 innodb_open_files小于open_files_limit 不变 均符合要求.

总结

  1. open_files_limit : 如果是root账号启动 以my.cnf文件里面的值为准, 如果是mysql启动, 就可能是 ulimit -n 看到的那个值
  2. innodb_open_files : 如果它大于open_files_limit且大于table_open_cache 则取值table_open_cache
  3. open_files_limit不能大于ulimit -n的值. 取其小的值.(root和mysql都是在这样)
  4. 合理的参数能避免很多问题, 参数设置是否合理, 通常可以查看show global status相关的值来判断.
  5. 很多东西不能马上有用, (比如之前解析的mysql启动流程) 但总会有用的.

题外话: 涉及到代码相关的问题, 看起来就会非常枯燥. 但正是这一点点的枯燥才能构成一个好用的程序. 比如之前解析ibd文件的时候, 看到那一堆数据类型, 存储方式都还不一样, 一点点从源码和注释里面薅出来, 看起来虽然枯燥, 但一旦全部整理出来了, 就很有成就感, 成就感会促使你去继续完善这个程序.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境
  • 问题
    • 问题1
    • 问题2
  • 分析思路
    • open_files_limit
    • innodb_open_files
  • 验证
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档