目录
table_cache
table_cache的使用背景
当我们的客户端程序提交 Query 给 MySQL 的时候,MySQL 需要对 Query 所涉及到的每一个表都取得一个表文件句柄信息,如果没有 Table Cache,那么 MySQL 就不得不频繁的进行打开关闭文件操作,无疑会对系统性能产生一定的影响,Table Cache 正是为了解决这一问题而产生的。在有了 Table Cache 之后,MySQL 每次需要获取某个表文件的句柄信息的时候,首先会到 Table Cache 中查找是否存在空闲状态的表文件句柄。如果有,则取出直接使用,没有的话就只能进行打开文件操作获得文件句柄信息。在使用完之后,MySQL 会将该文件句柄信息再放回 Table Cache 池中,以供其他线程使用。
对于每个连接线程,访问一张表时,每个线程都有自己本地的table cache,这样可以避免每次访问全局的table definetion cache来获得table的元数据,以免多线程并发访问时产生锁等待,但是缺点是增加了一定的内存开销。另外,每个table cache元素,除了有sql层TABLE_SHARE的内容外,还有对应storage层的handler,不同存储引擎对应的handler不同,表示该表在存储层需要使用对应的存储引擎进行操作。
table_cache的存储结构
table_cache是一个hash表结构,hash的元素是table_cache_element。
即某一个表在某个table_cache中存储的是一个table_cache_element,然后如果使用的话,还要到空闲链表中去查找。
table_cache_element
这个元素里面重要的是有两个链表,进行这个表在server层的缓存。每个链表的元素是Table,定义见下面。
table定义里面重要的结构体是有指向table share的指针,也有指向具体存储引擎的handler指针。
class Table_cache_element
{
private:
/*
Doubly-linked (back-linked) lists of used and unused TABLE objects
for this table in this table cache (one such list per table cache).
*/
typedef I_P_List <TABLE,
I_P_List_adapter<TABLE,
&TABLE::cache_next,
&TABLE::cache_prev> > TABLE_list;
TABLE_list used_tables;
TABLE_list free_tables;
TABLE_SHARE *share;
public:
Table_cache_element(TABLE_SHARE *share_arg)
: share(share_arg)
{
}
TABLE_SHARE * get_share() const { return share; };
friend class Table_cache;
friend class Table_cache_manager;
friend class Table_cache_iterator;
};
Table
struct TABLE
{
TABLE() {} /* Remove gcc warning */
/*
Since TABLE instances are often cleared using memset(), do not
add virtual members and do not inherit from TABLE.
Otherwise memset() will start overwriting the vtable pointer.
*/
TABLE_SHARE *s;
handler *file;
TABLE *next, *prev;
......
}
Table_Share
MySQL对一张表的定义建的对象, table_def_cache参数控制缓存的对象就是table_share;
里面存储的内容主要来自于.frm文件(.frm是mysql sql层的表的元数据信息,主要存储了表结构的定义),相当于.frm文件的缓存。同时,一个table的不同table object会share一个TABLE_SHARE。
table_share的管理:
TABLE_SHARE使用hash表进行快速查找,数据库名和table名作为其key,对应的名字是table_def_cache,TABLE_SHARE有大小限制,由table_definition_cache决定,因此,TABLE_SHARE使用LRU进行管理,对应的链表名字时oldest_unused_share。
1、查找使用:当用户有对表t1的查询请求时:1)在其本地的查找有无对应的table_cache元素,2)如果没有,调用get_table_share则到全局的table definition cache中查找有无对应的TABLE_SHARE,3)如果还没有,则从本地的t1表对应的t1.frm读取对TABLE_SHARE进行初始化,并插入到全局的table_def_cache中,并增加其引用计数。4)读取到TABLE_SHARE,对应的客户线程会将其缓存在table_cache,方便下次访问直接使用。
注意:1)如果TABLE_SHARE在第1步中找到,并且其在oldest_unused_share中,则需要将其从oldest_unused_share移除;2)如果读取TABLE_SHARE的个数超过了table_def_cache的大小,需要从oldest_unused_share链表的尾部将其它表TABLE_SHARE从table_def_cache中删除。
2、删除失效:当用户进行DDL操作时,会改变表的定义(也即TABLE_SHARE中的版本号会改变),也即会修改.frm文件。因此,对于.frm文件的缓存TABLE_SHARE,也需要在DDL时进行失效。因此会调用release_table_share,将表的TABLE_SHARE从table_def_cache和oldest_unused_share中删除。当然,有时候一些操作如close table会调用release_table_share,来减少表对应的TABLE_SHARE的引用计数,如果引用计数为0了,但是版本没发生变化,则将其放入到TABLE_SHARE尾部,并且不会将其从table_def_cache中删除。下次,如果在使用该表,则无需磁盘上读取。
Innodb(innobase_share)
innobase_share是innodb层对某张表的定义,全局共享结构, 受 innobase_share_mutex保护。
innobase_open_tables 是一个hash表,保存着所有的INNOBASE_SHARE。
存储引擎的表定义缓存,如果没有的话,需要从系统表中打开这个表
dict_sys->table_hash。
dict_sys_t:整个系统的数据字典, 全局的 dict_sys_t* dict_sys;
包括:hash_table_t* table_hash; /*!< hash table of the tables, based
包括:UT_LIST_BASE_NODE_T(dict_table_t) table_LRU; /*!< LRU list of tables */
数组字典使用了两个结构存储,一个hash方便查询, 一个是lru链表,用于缓存的置换。