1. 程式人生 > >MySQL open table

MySQL open table

strong 就會 ron post 成了 監控 total pen tps

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文件對應

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++;

要點:

  1. MySQL server層維護了一個全局的table definition cache即table_def_cache,所以一個table_share只會創建一次,後續進行共享。
  2. 初始化table_share的過程中,調用文件系統打開frm文件,初始化完成後,關閉frm文件。
  3. 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