How MySQL Opens and Closes Tables:https://dev.mysql.com/doc/refman/5.7/en/table-cache.html
其他可供參考的文章有:
關於SHOW OPEN TABLES命令的使用:https://www.percona.com/blog/2008/12/14/show-open-tables-what-is-in-your-table-cache/
一、本文涉及到的系統參數有3個:
- table_open_cache
Server層參數。
這個參數表示針對所有threads的table cache總和,5.6.7之前默認是400,5.6.8之后是2000。
這是個server層的參數,mysql不支持並行查詢,mysql的會話也沒有PGA的概念,一個thread引用myisam表時需要在server層上創建一個table對象(索引也需要創建一個但是是共享的,self join會創建2個,分區表每個分區按單表對待),如果同時多個會話引用一個表也會創建多個表對象,雖然會加大內存使用量,但是卻極大的減少了內部表鎖的爭用。
這個值的數目建議設置為max_connections*你的表數目,當然你可能也需要為一些臨時表等對象預留,但是這個數目已經足夠大啦。
那么mysql什么時候釋放這些表對象呢?
- 當緩沖已滿,而連接想要打開一個不在緩沖中的表時。
- 當緩沖數目已經超過了table_open_cache設置的值,mysql開始使用LRU算法釋放表對象。
- 當你用flush tables;語句時。
- open_files_limit
引擎層參數。
這個參數表示mysqld可用的最大文件描述符數目,如果你遇到“Too many open files”的錯誤,應當考慮加大它。這個參數的默認值是0表示無限制(大於5.6.7后默認值不再為0,參考官網),但其實他的值是與操作系統相關的,在Unix系統下這個值的數目不能大於ulimit -n。
這個參數應當大於等於table_open_cache。
目前不清楚當設置了innodb_open_files后此參數是指所有存儲引擎的句柄數限制,還是非innodb的句柄數限制,需要研究源碼才可以理清,這里暫且認為是前者。
- innodb_open_files
引擎層參數。
這個參數只對InnoDB存儲引擎有效,它指定了mysql可以同時打開的最大.ibd文件的數目。這個參數即不影響table_open_cache也不受open_files_limit影響,是獨立的只對InnoDB有效的。所以在默認為InnoDB存儲引擎時可以不考慮open_files_limit只去設innodb_open_files。
這3個參數的關系可以總結如下,為保證性能,你應當設置為如下值:
max_connections*你的表數目 = table_open_cache <=open_files_limit< ulimit -n
innodb_open_files<ulimit -n
二、本文涉及到的status參數有2個:open_tables和opened_tables
其中open_tables表示當前打開的table總和,即所有connection打開的table總數。
opened_tables表示打開過的表的數量總和,只有show global status才能看到它的值。這是個計數器,Opened_tables/Uptime的值過大說明table_open_cache過小,導致一些table對象(即下文說的table對象)經常會刷出server層,需要的時候再創建,最終導致此計數過大。


三、相關原理圖
參考:http://www.cnblogs.com/xpchild/p/3780625.html的源碼解析,以innodb為例。
如下圖,有個表叫xpchild(小屁孩666)庫中的pp表:

table: 針對每個會話,MySQL會為join查詢中涉及的每個表建一個TABLE對象,引用innodb表時會創建innodb層的handler,server層的table對象指向此handler,此handler操作文件系統底層的ibd文件。
table_share: 初次被訪問時,MySQL為每一張表建立一個table_share對象,與engine層的dict_table_t中的對應表對應,這些table_share對象共同組成server層的一個table cache。
handler: 在Innodb engine層對應於每個TABLE對象,引擎創建一個或多個handler(windows叫handlers,linux叫file descriptors),當然不同存儲引擎、是否分區都會影響單個table對象對應的handler個數。
dict_table_t: innodb為每一個innodb表load一個數據字典對象,這些對象的集合就是innodb中的data dictionary。
你可以將table對象看做表在server層的映射,將handler看做table對象為操作底層數據文件等磁盤文件而在engine層創建的句柄,也因此MySQL支持各種插入式engine。
flush tables with read lock:close了server層創建的所有的table_share對象(即清除了table cache),並使用一個全局的讀鎖鎖定所有schema下的所有表。
關於flush的各種用法及解釋,參考:https://dev.mysql.com/doc/refman/5.7/en/flush.html