七、配置列族的块缓存
? ? ? ?HBase支持使用块缓存来提高读性能。在进行扫描时,如果块缓存功能已启用并且仍有剩余的空间,从HDFS上的StoreFile中读取到的数据块就会被缓存在RegionServer的Java堆空间中,以便下一次访问同一块中的数据时可以直接访问缓存中的块。块缓存有助于减少数据检索时的磁盘I/O。
? ? ? ?块缓存可在表的列族一级进行配置。不同的列族可以有不同的缓存优先级,甚至还可以禁用块缓存。应用程序应根据数据规模和访问模式的不同而适当地利用这一缓存机制。
启动HBase Shell hbase(main):002:0> create 'table1', {NAME => 'f1'}, {NAME => 'f2', IN_MEMORY => 'true'}, {NAME => 'f3', BLOCKCACHE => 'false'} 0 row(s) in 1.1080 seconds hbase(main):003:0> describe 'table1' DESCRIPTION ENABLED {NAME => 'table1', FAMILIES => [{NAME => 'f1', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => ' false', BLOCKCACHE => 'true'}, {NAME => 'f2', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 't rue', BLOCKCACHE => 'true'}, {NAME => 'f3', BLOOMFILTER => 'NONE', REPLICATION_SCOPE => '0', VERSIONS => '3 ', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '2147483647', BLOCKSIZE => '65536', IN_MEMORY => 'fal se', BLOCKCACHE => 'false'}]} 1 row(s) in 0.0810 seconds
? ? ? ?我们创建了一张表(table1),它带有三个列族(f1、f2和f3)。对于列族f1,我们未指定任何属性,所以其所有属性都设置的是默认值。该列族的块缓存已启用(BLOCKCACHE => 'true'),而且常驻内存是关闭的(IN_MEMORY => 'false')。?
? ? ? ?HBase块缓存的块优先级分为单次存取、多次存取和在内存中存取三种。如果有必要,可以给块添加一个内存中标志,这意味着:HBase会更为积极地尽量将其保留在内存中(但并不能保证将其保留在内存中);否则,该块就处于单次存取优先级。如果该块又被再次存取,它就会被标为多次存取级。分配给这三个优先级的缓存空间并不相同。单次存取和内存中存取各占总缓存空间的25%,多次存取占50%。
? ? ? ?在我们创建列族f2时,标明了要将in-memory属性打开。因此,属于该列族的块都将以内存中优先级进行缓存。列族f3禁用了块缓存,这就意味着该列族的数据块不会被放入缓存中。我们不建议禁用块缓存。
? ? ? ?因为数据是以块(Block)为基础进行缓存的,所以访问同一块中的数据的效率就会非常高。对于占有空间较小的行来说,尤其是这样。因此,“将会被同时间访问的数据放在同一个列族中”是设计表时的一种很好的作法。例如,在使用HBase来存储从互联网上抓取的页面时,较好的作法是:将网页的各种元数据保存在一个启用了内存中存取属性的meta列族中,而将网页的原始内容存储在另一个名为raw的列族中。
? ? ? ?现有列族的块缓存属性也可以在HBase Shell中通过alter命令来修改。
?
八、调高读密集集群的块缓存大小
? ? ? ?在“调整MemStore内存大小”和“配置列族的块缓存”两节中我们介绍过:若要提高写性能,RegionServer需将大量的Java堆空间分配给MemStore;若要提高读性能,RegionServer则需使用大量的堆空间来缓存StoreFile的文件块。
? ? ? ?在写性能与读性能之间需要平衡。不过,在读密集集群上读性能更重要,因此你应该给块缓存分配更多的内存。
vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hfile.block.cache.size</name> <value>0.3</value> </property>
? ? ? ?RegionServer上块缓存的总空间由hfile.block.cache.size属性来指定。此属性指定了将RegionServer堆空间最大值的多大百分比分配给块缓存。默认情况下,是分配堆空间最大值的25%。
? ? ? ?在第1步中,我们将块缓存的总空间在接到了RegionServer堆空间最大值的30%。在读密集集群中,建议减少一些MemStore的空间,将更多内存分配给块缓存。MemStore和块缓存加在一起通常会消耗RegionServer堆空间最大值的60%~70%。这是一个合理的值。MemStore上限与块缓存的总和不应高于这个水平,除非你能绝对肯定超过也没有问题。
? ? ? ?在确定应分配多少内存给块缓存时,另一个可用的方法是通过Ganglia或HBase的Web用户界面来查看RegionServer的各项指标。例如在HBase Web用户界面中,单击某RegionServer的链接就可以看到该RegionServer的一些最重要的指标(如下图所示)。
? ? ? ?查看该页面上的MemStore大小、块缓存大小、命中率等指标,从中可以发现一些有助于对集群进行调整的有用信息。如果块缓存的命中率非常低,可能就需要审查一下自己的表模式定义和数据访问模式,把那些总是同时被访问的列放在一起。使用Bloom Filter是另一种提高块缓存命中率的方法。如果出现了许多块驱逐(block eviction)的情形,那么就应该考虑增加块缓存的大小,以便可以存储更多的块。
?
九、客户端Scan类的设置
? ? ? ?为了获得更好的读性能,除了服务器端调整之外,最重要的是调整客户端应用程序上Scan类的设置。正确的客户端Scan类设置会使Scan过程的效率得到大大的提高。而反过来说,错误的Scan类设置不仅会减缓Scan本身,而且会对整个RegionServer产生负面影响。因此,我们需要仔细配置客户端的Scan类设置。
? ? ? ?最重要的Scan类设置包括Scan缓存行数、Scan属性选择和Scan块缓存。
? ? ? ?1.为了在调用Scan类的next()方法时能读取到更多的行,需调高hbase.client.scanner.caching属性的值。
vi $HBASE_HOME/conf/hbase-site.xml <property> <name>hbase.client.scanner.caching</name> <value>500</value> </property>