從源碼角度談談open_files_limit的生成邏輯及"Too many open files"的解決思路


“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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM