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

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

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

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

​一、测试Demo

配置文件中的配置。

open_files_limit = 65536
table_open_cache = 1000
max_connections = 2000

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

[root@node1 ~]# su - mysql

[mysql@node1 ~]$ /usr/local/mysql5.7.34/bin/mysqld --defaults-file=/etc/my.cnf &

mysql> select @@open_files_limit,@@table_open_cache,@@max_connections,@@table_definition_cache;
+--------------------+--------------------+-------------------+--------------------------+
| @@open_files_limit | @@table_open_cache | @@max_connections | @@table_definition_cache |
+--------------------+--------------------+-------------------+--------------------------+
| 2048 | 400 | 1238 | 600 |
+--------------------+--------------------+-------------------+--------------------------+
1 row in set (0.01 sec)

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

二、源码分析

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

# vim mysql-5.7.34/sql/mysqld.cc

#ifdef _WIN32
int win_main(int argc, char **argv)
#else
int mysqld_main(int argc, char **argv)
#endif
{
...
sys_var_init();
ulong requested_open_files;
adjust_related_options(&requested_open_files);
...
}

其中:

  • sys_var_init():初始化系统参数(System Variables)。

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

下面看看 adjust_related_options 的处理逻辑

void adjust_related_options(ulong *requested_open_files)
{
/* In bootstrap, disable grant tables (we are about to create them) */
if (opt_bootstrap)
opt_noacl= 1; /* The order is critical here, because of dependencies. */
adjust_open_files_limit(requested_open_files);
adjust_max_connections(*requested_open_files);
adjust_table_cache_size(*requested_open_files);
adjust_table_def_size();
}

其中,

  • 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 的取值逻辑

void adjust_max_connections(ulong requested_open_files)
{
ulong limit; limit= requested_open_files - 10 - TABLE_OPEN_CACHE_MIN * 2; if (limit < max_connections)
{
sql_print_warning("Changed limits: max_connections: %lu (requested %lu)",
limit, max_connections); // This can be done unprotected since it is only called on startup.
max_connections= limit;
}
}

其中,

  • 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 的取值逻辑

void adjust_table_cache_size(ulong requested_open_files)
{
ulong limit; limit= max<ulong>((requested_open_files - 10 - max_connections) / 2,
TABLE_OPEN_CACHE_MIN); if (limit < table_cache_size)
{
sql_print_warning("Changed limits: table_open_cache: %lu (requested %lu)",
limit, table_cache_size); table_cache_size= limit;
} table_cache_size_per_instance= table_cache_size / table_cache_instances;
}

其中,

  • 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 的取值逻辑

void adjust_table_def_size()
{
ulong default_value;
sys_var *var; default_value= min<ulong> (400 + table_cache_size / 2, 2000);
var= intern_find_sys_var(STRING_WITH_LEN("table_definition_cache"));
assert(var != NULL);
var->update_default(default_value); if (! table_definition_cache_specified)
table_def_size= default_value;
}

同样,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 的取值逻辑

void adjust_open_files_limit(ulong *requested_open_files)
{
ulong limit_1;
ulong limit_2;
ulong limit_3;
ulong request_open_files;
ulong effective_open_files; /* MyISAM requires two file handles per table. */
limit_1= 10 + max_connections + table_cache_size * 2; /*
We are trying to allocate no less than max_connections*5 file
handles (i.e. we are trying to set the limit so that they will
be available).
*/
limit_2= max_connections * 5; /* Try to allocate no less than 5000 by default. */
limit_3= open_files_limit ? open_files_limit : 5000; request_open_files= max<ulong>(max<ulong>(limit_1, limit_2), limit_3); /* Notice: my_set_max_open_files() may return more than requested. */
effective_open_files= my_set_max_open_files(request_open_files); if (effective_open_files < request_open_files)
{
if (open_files_limit == 0)
{
sql_print_warning("Changed limits: max_open_files: %lu (requested %lu)",
effective_open_files, request_open_files);
}
else
{
sql_print_warning("Could not increase number of max_open_files to "
"more than %lu (request: %lu)",
effective_open_files, request_open_files);
}
} open_files_limit= effective_open_files;
if (requested_open_files)
*requested_open_files= min<ulong>(effective_open_files, request_open_files);
}

因为是最先调整的参数,所以,这里的 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 的处理逻辑。

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+= MY_FILE_MIN;
files= set_max_open_files(MY_MIN(files, OS_FILE_LIMIT));
if (files <= MY_NFILE)
DBUG_RETURN(files); if (!(tmp= (struct st_my_file_info*) my_malloc(key_memory_my_file_info,
sizeof(*tmp) * files,
MYF(MY_WME))))
DBUG_RETURN(MY_NFILE); /* Copy any initialized files */
memcpy((char*) tmp, (char*) my_file_info,
sizeof(*tmp) * MY_MIN(my_file_limit, files));
memset((tmp + my_file_limit), 0,
MY_MAX((int) (files - my_file_limit), 0) * sizeof(*tmp));
my_free_open_file_info(); /* Free if already allocated */
my_file_info= tmp;
my_file_limit= files;
DBUG_PRINT("exit",("files: %u", files));
DBUG_RETURN(files);
}

相关的常量定义如下:

#ifdef _WIN32
#define MY_FILE_MIN 2048
#else
#define MY_FILE_MIN 0
#endif #ifdef _WIN32
#define MY_NFILE (16384 + MY_FILE_MIN)
#else
#define MY_NFILE 64
#endif #define MY_MIN(a, b) ((a) < (b) ? (a) : (b)) #define OS_FILE_LIMIT UINT_MAX

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

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

下面看看 set_max_open_files 的处理逻辑。

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_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;
}
}
DBUG_PRINT("exit",("max_file_limit: %u", max_file_limit));
DBUG_RETURN(max_file_limit);
}

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

#include <sys/resource.h>

int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);

其中,

  • 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。在此基础上,

  1. 如果软限制等于 RLIM_INFINITY(无限制),则将 max_file_limit 赋值给软限制。

  2. 如果软限制大于等于 max_file_limit,则返回软限制,并退出函数。

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

  1. 软限制等于 RLIM_INFINITY(无限制),返回的是 max_file_limit。

  2. 软限制不是 RLIM_INFINITY,且大于 max_file_limit,则返回软限制。

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

  1. 如果不能修改成功,则将进程的软限制赋值给 max_file_limit。

  2. 如果能修改成功,则再次获取进程的 RLIMIT_NOFILE,将其赋值给 max_file_limit。这样做,可避免修改实际没有生效的问题。

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

  1. 如果不能修改进程的 RLIMIT_NOFILE,则返回的是软限制。

  2. 如果能修改进程的 RLIMIT_NOFILE,则返回的是 max_file_limit。

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

  1. mysqld 是在普通用户下启动的,修改后的软限制(即 max_file_limit)小于硬限制。

  2. mysqld 是在 root 用户下启动的或 mysqld 进程有 CAP_SYS_RESOURCE(忽略资源限制)权限。

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

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

# cat /proc/26424/limits
Limit Soft Limit Hard Limit Units
...
Max open files 2048 4096 files
...

其中,26424 是进程的 PID。

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

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

三、进程RLIMIT_NOFILE的决定因素

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

[root@node1 ~]# /usr/local/mysql5.7.34/bin/mysqld --defaults-file=/etc/my.cnf &

mysql> select @@open_files_limit,@@table_open_cache,@@max_connections,@@table_definition_cache;
+--------------------+--------------------+-------------------+--------------------------+
| @@open_files_limit | @@table_open_cache | @@max_connections | @@table_definition_cache |
+--------------------+--------------------+-------------------+--------------------------+
| 100000 | 1000 | 2000 | 900 |
+--------------------+--------------------+-------------------+--------------------------+
1 row in set (0.00 sec)

竟然不是 65536,而是 100000。

3.1 启动用户

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

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

root     soft        nofile       100000
root hard nofile 100000
mysql soft nofile 2048
mysql hard nofile 4096

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

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

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

  1. 软限制必须小于等于硬限制。

  2. 硬限制可以调小,但不能低于软限制。

除此之外,

  1. 如果是普通用户,对于硬限制的调整是不可逆的,即一旦调小,就不能再调大。

  2. 如果是 root 用户,则没有限制。但不能无限调大,最大值受到/proc/sys/fs/nr_open 的限制。后者决定了单个进程可打开的最大文件描述符数。

3.2 启动脚本

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

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

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

[root@node1 ~]# /usr/local/mysql5.7.34/bin/mysqld_safe --defaults-file=/etc/my.cnf &

mysql> select @@open_files_limit,@@table_open_cache,@@max_connections,@@table_definition_cache;
+--------------------+--------------------+-------------------+--------------------------+
| @@open_files_limit | @@table_open_cache | @@max_connections | @@table_definition_cache |
+--------------------+--------------------+-------------------+--------------------------+
| 65536 | 1000 | 2000 | 900 |
+--------------------+--------------------+-------------------+--------------------------+
1 row in set (0.00 sec)

竟然是 65536。

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

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 parse_arguments() {
...
--open-files-limit=*) open_files="$val" ;;
--open_files_limit=*) open_files="$val" ;;
...
}

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

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

LimitNOFILE=102400

3.3 小结

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

四、Too many open files

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

  1. 连接创建失败。相关的报错如下:

    [ERROR] Error in accept: Too many open files in system
  2. 查询失败。相关的报错如下:

    [ERROR] /usr/local/mysql5.7.34/bin/mysqld: Can't open file: './sbtest/t4.frm' (errno: 24 - Too many open files)

4.1 常见原因

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

  1. 系统可打开的文件描述符数达到了上限。这里的上限,指的是系统内核的上限,由/proc/sys/fs/file-max 决定。

  2. 进程可打开的文件描述符数达到了上限。

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

如何判断是哪个原因导致的呢?

  1. 查看系统文件描述符的使用情况。

    cat /proc/sys/fs/file-nr
    2752 0 65536

    输出有三列,如果第一列大于等于第三列,则意味着系统可打开的文件描述符数达到了上限。

    • 第一列:已分配的文件描述符数。
    • 第二列:可用的文件描述符数。
    • 第三列:内核能分配的最大文件描述符数,由/proc/sys/fs/file-max 决定。
  2. /var/log/messages。

    如果系统可打开的文件描述符数达到了上限,/var/log/messages 还会出现以下报错。

    kernel: [8314414.667267] VFS: file-max limit 65536 reached

4.2 解决方法

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

4.2.1 针对原因一,系统可使用的文件描述符数达到了上限。

  1. 看看哪些进程打开的文件最多。

    lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr

    输出有两列,第一列是打开的文件的个数,第二列是进程号。

  2. 通过进程号定位具体的进程。

    ps -ef | grep ${PID}
  3. 分析打开文件最多的进程,或 KILL,或重启,或优化。

  4. 如果短期内不能优化解决的,可先临时调大 file-max 的值。

    echo 100000 > /proc/sys/fs/file-max

    这里修改的是内存值,机器重启就会失效。如果要永久生效,还需修改/etc/sysctl.conf。

    # vim /etc/sysctl.conf
    fs.file-max=100000 # sysctl -p

4.2.2 针对原因二,进程可使用的文件描述符数达到了上限。

  1. 查看进程的最大文件描述符数。

    cat /proc/${PID}/limits | grep "Max open files"
  2. 查看进程当前打开的文件描述符数。

    lsof -p ${PID} | wc -l
  3. 调整进程的最大文件描述符数。

    从 CentOS 6 开始,支持在线调整进程的最大文件描述符数,无需重启进程。

    #CentOS 6
    echo -n "Max open files=102400:102400" > /proc/${PID}/limits #CentOS 7
    prlimit --pid ${PID} --nofile=102400:102400

    注意,CentOS 6 的调整方式在 CentOS 7 中不适用,同样,CentOS 6 中也不支持 prlimit 命令。

    这就意味着,后续MySQL出现“Too many open files”错误,再也不用重启进程了。

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

  1. 首先查看进程的启动用户。

    ps -ef | grep mysqld

    获取 mysqld 父进程的 ID(PPID),对应输出的第三列。

    基于 PPID 查看 mysqld 的父进程。

    ps -ef | grep ${PPID}

    第一列显示的用户就是启动用户。

  2. 修改启动用户可打开的最大文件描述符数。

    vim /etc/security/limits.conf
  3. 修改 open_files_limit 的配置,重启实例。

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

  1. 进程的最大文件描述符数不能超过/proc/sys/fs/nr_open 的大小。

  2. 如果修改了/proc/sys/fs/nr_open,需同步修改/proc/sys/fs/file-max,后者代表了内核能分配的最大文件描述符数。

  3. 为了保证配置在系统重启后依然有效,建议同步修改/etc/sysctl.conf。

    # vim /etc/sysctl.conf
    fs.nr_open=100000
    fs.file-max=100000 # sysctl -p

五、查看进程当前打开的文件描述符数

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

看下面这个 Demo。

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

mysql> show global status where variable_name like 'open%' or variable_name='innodb_num_open_files';
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| Innodb_num_open_files | 431 |
| Open_files | 2 |
| Open_streams | 0 |
| Open_table_definitions | 615 |
| Open_tables | 578 |
| Opened_files | 1303 |
| Opened_table_definitions | 1260 |
| Opened_tables | 1928 |
+--------------------------+-------+
8 rows in set (0.00 sec) mysql> select @@open_files_limit,@@innodb_open_files;
+--------------------+---------------------+
| @@open_files_limit | @@innodb_open_files |
+--------------------+---------------------+
| 1024 | 431 |
+--------------------+---------------------+
1 row in set (0.01 sec)

其中,

  • 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 的准确。

[root@node1 ~]# lsof -p 9020 | wc -l
1054

其中,9020 是进程的 PID。

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

六、总结

  1. open_files_limit 会取以下三个值中的最大值。

    • limit_1 = 10 + max_connections + table_cache_size * 2。
    • limit_2 = max_connections * 5。
    • limit_3 = open_files_limit ? open_files_limit : 5000。
  2. 如果进程的最大文件描述符数超过 open_files_limit,则实际生效的的是进程的最大文件描述符数。

  3. 如果进程的最大文件描述符数小于 open_files_limit,需区分启动用户。

    3.1 如果启动用户是 root,则实际生效的是 open_files_limit。

    3.2 如果启动用户是普通用户。

    • open_files_limit 小于等于硬限制,则实际生效的是 open_files_limit。
    • open_files_limit 大于硬限制,则实际生效的是进程的最大文件描述符数。
  4. 如果进程是通过 mysqld_safe 启动的,则 mysqld_safe 会基于 open_files_limit 调整进程的最大文件描述符数。

  5. 在源码里,其实还区分了一个场景,即最大文件描述符数为 unlimited。但实际上,在 RHEL 及其衍生版本中,不允许将 nofile 设置为 unlimited。

    # ulimit -n unlimited
    -bash: ulimit: open files: cannot modify limit: Operation not permitted

    虽然允许在/etc/security/limits.conf 文件中将 nofile 设置为 unlimited,但效果等同于 0,此时,相关用户不能创建新的进程,也不允许登录。

    # su - mysql
    su: cannot open session: Permission denied # ssh mysql@192.168.244.10
    Connection to 192.168.244.10 closed.
  6. 在调整进程的最大文件描述符数时,注意:单个进程的最大文件描述符数 <= fs.nr_open <= fs.file-max

  7. 从 CentOS 6 开始,可在线调整进程的最大文件描述符数,无需重启进程。

七、参考资料

https://man7.org/linux/man-pages/man2/getrlimit.2.html

https://www.ibm.com/docs/en/sdk-java-technology/7?topic=rja-system-resource-limits-ulimit-command

从源码角度谈谈MySQL "Too many open files"错误的根本原因的更多相关文章

  1. 从源码角度看MySQL memcached plugin——1. 系统结构和引擎初始化

    本章尝试回答两个问题: 一.memcached plugin与MySQL的关系: 二.MySQL系统如何启动memcached plugin. 1. memcached plugin与MySQL的关系 ...

  2. 从源码角度看MySQL memcached plugin——0.大纲

    本系列文章介绍MySQL memcached plugin插件.剖析已经完成.先把链接弄好,内容会陆续补上. 大纲如下: 系统结构和引擎初始化(已完成) 线程模型和连接的状态机 containers表 ...

  3. 从JDK源码角度看Short

    概况 Java的Short类主要的作用就是对基本类型short进行封装,提供了一些处理short类型的方法,比如short到String类型的转换方法或String类型到short类型的转换方法,当然 ...

  4. 从JDK源码角度看Byte

    Java的Byte类主要的作用就是对基本类型byte进行封装,提供了一些处理byte类型的方法,比如byte到String类型的转换方法或String类型到byte类型的转换方法,当然也包含与其他类型 ...

  5. 从JDK源码角度看Object

    Java的Object是所有其他类的父类,从继承的层次来看它就是最顶层根,所以它也是唯一一个没有父类的类.它包含了对象常用的一些方法,比如getClass.hashCode.equals.clone. ...

  6. 从JDK源码角度看Boolean

    Java的Boolean类主要作用就是对基本类型boolean进行封装,提供了一些处理boolean类型的方法,比如String类型和boolean类型的转换. 主要实现源码如下: public fi ...

  7. 从源码角度深入理解Toast

    Toast这个东西我们在开发中经常用到,使用也很简单,一行代码就能搞定: 1: Toast.makeText(", Toast.LENGTH_LONG).show(); 但是我们经常会遇到这 ...

  8. Android -- 带你从源码角度领悟Dagger2入门到放弃

    1,以前的博客也写了两篇关于Dagger2,但是感觉自己使用的时候还是云里雾里的,更不谈各位来看博客的同学了,所以今天打算和大家再一次的入坑试试,最后一次了,保证最后一次了. 2,接入项目 在项目的G ...

  9. Android -- 带你从源码角度领悟Dagger2入门到放弃(二)

    1,接着我们上一篇继续介绍,在上一篇我们介绍了简单的@Inject和@Component的结合使用,现在我们继续以老师和学生的例子,我们知道学生上课的时候都会有书籍来辅助听课,先来看看我们之前的Stu ...

随机推荐

  1. 一文读懂一条 SQL 查询语句是如何执行的

    2001 年 MySQL 发布 3.23 版本,自此便开始获得广泛应用,随着不断地升级迭代,至今 MySQL 已经走过了 20 个年头. 为了充分发挥 MySQL 的性能并顺利地使用,就必须正确理解其 ...

  2. Python Xpath语法

    Python    Xpath语法   一.选取节点 常用的路劲表达式: 表达式 描述 实例   nodename 选取nodename节点的所有子节点 xpath('//div') 选取了div节点 ...

  3. YOLOV4各个创新功能模块技术分析(一)

    YOLOV4各个创新功能模块技术分析(一) 简 介 yolov4论文:YOLOv4: Optimal Speed and Accuracy of Object Detection arxiv:http ...

  4. HLS后端示例

    HLS后端示例 TVM支持带有SDAccel的Xilinx FPGA板.这是有关如何将TVM部署到AWS F1 FPGA实例的文档. 此功能仍处于试验阶段.暂时无法使用SDAccel部署端到端神经网络 ...

  5. 那些年,我们一起做过的KNX智能化控制经典案例!

    那些年,我们一起做过的KNX经典案例! 光阴之箭已经穿越年轮,抵达2021 GVS在2008年加入KNX国际协会,成为中国首批引进KNX标准的企业,此后,还率先研发出基于KNX的核心协议栈,定标准,做 ...

  6. jvm相关自我总结和 VisualVM工具的使用

    idea 二个工具: jclasslib Hexview jdk监控工具 VisualVM工具的使用: https://www.ibm.com/developerworks/cn/java/j-lo- ...

  7. 工作流引擎Activiti使用进阶!详细解析工作流框架中高级功能的使用示例

    Activiti高级功能简介 Activit的高级用例,会超越BPMN 2.0流程的范畴,使用Activiti高级功能需要有Activiti开发的明确目标和足够的Activiti开发经验 监听流程解析 ...

  8. 【NX二次开发】Block UI 选项卡控件

    [NX二次开发]Block UI 选项卡控件

  9. 《手把手教你》系列基础篇之(一)-java+ selenium自动化测试-环境搭建(上)(详细教程)

    1.简介 jmeter系列的文章结束,本来想趁热打铁顺别将Jmeter和接口测试介绍一下,但是感觉Jmeter时间太长了怕大家吃腻了,还有一个原因就是许多小伙伴们或者童鞋们私信问宏哥什么时候可以有ja ...

  10. JavaScript中子函数访问外部变量的方法

    我们在写web页面时,肯定会经常遇到下面这种情况: <body> <div class="btns-wrapper"></div> <sc ...