linux服务器和mysql句柄数过多"too many open files"问题分析

问题说明:

1)  在Linux中查看日志时,发现有Can’t open so many files信息。应该是虚拟机打开文件数或者sockets数太多了。在Linux下,我们使用ulimit -n命令可以看到单个进程能够打开的最大文件句柄数量(socket连接也算在里面)。系统默认值1024。对于一般的应用来说(象Apache、系统进程)1024完全足够使用。但是如何象Java等单进程处理大量请求的应用来说就有点捉襟见肘了。如果单个进程打开的文件句柄数量超过了系统定义的值,就会提到“too many files open”的错误提示。

2) mysql报错:[ERROR] Error in accept: Two many open files


1 Linux下查看及修改进程打开的文件句柄数量


---- 查看Linux系统默认的最大文件句柄数,系统默认是1024
  1. # ulimit -n
  2. 1024

---- 查看Linux系统默认的最大文件句柄数详细说明

  1. # ulimit -a
    1. core file size          (blocks, -c) 0  
    2. data seg size           (kbytes, -d) unlimited  
    3. scheduling priority             (-e) 30  
    4. file size               (blocks, -f) unlimited  
    5. pending signals                 (-i) 30605  
    6. max locked memory       (kbytes, -l) 40000  
    7. max memory size         (kbytes, -m) unlimited  
    8. open files                      (-n) 1024
    9.     
    10. pipe size            (512 bytes, -p) 8  
    11. POSIX message queues     (bytes, -q) 819200  
    12. real-time priority              (-r) 65  
    13. stack size              (kbytes, -s) 8192  
    14. cpu time               (seconds, -t) unlimited  
    15. max user processes              (-u) 30605  
    16. virtual memory          (kbytes, -v) unlimited  
    17. file locks                      (-x) unlimited  


---- 查看Linux系统所有进程情况

  1. # top

---- 查看Linux系统某个应用的进程PID

  1. # ps aux| grep mysql 

---- 查看Linux系统某个进程打开的文件句柄数量

  1. # lsof -n | grep 5950 -c
  2. 212

---- 查看Linux系统所有进程以及各进程打开的文件句柄数量

  1. # lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr
  2. 212  131133
      第一列是打开的句柄数,第二列是进程号


--- 得到进程号后,查看该进程对应的详细应用内容

  1. # ps -ef|grep 131133

2  Linux下修改linux服务器的文件句柄数量

   修改Linux系统的最大文件句柄数限制的方法:

       1)   针对当前session有效,用户退出或者系统重新后恢复默认值

  1. # ulimit -n 65535

      2) 只对单个用户有效; 修改 profile文件:在 profile文件中添加:ulimit -n 65535    

  1. # ulimit -n 65535

      3) 永久生效法:针对单个进程最大文件句柄数:

           修改文件:/etc/security/limits.conf,在文件中添加:(立即生效-当前session中运行ulimit -a命令无法显示);修改以后保存,注销当前用户,重新登录,执行ulimit -a ,ok ,参数生效了:

  1. * soft nofile 32768 #限制单个进程最大文件句柄数(到达此限制时系统报警)  
  2. * hard nofile 65536 #限制单个进程最大文件句柄数(到达此限制时系统报错)  
      4)针对系统最大文件句柄数:
           修改文件:/etc/sysctl.conf。在文件中添加:
  1. fs.file-max=655350 #限制整个系统最大文件句柄数  
           运行命令:/sbin/sysctl -p 使配置生效
      5)  文件参数说明 /etc/security/limits.conf :
  1. * cat limits.conf  
  2. * soft core 102400
    * hard core 102400
    * hard nofile 10000
    * soft nofile 10000
    * hard nproc 10000
    * soft nproc 10000
  1.        最后系统使用这个

           # End of file

           * - nofile 102400

           * - nproc 102400

      解释:*代表domain,就是全局的意思,可以改成具体的用户名来限制某个用户。
          soft / hard:这个当中的硬限制是实际的限制,而软限制,是warnning限制,只会做出warning.
          nofile 代表max number of opened file,

         10000:代表最大打开句柄数

3  MySQL 报“too many open files”异常问题分析

       我们需要了解的是,在linux里面打开文件是以文件描述符(FD,file descripter)的形式打开的,每打开一次文件,那么os就分配给你一个文件描述符,对于同一个文件如果有多个进程打开,那么就可以分配多个文件描述符。好了,现在我们回到这个问题上,这位朋友说他并没有一次性打开这么多的文件,怎么会出现这个问题呢?刚才说了,每个进程打开同一个文件都可能分配一个独立的FD,而mysql正是这么做的,每个session开打的数据文件的描述符都是独立的,而对于索引文件则是所有的session共享,
我先将手册里面的原话贴出来:
MySQL is multi-threaded, so there may be many clients issuing queries for a given table simultaneously. To minimize the problem with multiple client sessions having different states on the same table, the table is opened independently by each concurrent session. This uses additional memory but normally increases performance. WithMyISAMtables, one extra file descriptor is required for the data file for each client that has the table open. (By contrast, the index file descriptor is shared 
between all sessions.)
好了,那你现在应该明白了,为什么并没有同时打开那么文件,但是却有那么多的文件描述符。弄清楚问题,那么现在就来解决这个问题。首先是增大 open_files_limit ,具体用法参考手册上说明,然后根本解决方法是在os上增大mysqld这个线程的最大开打文件数,在linux里面可以通过编辑文件 /etc/security/limits.conf。
    1) 修改limits.conf中soft nofile 和hard nofile值。上面就限制了mysql用户最大的打开文件数。同时可以通过ulimit -a命令来查看这些信息。
    2) 上面配置的用户级别打开FD的个数,系统级别的FD最大打开数可以通过 cat /proc/sys/fs/file-max查看,编辑有两种方式:

          a. echo n > /proc/sys/fs/file-max 

          b. vi /etc/sysctl.conf 然后添加一行 fs.file-max=n  然后使用sysctl -p使其生效

但有时候,我们遇到的不是two many open files 而是 two many connections,这个就是并发的连接数,通过修改参数mysql的max_connections来解决
   3) 另外一种说法:
       

网络上的讨论有很多,包括调整系统本身的 fs.file-max 大小,还有在 /etc/my.cnf 中加入 open_files_limit 的设定值,但是实际上,光是这样的调整是不够或方向是不对的,因为 mysql 要开多少的 files,真正相关的不只这些设定。

相关的设定有:

/proc/sys/fs/file-max
这是系统资源分配的最高档案数,设定值与内存大小有关,早期 ram 很贵的时代,这个值通常不会太大,所以 mysql 开的档案数如果太多,确实可能被这个值限制住,但是现在动辄数 G  memory,这个值在我的 linux 系统上都内定开到 20 万以上,因此问题不在这个值。 (若真想修改这个值,可以用 sysctl -w fs.file-max=##### 来修改,但系统重开后自动改回,可写入/etc/rc.local 开机执行)


ulimit -n
ulimit 可以查看每个 shell 的使用资源大小,-n 参数在 man page 中是写 The maximum number of open file descriptors,换句话说,mysql 所处的 shell,真的能开的档案只是ulimit -n 的值,在我的系统上 ulimit -n 的值仅有 1024,所以就算 fs.file-max 有几十万,mysql shell 可以开的就是 1024 而已,这才是关键所在。

my.cnf 中的 table_cache,max_connections, open_files_limit 三个参数


table_cache: mysql 5.1.3 版后已更名为 table_open_cache,指 mysql 开启 table  cache file 数,一般 mysql 开一个 table就会开启 *.MYI  *.MYD 两个档,比方说我们用 phpMyAdmin 开一个有 100  tables  DBmysql  cache  200 files (default: 64)
max_connections: 最高联机数。 (default: 100)


open_files_limit: mysqld 开启的最高档案数。 (default: 0)

理论上 mysqld  open file 后会 cache 住,那它要开到多少个档案之后,才会去释放掉 cache 的档案?那就得看 my.cnf 里面,table_cache, max_connections, open_files_limit 的值,如果open_files_limits 的值为 0,就看 table_cache max_connections 透过某个函数计算出来的值;如果 open_files_limits 的值不为 0,那应该是要看这个值的大小设定。

问题来了,那个函式怎么算的,一般说法是 table_cache * 2 + max_connections,我自己的系统则是还要再 +10,我在my.cnf 原本的设定是table_cache 1024, max_connections 150, open_files_limits 则不设 (default:0),结果计算出来是 1024 x 2 + 150 + 10 = 2208,所以照理论来说 mysql 可以开到 2208 个档案。但实际却在我 DBtable 数大增后,便很容易因为文章一开头的error  mysql 挂点了。


仔细一看 log 后更可以进一步发现这个 [Warning] Could not increase number of max_open_files to more than1024 (request: 2208)

换句话说,如果 table_cache * 2 + max_connections 超过 1024 就会有警告,而 1024 便是 ulimit -n 那个 1024,即 shell的限定使用资源。因此mysql 真正会挂的主因是,它认为可以开到 2208 个,但系统只给它开到 1024 个,所以 mysql 就会一直去开第 1025 个失败,然后mysql挂了

结论就是,不管我们要将我们的 mysql 设置 open_files_limit 或是使用 table_cache * 2 + max_connections,都应该要注意ulimit -n 的值才是正解,跟 fs.file-max关联反而较小了。


PS: 要查看 mysql 开启的 files 数,可先用 ps aux| grep mysql  mysql PID,再利用 lsof -p PID# | wc -l 来统计。



你可能感兴趣的:(Mysql,linux)