天天看點

資料庫核心月報 - 2015 / 08-MySQL · 功能分析 · MySQL表定義緩存

mysql的表包含表名,表空間、索引、列、限制等資訊,這些表的中繼資料我們暫且稱為表定義資訊。

對于innodb來說,mysql在server層和engine層都有表定義資訊。server層的表定義記錄在frm檔案中,而innodb層的表定義資訊存儲在innodb系統表中。例如:

注:以上都是memory表,它們内容是從實際系統表中擷取的。實際上innodb系統表engine也是innodb類型的,資料也是以b樹組織的。

在資料庫每次執行sql都會通路表定義資訊,如果每次都從frm檔案或系統表中擷取,效率會較低。是以mysql在server層和innodb層都有表定義的緩存。以mysql 5.6為例,參數table_definition_cache控制了表定義緩存中表的個數,server層和innodb層的表定義緩存共用此參數。

server層表定義為table_share對象,table_share對象有引用計數和版本資訊,每次使用flush操作會遞增版本資訊。

server層表定義緩存由hash表和old_unused_share連結清單組成,通過hash表table_def_cache以表名為key緩存table_share對象,同時未使用的table_share對象通過old_unused_share連結清單連結。

擷取table_share(<code>get_table_share</code>)

先從hash查找,找不到再讀取frm檔案加載表定義資訊。同時遞增引用計數。

釋放table_share(<code>release_table_share</code>)

遞減引用計數。當引用計數為0時,如果版本發生變化,直接删除此table_share。

old_unused_share連結清單調整:

擷取table_share時(<code>get_table_share</code>)

未使用的table_share對象被啟用,須從lru連結清單取出;

如果緩存總數超出table_definition_cache大小,須依次從old_unused_share連結清單尾部去除。

釋放table_share時(<code>release_table_share</code>)

當引用計數為0時,如果版本沒有發生變化,将table_share對象加入old_unused_share連結清單尾部。如果緩存總數超出table_definition_cache大小,須依次從old_unused_share連結清單尾部去除。

真正free table_share對象時,如果此對象還在old_unused_share連結清單中,須從其中去除。

innodb表定義為<code>dict_table_t</code>, 緩存為<code>dict_sys_t</code>,結構如下

主要由hash表和lru連結清單組成。

兩個hash表,分别按name和id,便于按name和id進行查找。

table_non_lru:

存放不放入到lru連結清單的表,這些表不會從緩存中淘汰出去。那麼哪些表會放入table_non_lru連結清單呢?

系統表,如sys_tables sys_columns sys_fields sys_indexes等;

有引用關系的表都加入table_non_lru(dict_foreign_add_to_cache);

有全文索引的表都加入table_non_lru(fts_optimize_add_table);

便于删表,删表前對将表加入table_non_lru,删表時加載表時保證表仍然在緩存中,例如表corrupted時。

table_lru

不在table_non_lru連結清單中的表都加入table_lru連結清單中。

dict_table_t* sys_tables 等

常用系統表單獨辨別出來,每次使用時直接取出,不需要從hash表查找。

lru的維護

既然存在table_lru連結清單,我們就需要考慮lru的調整:

将最近使用的表放入lru頭部(<code>dict_move_to_mru</code>)

每次按name和id查找時都會調整,參考<code>dict_table_open_on_name</code>和<code>dict_table_open_on_id</code>。

lru的淘汰

淘汰哪些表

lru中表才可以淘汰,table_non_lru中的表不參入淘汰。

表引用計數必須為0(<code>table-&gt;n_ref_count == 0</code>)。

表的索引被自适應哈希引用計數必須為0(<code>btr_search_t-&gt;ref_count=0</code>)。

何時淘汰

主線程控制每47(srv_master_dict_lru_interval)秒檢查一次,隻周遊一半lru連結清單。

主線程空閑時檢查一次,但掃所有lru連結清單,清理控制緩存表個數不能超過table_definition_cache。

如何淘汰

從lru尾部開始,淘汰滿足條件表(<code>dict_make_room_in_cache</code>)。

注:

1. table_non_lru沒有實際作用,主要用于debug;

2. 如果有較多引用限制的表,它們不受lru管理,參數table_definition_cache的作用會弱化。