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->n_ref_count == 0</code>)。
表的索引被自适應哈希引用計數必須為0(<code>btr_search_t->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的作用會弱化。