Limit innodb dictionary cache size

知识背景

MySQL中每个SELECT/DML/DDL在执行之前需要打开相关的表,执行结束后将它们关闭,为了避免重复打开/关闭的开销,server层提供了table cache和table definition cache用来缓存打开的表信息,在SELECT/DML/DDL执行之前首先从table cache中取空闲表,cache中没有相应表才真正打开表,执行结束后将表置为空闲并放入到table cache中;关于这部分的代码分析可以参考之前写的一篇博客;同时,table cache与MDL一起控制表的并发访问,这部分内容打算之后总结出一篇博客

innodb实现了dict_sys,在内存中缓存了载入过的表信息,包括表的元数据信息(表名,space,列信息,索引,外键等),还包括运行时的信息(有多少handler打开了该表,表上加锁信息,运行时统计信息),具体可以参考数据结构dict_table_struct

dict_sys在实现过程中并没有考虑内存使用情况,一个表只有在drop时才会从dictionary cache中清除,也许是基于这样的考虑:一个table在dictionary cache占用的内存不会很大(10KB数量级(见percona测试),应该不会超过100KB,和表模式和运行时系统使用该表的情况有关),因此即使系统中打开了10000个表消耗的空间也不会超过1G,对于大内存运行的innodb,完全不用关心这部分的开销,毕竟这点存储开销带来的性能提升是非常可观的

应用需求

有一些轻量级的应用,内存比较紧张,如同事有这样一个需求:数据库有很多表,需要经常查询,但是以embedded server运行,希望能够限制内存使用,这时innodb dictionary cache的设计对他们来说就有些“memory leak”了:随着系统运行,打开的表原来越多,系统内存消耗越来越大,并且innodb dictionary cache不像table cache那样可以通过flush tables来释放,它会常驻在内存中直到被drop

percona提供了一个innodb_dict_size_limit的patch,可以满足这个需求:

patch实现原理

1、dict_sys中维护了一个table_LRU链表,表头是新近访问过的表,表尾是长时间没有访问过的表,每次表被访问后将表移到table_LRU头部

2、增加变量innodb_dict_size_limit控制innodb dictionary cache大小,默认为0,表示不限制

3、在每次从dict_sys获取一个dict_table后(每个SELECT/DML/DDL执行的时候都需要首先打开表),计算dict_sys使用的内存,计算公式:

dict_sys->table_hash->n_cells + dict_sys->table_id_hash->n_cells)*sizeof(hash_cell_t) + dict_sys->size

当innodb_dict_size_limit设置不为0时,而当前dict_sys使用内存超过innodb_dict_size_limit,就不断从table_LRU的尾部淘汰未在使用中的表,直到dict_sys使用的内存不超过innodb_dict_size_limit或者不能淘汰(表都在使用中)

4、对于系统表,将其pin在dictionary cache中,不能淘汰

总结和介绍就到这里,具体参考patch

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注