首页  ·  知识 ·  数据库
MyISAM索引缓存优化方法
佚名  http://www.zxbc.cn/  MYSQL  编辑:dezai  图片来源:网络
为了能最小化磁盘I/O,MyISAM 存储引擎采用了很多数据库系统使用的一种策略。它采用一种机制将最经常访问的表保存在内存区块中:   对索引区块来

为了能最小化磁盘I/O,MyISAM 存储引擎采用了很多数据库系统使用的一种策略。它采用一种机制将最经常访问的表保存在内存区块中:

  对索引区块来说,它维护着一个叫索引缓存(索引缓冲)的结构体。这个结构体中放着许多那些最常使用的索引区块的缓冲区块。
  对数据区块来说,MySQL没有使用特定的缓存。它依靠操作系统的本地文件系统缓存。
  本章首先描述了 MyISAM 索引缓存的基本操作。然后讨论在MySQL 4.1中所做的改进,它提高了索引缓存性能,同时能更好地控制缓存操作:

  线程之间不再是串行地访问索引缓存。多个线程可以并行地访问索引缓存。
  可以设置多个索引缓存,同时也能指定数据表索引到特定的缓存中。
  索引缓存机制对 ISAM 表同样适用。不过,这种有效性正在减弱。自从MySQL 3.23开始 MyISAM 表类型引进之后,ISAM 就不再建议使用了。MySQL 4.1更是延续了这个趋势,ISAM 类型默认被禁用了。

  可以通过系统变量 key_buffer_size 来控制索引缓存区块的大小。如果这个值大小为0,那么就不使用缓存。当这个值小得于不足以分配区块缓冲的最小数量(8)时,也不会使用缓存。

  当索引缓存无法操作时,索引文件就只通过操作系统提供的本地文件系统缓冲来访问(换言之,表索引区块采用的访问策略和数据区块的一致)。

  一个索引区块在 MyISAM 索引文件中是一个连续访问的单元。通常这个索引区块的大小和B树索引节点大小一样(索引在磁盘中是以B树结构来表示的。这个树的底部时叶子节点,叶子节点之上则是非叶子节点)。

  在索引缓存结构中所有的区块大小都是一样的。这个值可能等于,大于,或小于表的索引区块大小。通常这两个值是不一样的。

  当必须访问来自任何表的索引区块时,服务器首先检查在索引缓存中是否有可用的缓冲区块。如果有,服务器就访问缓存中的数据,而非磁盘。就是说,它直接存取缓存,而不是存取磁盘。否则,服务器选择一个(多个)包含其它不同表索引区块的缓存缓冲区块,将它的内容替换成请求表的索引区块的拷贝。一旦新的索引区块在缓存中了,索引数据就可以存取了。

  当发生被选中要替换的区块内容修改了的情况时,这个区块就被认为'脏'了。那么,在替换之前,它的内容就必须先刷新到它指向的标索引。

  通常服务器遵循LRU(最近最少使用)策略:当要选择替换的区块时,它选择最近最少使用的索引区块。为了想要让选择变得更容易,索引缓存模块会维护一个包含所有使用区块特别的队列(LRU链)。当一个区块被访问了,就把它放到队列的最后位置。当区块要被替换时,在队列开始位置的区块就是最近最少使用的,它就是第一候选删除对象。
 共享访问索引缓存

  在MySQL 4.1以前,访问索引缓存是串行的:两个线程不能并行地访问索引缓存缓冲。服务器处理一个访问索引区块的请求只能等它之前的请求处理完。结果,新的请求所需的索引区块就不在任何索引缓存环冲区块中,因为其他线程把包含这个索引区块的缓冲给更新了。

  从MySQL 4.1.0开始,服务器支持共享方式访问索引缓存:

  没有正在被更新的缓冲可以被多个线程访问。

  缓冲正被更新时,需要使用这个缓冲的线程只能等到更新完成之后。

  多个线程可以初始化需要替换缓存区块的请求,只要它们不干扰别的线程(也就是,它们请求不同的索引区块,因此不同的缓存区块被替换)。

  共享方式访问索引缓存令服务器明显改善了吞吐量。

  多重索引缓存

  共享访问索引缓存改善了性能,却不能完全消除线程间的冲突。它们仍然争抢控制管理存取索引缓存缓冲的结构。为了更进一步减少索引缓存存取冲突,MySQL 4.1.1提供了多重索引缓存特性。这能将不同的表索引指定到不同的索引缓存。

  当有多个索引缓存,服务器在处理指定的 MyISAM 表查询时必须知道该使用哪个。默认地,所有的 MyISAM 表索引都缓存在默认的索引缓存中。想要指定到特定的缓存中,可以使用 CACHE INDEX 语句。

  如下语句所示,指定表的索 t1, t2 和 t3 引缓存到名为 hot_cache 的缓存中:


  mysql> CACHE INDEX t1, t2, t3 IN hot_cache;
  +---------+--------------------+----------+----------+
  | Table | Op | Msg_type | Msg_text |
  +---------+--------------------+----------+----------+
  | test.t1 | assign_to_keycache | status | OK |
  | test.t2 | assign_to_keycache | status | OK |
  | test.t3 | assign_to_keycache | status | OK |
  +---------+--------------------+----------+----------+


  注意,如果服务器编译支持存 ISAM 储引擎了,那么 ISAM 表也使用索引缓存机制。不过,ISAM 表索引只能使用默认的索引缓存而不能自定义。
 CACHE INDEX 语句中用到的索引缓存是根据用 SET GLOBAL 语句的参数设定的值或者服务器启动参数指定的值创建的,如下:


  mysql> SET GLOBAL keycache1.key_buffer_size=128*1024;


  想要删除索引缓存,只需设置它的大小为0:


  mysql> SET GLOBAL keycache1.key_buffer_size=0;


  索引缓存变量是一个结构体变量,由名字和组件构成。例如 keycache1.key_buffer_size, keycache1 就是缓存名,key_buffer_size 是缓存组件。

  默认地,表索引在服务器启动时指定到主(默认的)索引缓存中。当一个索引缓存被删掉后,指定到这个缓存的所有索引都被重新指向到了默认索引缓存中去。

  对一个繁忙的系统来说,我们建议以下三条策略来使用索引缓存:

  热缓存占用20%的总缓存空间。用于繁重搜索但很少更新的表。
  冷缓存占用20%的总缓存空间。用于中等强度更新的表,如临时表。
  冷缓存占用60%的总缓存空间。作为默认的缓存,用于所有其他表。
  使用三个缓存的一个原因是好处在于,存取一个缓存结构时不会阻止对其他缓存的访问。访问一个表索引的查询不会跟指定到其他缓存的查询竞争。性能提高还表现在以下几点原因:

  热缓存只用于检索记录,因此它的内容总是不需要变化。所以,无论什么时候一个索引区块需要从磁盘中引入,被选中要替换的缓存区块的内容总是要先被刷新。

  索引被指向热缓存中后,如果没有需要扫描全部索引的查询,那么对应到B树中非叶子节点的索引区块极可能还保留在缓存中。

  在临时表里必须频繁执行一个更新操作是相当快的,如果要被更新的节点已经在缓存中了,它无需先从磁盘中读取出来。当临时表的索引大小和冷缓存大小一样时,那么在需要更新一个节点时它已经在缓存中存在的几率是相当高的。
中点插入策略

  默认地,MySQL 4.1的索引缓存管理系统采用LRU策略来选择要被清除的缓存区块,不过它也支持更完善的方法,叫做"中点插入策略"。

  使用中点插入策略时,LRU链就被分割成两半:一个热子链,一个温子链。两半分割的点不是固定的,不过缓存管理系统会注意不让温子链部分"太短",总是至少包括全部缓存区块的 key_cache_division_limit 比率。key_cache_division_limit 是缓存结构体变量的组件部分,因此它是每个缓存都可以设置这个参数值。

  当一个索引区块从表中读入缓存时,它首先放在温子链的末尾。当达到一定的点击率(访问这个区块)后,它就提升到热子链中去。目前,要提升一个区块的点击率(3)对每个区块来说都是一样的。将来,我们会让点击率依靠B树中对应的索引区块节点的级别:包含非叶子节点的索引区块所要求的提升点击率就低一点,包含叶子节点的B索引树的区块的值就高点。

  提升起来的区块首先放在热子链的末尾。这个区块在热子链内一直循环。如果这个区块在该子链开头位置停留时间足够长了,它就会被降级回温子链。这个时间是由索引缓存结构体变量的组件 key_cache_age_threshold 值来决定的。

  这个阀值是这么描述的,一个索引缓存包含了 N 个区块,热子链开头的区块在低于 N*key_cache_age_threshold/100 次访问后就被移动到温子链的开头位置。它又首先成为被删除的候选对螅蛭惶婊坏那榛故谴游伦恿吹目肺恢每嫉摹?/P>

  中点插入策略就能在缓存中总能保持更有价值的区块。如果更喜欢采用LRU策略,只需让 key_cache_division_limit 的值低于默认值 100。

  中点插入策略能帮助改善在执行需要有效扫描索引,它会将所有对应到B树中高级别的有价值的节点推出的查询时的性能。为了避免这样,就必须设定 key_cache_division_limit 远远低于100以采用中点插入策略。则在扫描索引操作时那些有价值的频繁点击的节点就会保留在热子链中了。

  索引预载入

  如果索引缓存中有足够的区块用来保存全部索引,或者至少足够保存全部非叶子节点,那么在使用前就载入索引缓存就很有意义了。将索引区块以十分有效的方法预载入索引缓存缓冲:从磁盘中顺序地读取索引区块。

  没有预载入,查询所需的索引区块仍然需要被放到缓存中去。虽然索引区块要保留在缓存中,因为有足够的缓冲,它们可以从磁盘中随机读取到,而非顺序地。

  想要预载入缓存,可以使用 LOAD INDEX INTO CACHE 语句。如下语句预载入了表 t1 和 t2 的索引节点(区块):


  mysql> LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES;
  +---------+--------------+----------+----------+
  | Table | Op | Msg_type | Msg_text |
  +---------+--------------+----------+----------+
  | test.t1 | preload_keys | status | OK |
  | test.t2 | preload_keys | status | OK |
  +---------+--------------+----------+----------+


  增加修饰语 IGNORE LEAVES 就只预载入非叶子节点的索引区块。因此,上述语句加载了 t1 的全部索引区块,但是只加载 t2 的非叶子节点区块。

  如果使用 CACHE INDEX 语句将索引指向一个索引缓存,将索引区块预先放到那个缓存中去。否则,索引区块只会加载到默认的缓存中去。
索引缓存大小

  MySQL 4.1引进了对每个索引缓存的新变量 key_cache_block_size。这个变量可以指定每个索引缓存的区块大小。用它就可以来调整索引文件I/O操作的性能。

  当读缓冲的大小和本地操作系统的I/O缓冲大小一样时,就达到了I/O操作的最高性能了。但是设置索引节点的大小和I/O缓冲大小一样未必能达到最好的总体性能。读比较大的叶子节点时,服务器会读进来很多不必要的数据,这大大阻碍了读其他叶子节点。

  目前,还不能控制数据表的索引区块大小。这个大小在服务器创建索引文件 `.MYI' 时已经设定好了,它根据数据表的索引大小的定义而定。在很多时候,它设置成和I/O缓冲大小一样。在将来,可以改变它的值,并且会全面采用变量 key_cache_block_size。

  重建索引缓存

  索引缓存可以通过修改其参数值在任何时候重建它,例如:


  mysql> SET GLOBAL cold_cache.key_buffer_size=4*1024*1024;


  如果设定索引缓存的结构体变量组件变量 key_buffer_size 或 key_cache_block_size 任何一个的值和它当前的值不一样,服务器就会清空原来的缓存,在新的变量值基础上重建缓存。如果缓存中有任何的'脏'索引块,服务器会先把它们保存起来然后才重建缓存。重新设定其他的索引缓存变量并不会重建缓存。

  重建缓存时,服务器会把所有的'脏'缓冲的内容先刷新到磁盘中去。之后,缓存的内容就无效了。不过,重建的时候并不阻止那些需要使用指向到缓存中的索引的查询。相反地,服务器使用本地文件系统缓存直接访问数据表索引。文件系统缓存不如索引缓存来的高效,因此,可以预见这时的查询会比较慢。一旦缓存重建完了,指向它的索引又可以使用了,同时也就不再使用文件系统缓存来访问索引了。

本文作者:佚名 来源:http://www.zxbc.cn/
CIO之家 www.ciozj.com 微信公众号:imciow
    >>频道首页  >>网站首页   纠错  >>投诉
版权声明:CIO之家尊重行业规范,每篇文章都注明有明确的作者和来源;CIO之家的原创文章,请转载时务必注明文章作者和来源;
延伸阅读