標籤:mysql flush mit 通過 src 不同的 null 關係 鏈表
背景:
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檔案對應
handler: 對應於每個TABLE對象,innodb引擎建立一個handler
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開啟
inline_mysql_file_close: my_close關閉
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