从源码角度谈谈open_files_limit的生成逻辑及”Too many open files”的解决思路

“Too many open files”是一个比较常见的错误,不仅仅是在 MySQL 中。只要是在 Linux 中启动的进程,都有可能遇到这个错误。

究其原因,是进程打开的文件描述符数超过了自身的限制。

这个限制,是进程级别的,在 MySQL 中,与 open_files_limit 的设置有关。

但是 open_files_limit 并不是所设即所得,配置的和实际生效的并不完全一样。

配置文件中的配置。

切换到 mysql 用户下,启动实例,看看这三个参数,外加 table_definition_cache 的实际有效值。

实际值和配置值,竟然没有一个相同,是不是很意外?

下面,结合源码看看这三个参数的生成逻辑。

vim mysql-5.7.34/sql/mysqld.cc

其中:

  • sys_var_init():初始化系统参数(System Variables)。
  • adjust_related_options():在初始化的基础上,调整相关参数的配置。

下面看看 adjust_related_options 的处理逻辑

其中,

  • adjust_open_files_limit:调整 open_files_limit,调整后的值会赋值给 requested_open_files。
  • adjust_max_connections:调整 max_connections,依赖于 requested_open_files,即调整后的 open_files_limit。
  • adjust_table_cache_size:调整 table_cache_size,依赖于调整后的 open_files_limit 和 max_connections。
  • adjust_table_def_size:调整 table_definition_cache,依赖于调整后的 table_cache_size。

这四个参数中,最为复杂的是 open_files_limit 的调整,它涉及到系统对进程能打开的最大文件描述符的限制。

不妨,先基于调整后的 open_files_limit,看看后面三个参数的调整逻辑。

2.1 max_connections

首先,看看 max_connections 的取值逻辑

其中,

  • requested_open_files:调整后的 open_files_limit。具体在本 Demo,是 2048。
  • TABLE_OPEN_CACHE_MIN:常量,代表 table_cache_size 可允许的最小值。
#define TABLE_OPEN_CACHE_MIN    400
  • max_connections:max_connections 的初始值。取配置文件中的配置,2000。如果配置文件中没有设置,则默认为 151。

基于代码中的计算逻辑,limit = 2048 – 10 – 400 * 2 = 1238。

1238 小于 2000,所以 max_connections 最后取值为 1238,与 Demo 开头的查询结果一致。

2.2 table_cache_size

接下来,看看 table_cache_size 的取值逻辑

其中,

  • max_connections:max_connections 调整后的的值,即 1238。
  • table_cache_instances:table_open_cache_instances。
  • table_cache_size:table_cache_size 的初始值。在这个 Demo 中,是 1000。

基于代码中的计算逻辑,limit = max( ( 2048 – 10 – 1238 ) / 2,400 ) = 400。

400 小于 1000,所以,table_cache_size 最后取值为 400。

2.3 table_definition_cache

接下来,看看 table_definition_cache 的取值逻辑

同样,table_cache_size 也是调整后的值,即 400。

default_value = min( 400 + 400 / 2,2000) = 600。

顾名思义,default_value 是默认值。如果没有显式设置 table_definition_cache,则取默认值。

2.4 open_files_limit

最后看看 open_files_limit 的取值逻辑

因为是最先调整的参数,所以,这里的 max_connections,table_cache_size,open_files_limit 都是初始值。

基于代码中的计算逻辑,

  • limit_1 = 10 + max_connections + table_cache_size _ 2 = 10 + 2000 + 1000 _ 2 = 4100
  • limit_2 = max_connections _ 5 = 2000 _ 5 = 10000
  • limit_3 = open_files_limit ? open_files_limit : 5000 = 65536

request_open_files = max(max(limit_1, limit_2), limit_3) = 65536,可理解为需要打开的文件数。

接下来,调用 my_set_max_open_files 函数获取 effective_open_files,后者是实际能打开的最大文件数。

看看 my_set_max_open_files 的处理逻辑。

相关的常量定义如下:

可见,在 Linux 系统中,MY_MIN(files, OS_FILE_LIMIT)还是等于 files,即 request_open_files。

这段代码里,重点还是调用 set_max_open_files 函数。

下面看看 set_max_open_files 的处理逻辑。

这里,使用了两个系统函数:getrlimit(),setrlimit(),分别用来获取和设置系统的资源限制(resource limit)。

其中,

  • resource:资源类型。 可设置的资源类型有:RLIMIT_AS,RLIMIT_CORE,RLIMIT_CPU,RLIMIT_DATA,RLIMIT_FSIZE,RLIMIT_LOCKS,RLIMIT_MEMLOCK,RLIMIT_MSGQUEUE,RLIMIT_NICE,RLIMIT_NOFILE,RLIMIT_NPROC,RLIMIT_RSS,RLIMIT_RTPRIO,RLIMIT_RTTIME,RLIMIT_SIGPENDING,RLIMIT_STACK。 其中,RLIMIT_NOFILE 代表进程能打开的最大文件描述符数。
  • rlimit:结构体,定义了两个变量。
struct rlimit {
    rlim_t rlim_cur;  /* Soft limit */
    rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};

其中,rlim_cur 代表软限制,rlim_max 代表硬限制。 注意:资源限制,真正起限制作用的是软限制,硬限制只是限制了软限制可设置的上限。

这两个函数,如果调用成功,则返回 0,反之,则返回-1。

了解完背景知识,接下来看看set_max_open_files具体的处理逻辑。

首先,获取进程的 RLIMIT_NOFILE。在此基础上,

这就意味着,在软限制大于等于 max_file_limit 的情况下:

接下来,就只有一种情况,即软限制小于max_file_limit。此时,会将max_file_limit赋值给软限制和硬限制,并尝试修改进程的RLIMIT_NOFILE。

这就意味着,在软限制小于 max_file_limit 的情况下:

什么情况下,能修改成功呢?

注意,这里的启动用户指的是启动 mysqld_safe 或 mysqld 的用户,不是运行用户(配置文件中指定的 user)。

既然 max_file_limit 的设置与进程的 RLIMIT_NOFILE 的有关,接下来,我们看看该进程的 RLIMIT_NOFILE。

其中,26424 是进程的 PID。

2048 是软限制,4096 是硬限制。

回到源码,启动用户是 mysql,2048 小于 65536,且 65536 大于 4096,所以不能修改进程的 RLIMIT_NOFILE。故而,最后,我们看到的 open_files_limit 是 2048。

同样的设置,如果 mysqld 是在 root 用户下启动呢?

竟然不是 65536,而是 100000。

3.1 启动用户

这个实际上与启动用户有关。用户决定了通过它启动的进程的资源上限,包括文件描述符数。

这里的上限,是在/etc/security/limits.conf 中定义的。如本 Demo 中的配置,

注意,这里虽然配置的是用户,但实际上限制的是通过用户启动的进程。

一般建议显式设置,如果不设置的话,则默认是 1024。1024,对于大多数应用来说,还是太小了。

用户可自由调整软限制和硬限制的大小,但需遵循以下规则:

除此之外,

3.2 启动脚本

除了启动用户,启动脚本也能限制进程的资源使用。

看下面这个 Demo,同样是在 root 用户下,只不过这次是通过 mysqld_safe 启动。

同样是在 root 用户下,如果是通过 mysqld_safe 启动呢?

竟然是 65536。

这背后,其实是 mysqld_safe 在”捣鬼”。

具体来说,如果配置文件或命令行中显式设置了 open_files_limit,则 mysqld_safe 在启动的过程中会通过”ulimit -n $open_files”调整进程的最大文件描述符数。

除了启动脚本,在 CentOS 7 中,如果是通过 systemd 管理进程,还可通过 LimitNOFILE 在进程级别设置文件描述符数的上限。

3.3 小结

进程能打开的最大文件描述符数由启动用户决定。除此之外,启动脚本(包括systemd),能在进程级别设置文件描述符数的上限。

文件描述符不够,在 MySQL 中的影响主要有:

4.1 常见原因

出现”Too many open files”,常见的原因有两个:

之所以要区分这两种场景,是因为两者的解决方法不一样。

4.2 解决方法

确定了原因,下面看看解决方法。

如果系统不支持在线调整进程的最大文件描述符数。就只能修改配置,重启实例。具体步骤如下:

无论是在线,还是通过/etc/security/limits.conf,调整进程的最大文件描述符数,在调整的时候,注意以下几点:

在 MySQL 状态变量中,没有能直观反映文件描述符的指标。

看下面这个 Demo。

open_files_limit 等于 1024,当文件描述符不足,出现”Too many open files”错误时,mysql 状态变量及 lsof 的统计结果如下:

其中,

  • Innodb_num_open_files:InnoDB 当前打开的文件数。InnoDB 同时能打开的最大文件数由 innodb_open_files 参数决定,后者默认为-1,基于 table_cache_size 自动调整。 innodb_open_files 并不是一个硬限制。如果 InnoDB 打开的文件数达到了 innodb_open_files 的限制,则会通过 LRU 算法关闭其它文件。
    if (innobase_open_files < 11) {
            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; }}

* Open_files:当前打开的文件数。
* Opened_files:打开过的文件数。

从输出结果来看,这三个变量的值与 open_files_limit(1024)相去甚远,不如 lsof 的准确。

其中,9020 是进程的 PID。

lsof 同样适用于其它进程的统计。

Original: https://www.cnblogs.com/ivictor/p/14900930.html
Author: iVictor
Title: 从源码角度谈谈open_files_limit的生成逻辑及”Too many open files”的解决思路

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/599401/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 2021长安杯wp

    案件背景 2021年4月25日,上午8点左右,警方接到被害人金某报案,声称自己被敲诈数万元;经询问,昨日金某被嫌疑人诱导果聊,下载了某果聊软件,导致自己的通讯录和果聊视频被嫌疑人获…

    数据库 2023年6月11日
    066
  • 2022-8-20 数据库连接池

    1. 概念:其实就是一个容器(集合),存放数据库连接的容器。 &#x5F53;&#x7CFB;&#x7EDF;&#x521D;&#x59CB…

    数据库 2023年6月14日
    099
  • 数据类型

    布尔类型:true和false;占一个位 public class Demo01 {    public static void main(String[] args) { Ori…

    数据库 2023年6月11日
    088
  • xtrabackup2版本和xtrabackup8版本对比

    导语在使用xtrabackup8版本对mysql8版本进行备份恢复搭建从库的时候,继续使用xtrabackup2版本的方式,从xtrabackup_binlog_info 文件中找…

    数据库 2023年6月16日
    0104
  • MySQL学习笔记

    MySQL学习笔记 解决MYSQL中文乱码问题 一、乱码的原因: 1、 client客户端的编码不是utf8 2、server端的编码不是utf8 3、database数据库的编码…

    数据库 2023年5月24日
    096
  • 设计模式之享元模式

    一、享元模式模式:享元模式是实现对象重用的一种方式,适用于为了尽可能的减少对象的重复创建而增大资源开销的情况,与单例模式有类似的作用。 二、实现思路 :对象被第一次创建后,如果后续…

    数据库 2023年6月14日
    076
  • podman

    podman Podman 是一个无守护程序、开源的 Linux 原生工具,旨在使用开放容器计划 (OCI) 容器和容器映像轻松查找、运行、构建、共享和部署应用程序。Podman …

    数据库 2023年6月14日
    076
  • MYSQL/Oracle中常用函数总结

    记录在日常工作或者学习中中使用到的函数,以下是做一个备忘~ MySQL: 窗口函数: 原文地址:https://zhuanlan.zhihu.com/p/92654574 1、窗口…

    数据库 2023年6月14日
    090
  • Redis学习(1)—Redis概述

    什么是NoSQL NoSQL:Not Only SQL,意思不仅仅是SQL,它是属于 非关系型数据库。那什么是关系型数据库?数据结构是一种有行有列的数据库。 NoSQL数据库是为了…

    数据库 2023年6月14日
    070
  • Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块 os.access()方法

    Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块 os.access()方法 stat模块描述了os.stat(filename)…

    数据库 2023年6月9日
    066
  • 线程池执行流程图

    public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeU…

    数据库 2023年6月16日
    0113
  • B+树索引页大小是如何确定的?

    B+树简介 在正式介绍本文的主题前,需要对 B+ 树有一定的了解,B+树是一种磁盘上数据的索引结构,大概长这个样子。 B+树的叶子节点是所有的数据,非叶子节点称为索引页,索引页里有…

    数据库 2023年6月14日
    069
  • 系统稳定性—OutOfMemoryError常见原因及解决方法

    当 JVM内存严重不足时,就会抛出 java.lang.OutOfMemoryError错误。本文总结了常见的 OOM原因及其解决方法,如下图所示。如有遗漏或错误,欢迎补充指正。 …

    数据库 2023年6月11日
    089
  • MyRocks DDL原理

    最近一个日常实例在做DDL过程中,直接把数据库给干趴下了,问题还是比较严重的,于是赶紧排查问题,撸了下crash堆栈和alert日志,发现是在去除唯一约束的场景下,MyRocks存…

    数据库 2023年6月9日
    087
  • Facade 外观(结构型)

    Facade 外观 (结构型) 一:描述: Facade 外观模式是为子系统至客户端之间提供简单的一致的接口,来降低耦合度。 二:模式图 三:实现代码简单例子: 1 、业务模块; …

    数据库 2023年6月11日
    093
  • 一个校验接口引发的思考–我真的了解Response吗

    一个校验接口 最近,我需要对接一个外部接口,基本功能是:校验指定的门店是否完善了货运信息。接口大致是这样的: POST https://******/Dealer/CheckCar…

    数据库 2023年6月6日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球