背景:
數據庫鏈接不上,報錯:
root@localhost:/var/log/mysql# mysql -uzjy -p -h192.168.1.111 --default-character-set=utf8 -P3306 Enter password: ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0
分析方法:
在這個地方我看不出什么,直接看錯誤日志:
[ERROR] /usr/sbin/mysqld: Can't open file: './java/tt_fte.frm' (errno: 24)
root@localhost:/var/log/mysql# perror 24 OS error code 24: Too many open files
一看到這里,就覺得需要調整 open_files_limit 參數了(默認最小1024),至此問題解決。雖然問題解決了,但是還沒有弄清楚MySQL打開了多少個文件描述符,打開了哪寫文件描述符號,以及如何預防。怎么了解MySQL打開了多少個文件描述符呢?
知識點:用lsof去查看,理解myisam和innodb的文件描述符、OS 的ulimit相關認識。
基於上面的問題,現在來分析(此時數據庫就連不上了,一直報無法打開的錯誤信息)查看MySQL打開的文件:
root@localhost:~# lsof -p 26288 | wc -l 1042 #因為數據庫鏈接不了,所以只能通過系統查看他的文件描述符 root@localhost:~# cat /proc/26288/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 79877 79877 processes Max open files 1024 4096 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 79877 79877 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us
也可以通過下面的方法查看,下面的方法最為精確
root@localhost:~# ls -lh /proc/26288/fd | wc -l
1024
上面看出,MySQL這時打開的文件描述符1024,已經達到上限,所以再打開的時候就報錯了。修改open_files_limit 參數,設置為2000試試?
root@localhost:~# lsof -p 27732 | wc -l 1053 root@localhost:~# cat /proc/27732/limits Limit Soft Limit Hard Limit Units Max cpu time unlimited unlimited seconds Max file size unlimited unlimited bytes Max data size unlimited unlimited bytes Max stack size 8388608 unlimited bytes Max core file size 0 unlimited bytes Max resident set unlimited unlimited bytes Max processes 79877 79877 processes Max open files 2000 2000 files Max locked memory 65536 65536 bytes Max address space unlimited unlimited bytes Max file locks unlimited unlimited locks Max pending signals 79877 79877 signals Max msgqueue size 819200 819200 bytes Max nice priority 0 0 Max realtime priority 0 0 Max realtime timeout unlimited unlimited us root@localhost:~# ls -lh /proc/27732/fd | wc -l 1035
上面看出,MySQL需要打開的文件描述符1035,小於2000,數據庫正常。到此為止,上面的問題得到解決。想更清楚了解的請繼續看:
查看數據庫的變量:
zjy@localhost : (none) 10:27:19>show global status like 'open%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | Open_files | 804 | | Open_streams | 0 | | Open_table_definitions | 400 | | Open_tables | 400 | | Opened_files | 6803 | | Opened_table_definitions | 3861 | | Opened_tables | 4315 | +--------------------------+-------+ 7 rows in set (0.00 sec) zjy@localhost : (none) 10:31:11>show global variables like 'open%'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | open_files_limit | 2000 | +------------------+-------+ 1 row in set (0.00 sec) zjy@localhost : (none) 10:39:03>show global variables like 'table_open_cache'; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | table_open_cache | 400 | +------------------+-------+ 1 row in set (0.00 sec)
在MySQL 5.1.3之后,還添加了2個狀態值:Open_table_definitions和Opened_table_definitions。這2個值代表的意思如下:
Open_table_definitions :代表當前緩存了多少.frm文件。
Opened_table_definitions:代表自從MySQL啟動后,緩存了.frm文件的數量。 需要注意的是.frm文件是MySQL用於存放表結構的文件,對應myisam和innodb存儲引擎都必須有的,可以通過show open tables 查看 這2個變量的值。
說到這里主要關注的參數還是:table_open_cache 其作用是:從MySQL5.1.3 開始改為table_open_cache,所有線程所打開表的數量, 增加此值就增加了mysqld所需要的文件描述符的數量.它的作用就是緩存表文件描述符,降低打開關閉表的頻率, 如果這個參數設置得過小,那么很快就會被占滿,再有新請求過來的時候,就不得不關閉一些已打開的表以便為新請求騰出空間,從而出現頻繁的打開關閉MyISAM表文件的情況,而INNODB表的打開 不受這個參數控制,而是放到其數據字典當中,即在ibd中。
查看打開的各個文件描述符是什么:大部分都是MYI,MYD,IBD文件。
root@localhost:/proc/27732/fd# ls -lh | grep MYI | wc -l 400 root@localhost:/proc/27732/fd# ls -lh | grep MYD | wc -l 400 root@localhost:/proc/27732/fd# ls -lh | grep ibd | wc -l 215 root@localhost:/proc/27732/fd# lsof | grep /var/lib/mysql | grep MYI | wc -l 400 root@localhost:/proc/27732/fd# lsof | grep /var/lib/mysql | grep MYD | wc -l 400 root@localhost:/proc/27732/fd# lsof | grep /var/lib/mysql | grep '\.ibd' | wc -l 214
上面看到400,是否和table_open_cache有關?經過測試得出:MySQL變量 Open_tables 打開了 table_open_cache的數目,和lsof中的MYI和MYD數目對應(MYISAM)。
即 Open_tables <= table_open_cache,要是Open_tables 和 table_open_cache 一樣,表示MySQL已經用完了表緩存,可以適當的調大。
那214是什么意思?是不是INNODB表?經驗證確認:
zjy@localhost : (none) 10:43:54>select count(*) from information_schema.tables where ENGINE='innodb'; +----------+ | count(*) | +----------+ | 214 | +----------+ 1 row in set (0.05 sec)
從上面的信息中得出:
open_files_limit = table_open_cache*2 + innodb表
把上面的數字帶進去:
open_files_limit = 400*2 + 214 = 1014
結果為1014 要小於默認的1024,為什么默認時候1024報錯呢,那除這些外還包含什么?
root@localhost:/proc/27732/fd# ls -lh | grep -v MYI | grep -v MYD | grep -v "\.ibd" | wc -l 23 #注意這個出現的數目不是固定的,有隨機性。包含了各種日志、共享表空間、socket文件等信息。

root@localhost:/proc/27732/fd# ls -lh | grep -v MYI | grep -v MYD | grep -v "\.ibd"
total 0
lrwx------ 1 root root 64 Jan 30 22:29 0 -> /dev/null
l-wx------ 1 root root 64 Jan 30 22:29 1 -> /var/log/mysql/mysql.err
lrwx------ 1 root root 64 Jan 30 22:29 10 -> socket:[795955]
lrwx------ 1 root root 64 Jan 30 22:29 11 -> /tmp/ibKm37iF (deleted)
lrwx------ 1 root root 64 Jan 30 22:29 12 -> socket:[795956]
lrwx------ 1 root root 64 Jan 30 22:29 13 -> socket:[800403]
lrwx------ 1 root root 64 Jan 30 22:29 17 -> /var/lib/lxc/localhost/rootfs.hold
l-wx------ 1 root root 64 Jan 30 22:29 2 -> /var/log/mysql/mysql.err
lrwx------ 1 root root 64 Jan 30 22:29 3 -> /var/lib/mysql/ibdata1
lrwx------ 1 root root 64 Jan 30 22:29 31 -> /var/lib/mysql/master.info
lrwx------ 1 root root 64 Jan 30 22:29 32 -> /var/lib/mysql/mysqld-relay-bin.index
lrwx------ 1 root root 64 Jan 30 22:29 33 -> /var/lib/mysql/relay-log.info
lrwx------ 1 root root 64 Jan 30 22:29 34 -> /var/lib/mysql/mysqld-relay-bin.000123
lrwx------ 1 root root 64 Jan 30 22:29 37 -> socket:[795961]
lrwx------ 1 root root 64 Jan 30 22:29 4 -> /tmp/ibse5AFw (deleted)
lrwx------ 1 root root 64 Jan 30 22:29 5 -> /tmp/ibBAQlVN (deleted)
lrwx------ 1 root root 64 Jan 30 22:29 6 -> /tmp/ibxpP6a5 (deleted)
lrwx------ 1 root root 64 Jan 30 22:39 624 -> socket:[800541]
lrwx------ 1 root root 64 Jan 30 22:39 625 -> socket:[800542]
lrwx------ 1 root root 64 Jan 30 22:29 7 -> /tmp/ib9dUH3m (deleted)
lrwx------ 1 root root 64 Jan 30 22:29 8 -> /var/lib/mysql/ib_logfile0
lrwx------ 1 root root 64 Jan 30 22:29 9 -> /var/lib/mysql/ib_logfile1
最后結果:
open_files = 400*2 + 214 + 23(隨機) = 1037
得到了最后的打開文件描述符的結果為1037,那把 open_files_limit 設置成1037看看會是什么情況?理論上不能創建表了,重啟之后但是數據庫正常,那創建一張INNODB表試試?
可以創建4張表,(和上面說的隨機性有關系,因為此時的數據不是23了。)到創建第5張表的時候:
zjy@localhost : zhoujy 11:03:24>create table idx_mer5(id int,name varchar(10),name1 varchar(10),address varchar(30))engine =innodb; ERROR 23 (HY000): Out of resources when opening file './zhoujy/' (Errcode: 24)
數據庫直接報錯,無法創建表,此時重啟數據庫,又會出現和本文開頭的錯誤信息,因為此時會多了(ls -lh | grep -v MYI | grep -v MYD | grep -v CSV | grep -v "\.ibd")的內容。找到問題出現的原因了。解決辦法是:
1,在不修改open_files_limit下把table_open_cache參數調小。
2,修改open_files_limit,至少設置成上面的這個公式。
總結:
在Mysql數據庫中,想知道Mysql打開了多少張表,用:
root@localhost >show global status like 'open%'; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | Open_files | 351 | | Open_streams | 0 | | Open_table_definitions | 305 | | Open_tables | 305 | | Opened_files | 996 | | Opened_table_definitions | 403 | | Opened_tables | 1227 | +--------------------------+-------+ 7 rows in set (0.00 sec)
具體打開了哪些表:
root@localhost >show open tables;
打開了多少文件描述符,用:
root@zhoujy:~# pidof mysqld 19068 root@zhoujy:~# cd /proc/19068/fd
#總的文件描述符: root@zhoujy:/proc/19068/fd# ls -lh | wc -l 498 #MyISAM描述符: root@zhoujy:/proc/19068/fd# ls -lh | grep 'MYD' | wc -l 172 #CSV描述符: root@zhoujy:/proc/19068/fd# ls -lh | grep 'CSV' | wc -l 2 #Innodb描述符: root@zhoujy:/proc/19068/fd# ls -lh | grep '\.ibd' | wc -l 131
要是此時的【文件描述符】的總數 和 open_files_limit 一樣,在新建表的時候就會導致數據庫報打不開表的錯誤信息。
知識點:
MyISAM和CSV表打開時占用2個文件描述符,Innodb則需要1個文件描述符。一些日志信息(relay log,binlog,error-log等)也需要文件描述符。table_open_cache對MyISAM有效,對Innodb無效。當運行 flush tables 關閉表的時候,只對MyISAM表有效,即關閉有MISAM表的文件描述符,Innodb表也會關閉,但是文件描述符不會關。
當表都是MyISAM,在極端的情況下,table_open_cache數目的表全部被打開(512張)就會占用掉1024個文件描述符。而open_files_limit是1024的話,就會出現報錯的情況(本文例子的情況)。所以如果是有大量的 MyISAM 表,那么就需要特別注意打開文件數是否會超出限制了。
總之,確實設置open_files_limit的時候,先要知道table_open_cache 為多少,再加上inodb表的數目和一些日志的數目。上面的結果和系統的ulimit沒有關系(ubuntu默認apt下來的實例),要是不是默認安裝的實例呢?請繼續看:
在測試中發現,設置OS的文件描述符(/etc/security/limits.conf)值是無效的,即MySQL啟動后open_files_limit始終以my.cnf中設置的參數open_files_limit為准。(版本MySQL5.5.29 ubuntu0.12.04.1),而在非Ubuntu中是按照他們(os和mysql)哪個最高用哪個的方法,通過 cat /proc/10415/limits 查看,依然都是數據庫配置文件(open_files_limit)中設置的值。懷疑是ubuntu定制mysql的問題(apt下來的mysql)。
而用mysqld_safe開啟的實例(非apt-get install 安裝),則按照下面的規則:
默認設置的open_files_limit 比其他相關的參數都大。[max_connections*5和10+max_connections+table_cache_size*2 大] 一: 1:ulimit -n 65535 2:修改 my.cnf 限制 open_files_limit = 10000 3:重啟 mysql 4:show global variables like '%open%'; | open_files_limit | 10000 | 二: 注釋 open_files_limit | open_files_limit | 65535 三: 1:ulimit -n 5000 2:修改 my.cnf 限制 open_files_limit = 10000 3:重啟 mysql 4:show global variables like '%open%'; | open_files_limit | 10000 四: 注釋 open_files_limit | open_files_limit | 5000
這里總結一句話:當open_files_limit沒有被配置的時候,比較max_connections*5和ulimit -n的值,哪個大用哪個,當open_file_limit被配置的時候,
比較open_files_limit和max_connections*5的值,哪個大用哪個。
注意:open_files_limit 大小和 max_connections*5需要比較,那個最大就用那個值來設置open_files_limit 。【比較open_files_limit,max_connections*5和10+max_connections+table_cache_size*2中最大值】
--my.cnf-- open_files_limit = 5000 max_connections = 1024 mysql> show global variables like '%open%'; +-------------------+----------+ | Variable_name | Value | +-------------------+----------+ | open_files_limit | 5120 | #1024*5 > 5000 +-------------------+----------+

vi /etc/security/limits.conf mysql soft nofile 65536 mysql hard nofile 65536
總結:
所以在配置open_files_limit的時候,設置多大合適,需要知道具體的表數目和類型等,具體情況需要自己分析。沒有配置的則需要注意配置OS的ulimit(啟動前設置)和max_connections的大小。而用apt-get 下來的mysql實例則都是按照open_files_limit和max_connections 來比較設置的,和OS的ulimit無關。
########2016-08-12更新########
########2020-04-08更新########
MySQL大量線程處於Opening tables的問題分析
相關資料: