读书笔记--MySQL 表对象缓存

表对象缓存
表对象缓存,是将某个表对象字典信息缓存到内存中,用来提高对表访问的效率。某个表被访问过一次后,在服务器没有关闭且表定义没有被修改的条件下,访问这个表时,只需要从内存中找到这个已经缓存起来的对象并做相应的操作,而不必每次从系统表中读取它的定义并解析。表字典对象的缓存是通过HASH表来管理,通过表名(库名)来构造一个HASH键值,用来从HASH表中搜索对象。

对表对象的缓存,不只是简单地将一些表的定义通过HASH存储起来就完了,这样可能导致一个用户在表对象上做了任何修改等都会影响到其他用户。更重要的原因是,MySQL是插件式的数据库,每一个用户得到表对象之后还需要将表实例化,这个实例化的对象只有自己能使用,所以并不是简单的使用户都是使用一个缓存对象。它在缓存过程中用到一个TABLE_SHARE的结构体,这个结构体唯一对应MySQL中一个表对象,这里是不区分存储引擎的,它实际上就是对具体一个表的定义的翻译或映射。

打开表时,1)首先需要从系统表中将这个表的所有信息都读入内存中,这些信息包括表名,模式名(库名),所有列信息,列的默认值,表的字符集,对应的.frm文件路径,存储引擎,主键等等。所有这些信息会存放到TABLE_SHARE结构体中,这个结构体相当于是表对象缓存的第一层。这个结构体是所有用户可以共享的,所以他是静态的,不允许修改,从系统表中读取进来之后直到这个表从缓存删除,中间不会做任何修改。2) 用户要访问一个表,只构造了TABLE_SHARE是不够的,当系统得到一个SHARE对象后,系统会重新构造一个新的对象交给当前的操作,它是TABLE_SHAREDE 一个衍生品,对应结构体名是TABLE,它是真正在操作中使用的对象。结构体TABLE_SHARE和TABLE结构体很多成员是相同的,并且可以直接复制过去。因为TABLE_SHARE 是一个静态的缓存对象,所以相对TABLE就可以称作一个相对动态,正在进行一些操作的实例了。
重要的两点:
1) TABLE这个对象是动态的,被实例化的对象,它相当于是一个被打开的表,它已经不仅仅是MySQL Server层的对象了,而是具体到某一个存储引擎了,所以这里还需要构造这个对象有关存储引擎的信息,并且打开这个表。
2) MySQL是一个插件式的数据库管理系统,对于表对象的管理,MySQL层与存储引擎层就是在这里分开的。TABLE算是他们之间桥梁,下层是存储引擎,上层是Server层。对于MySQL的存储引擎,都要提供一些公共的接口来驱动其存储引擎,这些接口都是给上层调用的,来操作对应的存储引擎。

实例化过程中:
首先是要调用存储引擎接口函数create来创建一个对应的存储引擎句柄,创建之后通过这个句柄函数ha_open来打开这个表。打开表主要是对这个新建的存储引擎句柄进行一些初始化操作。在打开之后,这个表的实例化也算是完成了,而这个被打开的实例句柄就挂在TABLE结构体中。TABLE对象与一个MySQL操作相对应,在MDL锁不冲突的情况下,每个线程对应一个实例化的表对象TABLE,而他们指向的是同一个SHARE对象。

实例化后的操作与管理
实例化后这个表对象就可以直接与存储引擎进行交互了。比如插入一条记录,直接调用TABLE已经被实例的存储引擎句柄接口函数ha_write_row即可。
当一个操作完成后,它所实例化的表就不需要了,此时系统不是将这个本地的实例化释放掉,而是将其保存起来以方便下次用户再次访问这个表的时候复用,当然,可能需要一些额外的操作,比如将实例状态恢复。 不在被使用的实例化对象会被保存在SHARE的一个free_tables链表中,free_tables是保存未被使用的实例化对象,正在使用的会被保存在userd_tables链表上。在并发比较高的情况下,可能used_tables中有多个,在free_tables中却没有,但全部执行完之后却是相反。
MySQL用来管理缓存空间大小的方法是通过计数来实现的。默认情况下,系统中总的SHARE个数不能超过table_definition_cache个。
对应SHARE结构体的缓存淘汰问题,如果只是对这个表进行增删改查等没有涉及修改表定义的操作,SHARE是不会被删除的,只有可能被淘汰,因为如果查询的表太多的话,表对象缓存个数是有限的。
一般情况下只要对表结构等方面进行修改,这个表对象的缓存SHARE对象就必须从缓存删除,同时要删除它上面的所有被实例化的表对象缓存结构。

涉及的参数变量
table_open_cache和table_definition_cache是跟表缓存对象相关的参数。前面已经了解表的缓存是有两层,从文件开始往上,第一层是数据字典的缓存,也就是SHARE对象的缓存。其空间是通过table_definition_cache来控制,以表的个数为单位。第二层就是将这些SHARE对象实例化并打开表所占用的缓存空间,其大小通过参数table_open_cache来控制,以表的实例化个数为单位。

优点总结
相比全字典缓存(在数据库启动时就把所有字典信息一次性载入内存中来,这样在使用过程中的效率非常高,但在DDL操作方面有很大不足),他的触发时机是用到的时候才载入缓存,在被修改之后,会将其从缓存中删除,以后用到的时候,再次载入,这样的实现方式降低了DDL操作或回滚导致的字典缓存维护工作。
有效的利用了内存空间,因为可以通过设置表对象缓存空间的大小来控制内存的使用情况,同时只有用到的对象才会被载入到内存中,提高了内存的利用率。

缺点总结
该缓存在效率上有优化的空间。控制缓存空间大小是根据实例化表对象的个数来计算的,在系统中默认最大值是table_open_cache,如果超过这个值系统会自动淘汰一些不常用的实例化表对象。但如果一个表定义的非常大,那么这样下来可能会把系统内存用光。对应SHARE的缓存也是一样的,如果用户访问了很多不同定义或很大的表,也会有同样的问题。
为了实现插件式数据库,其实还是有一些效率的代价的。在表缓存方面,中间加入了一层SHARE的缓存,真正用到的时候还需要实例化,因为每个用户操作及不同时间的状态是不同的,所以用到的时候还需在SHARE基础上实例一个新的对象出来,这样给内存,cpu带来一定程度的压力。



你可能感兴趣的:(mysql)