MySQL open table
MySQL open table
背景:
MySQL經常會遇到Too many open files,MySQL上的open_files_limit和OS層面上設置的open file limit有什麽關系?
源碼中也會看到不同的數據結構,TABLE, TABLE_SHARE,跟表是什麽關系?
MySQL flush tables又做了些什麽,這幾個東西放在一起,就會比較迷惑,下面進行梳理一下:
1 數據結構
table: MySQL為每一個查詢sql中的表建一個TABLE對象
table_share: MySQL為每一張表建立一個table_share對象,與frm文件對應
dict_table_t: innodb為每一個ibd表load一個數據字典對象
簡略圖如下:
1. open table的過程
測試sql:select * from xpchild.pp
函數調用棧:
open_and_lock_tables
open_tables
open_and_process_table
open_table:打開一個table,並賦值給table_list->table
下面進入核心的邏輯:
開始鎖lock_open:
1.1 table_share:
首先根據db&&table_name創建一個table_share.
函數調用棧:
get_table_share_with_discover: 創建或者查找table_share對象。
get_table_share:如果全局cache:table_def_cache中存在,就直接使用,如果不存在,就創建一個table_share
alloc_table_share: 如果不存在,先分配一個share的對象。
open_table_def: 打開frm文件。
mysql_file_open: my_open打開
share->ref_count++;
要點:
- MySQL server層維護了一個全局的table definition cache即table_def_cache,所以一個table_share只會創建一次,後續進行共享。
- 初始化table_share的過程中,調用文件系統打開frm文件,初始化完成後,關閉frm文件。
- share結構中ref_count來表示有多少個table關聯
1.2 打開table:
open_table_from_share
step1: 初始化table中的變量,包括file handler (get_new_handler(share->db_type()))
step2: 添加各種索引,字段結構
step3: file->ha_open:ha_innobase::open 為innodb的table創建一個handler。
要點:
1. 在open table的過程中,innodb會創建一個handler,並打開ibd文件,初始化dict_table結構。
釋放鎖lock_open
1.3 close table:
sql執行結束之前,會調用close table
close_open_tables(thd)
table_def_unuse_table:
要點:
1.table_share維護了兩個雙向鏈表used_tables,free_tables,當close table的時候,
1.1 table->in_use=NULL,
1.2 把table從used_tables移除
1.3 加入到free_tables
2.全局參數table_cache_size,已經當前table_cache_count計數控制cache的置換策略
2. 再次執行sql
因為第一步已經完成了table_share的創建,並且cache了table,再次執行sql時,open table的過程就比較簡單:
2.1: get_table_share:從cache中直接獲取table_share對象
2.2:open_table_from_share:從s->free_tables中獲取緩存的可以使用的table,然後增加ref計數。
if (!share->free_tables.is_empty())
table= share->free_tables.front();
++share->ref_count;
3 系統計數:
opened_tables:系統在open_table_from_share中,對新建的table,進行thd->status_var.opened_tables++計數。
opened_shares: 系統在 open_table_def的函數中,對於首次進行open的table_share進行thd->status_var.opened_shares++計數
註: 所以當系統的status:open_tables增長比較多的時候,可以適當的增加table_cache_size,用於緩存更多的table,畢竟open table的開銷還是不小的。
4 status統計
使用show status命令,跟open有關的幾個:
{"Open_files", (char*) &my_file_opened, SHOW_LONG_NOFLUSH}
註釋:【全局變量】MySQL和innodb通過文件系統打開的文件的計數,這裏包括了所有的文件,binlog,relay,alert,slow log等。
{"Open_table_definitions", (char*) &show_table_definitions, SHOW_FUNC},
註釋:【全局變量】server當前打開的table_share數量,等於table_def_cache.records的數量
{"Open_tables", (char*) &show_open_tables, SHOW_FUNC}
註釋:【全局變量】 server當前打開的table數量,server維護了一個全局變量table_cache_count
{"Opened_files", (char*) &my_file_total_opened, SHOW_LONG_NOFLUSH}
註釋:【全局變量】 啟動以來打開過的文件的總數
{"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}
註釋: 【線程變量】 在真正open_table_from_share的過程中,累計計數
{"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS}
註釋: 【線程變量】 在真正打開share的時候,累計計數
註: use test的過程:
在use test的過程中,會輪詢把test db下的所有表的table_share都創建一遍,即open所有的frm文件,並創建table_share對象,最後close 所有的frm文件。
為了方便調試,寫了一個進程監控的程序:pidmon.py
在gdb的時候,可以看到mysqld進程打開的所有文件:
Too many open files:
這裏包括了server層的open frm,和innodb層open ibd的時候,當打開的文件超過limit限制,就會報這個錯誤。
但這個限制牽涉到兩個參數:
一個是MySQL配置的open_files_limit
一個是OS層配置的進程的open file limit
MySQL open table