2. 表定义信息
2.1 TABLE 和 TABLE_SHARE
前面介绍数据字典是 MySQL 维护元数据的模块,MySQL 真正操作表数据是基于表定义对象 TABLE,和存储引擎和数据打交道,每个 TABLE 内有一个handler,表示使用的引擎对象,Innodb 引擎对应的表定义信息是 dict_table_t,TABLE 对表保留有操作数据的必备的信息,如表及字段的特征信息,元数据锁,record 查找结果等。每次执行 SQL 时,都会将涉及的表列表(TABLE_LIST,如 Join 多个表)逐个 open,最终是从数据字典中的 Table_impl 获取构建 TABLE 的对象信息。
TABLE 对象由每个 thd 局部持有,通过 prev 和 next 指针串起了正在操作这个 thd 所控制的所有 TABLE 对象。即 THD::opened_tables。因为不同 thd 可能同时访问相同的表,因此 TABLE 都对应一个全局的对象TABLE_SHARE。每个 TABLE 都是从 TABLE_SHARE 构建而来,在 TABLE_SHARE 也存有每个 TABLE 的引用次数。
2.2 server 层的缓存策略
为了避免每次都从 DD 模块中构建 TABLE 对象,表定义信息也有自己的缓存策略,Server 层和 Innodb 层的缓存管理是相互独立的。
Thd 的局部表定义 TABLE 是由 table_cache_manager 管理的,为了提高并发度,首先按 Thd 的 thread_id 分片,每个 Thd 对应一个 Table_cache 结构,Table_cache 中 m_unused_table 缓存了所有的空闲 TABLE 对象,m_cache 是按照 “database_name + table_name” 和 Table_each_element 的哈希表,Table_each_element 唯一对应于一个 TABLE_SHARE,独立管理着和该 TABLE_SHARE 的所有使用和空闲的 TABLE 对象。
因为 Table_cache 是按照 thread_id 分片,因此同个 TABLE_SHARE 可能分布在多个 Table_cache 的 Table_each_element 中,分布信息也记录在 TABLE_SHARE 中。
table_cache_manager 的大小由 table_cache_size 控制,平均划分到 Table_cache 中。

图 4: TABLE 的缓存管理
当从 Table_cache_manager 找不到可用的 TABLE_SHARE 对象,会从 Table definition cache 中获取,Table definition cache 是所有 Table_Share 的缓存(包括正在使用或者未被使用的),其大小由 table_def_size 控制。当 TABLE_SHARE 被释放后会被加入 oldest_unused_share 链表尾部缓存,下次开表时直接复用,超过 table_def_size 时,也优先从 oldest_unused_share 中释放。
2.3 Innodb 层的表定义信息
Innodb 中同样需要使用表定义信息,在 innodb 的形式是 dict_table_t,缓存在 dict_sys_t 中,大小和 TABLE_SHARE 的缓存一样,由 table_def_size 控制。
包括了两个哈希表:按 name 索引的 table_hash 和 按 id 索引的 table_id_hash;以及两个 LRU 链:用于剔除 的 table_LRU 以及不剔除的 table_non_LRU。
table_non_LRU 中的表不会被缓存主动剔除 [3],如常用的系统 DD 表 (sys_tables, sys_columns, …),引用关系的表 (dict_foreign_add_to_cache), 全文索引的表 (fts_optimize_add_table),便于删表,删表逻辑时保证快速从缓存加载表。
table_LRU 随着开表(dict_table_open_on_name 和 dict_table_open_on_id)动态调整缓存中的 dict_table_t。master 线程也会定时清理 table_LRU 中超出的 dict_table_t。
dict_table_t 的开表流程也是基于临时 TABLE_SHARE 构建的。
在 dict_sys_t 中还保留一些系统 DD 表直接指针标识,使用时直接访问,避免从哈希表中查找。
引用
[1]MySQL · 源码分析 · 详解 Data Dictionary




