zoukankan      html  css  js  c++  java
  • HBase Block Cache(块缓存)

    Block Cache

    HBase提供了两种不同的BlockCache实现,用于缓存从HDFS读出的数据。这两种分别为:

    1. 默认的,存在于堆内存的(on-heap)LruBlockCache
    2. 存在堆外内存的(off-heap)BucketCache

    下面我们会讨论每种方法的优点和缺点、如何对两种方式做选择,以及这两种类型的相关配置。

    Cache Choices

    LruBlockCache是最初始的实现,并且全部存在Java堆内存中。BucketCache是另一个选择,主要用于将block cache的数据存在off-heap(堆外内存),不过BlockCache也可以作为一种文件备份式的缓存。

    当开启了BucketCache后,便启用了两级缓存的系统。以前我们会用“L1”和“L2”来描述这两个等级,但是现在这个术语已经在hbase-2.0.0后被弃用了。现在“L1” cache 直接指的是LruBlockCache,“L2”指的是一个off-heap的BucketCache。(hbase-2.0.02之后)当BucketCache启用后,所有数据块(DATA block)会被存在BucketCache 层,而meta 数据块(INDEX 以及BLOOM块)被存在on-heap的LruBlockCache中。管理这两层缓存,以及指示数据块如何在它们之间移动的策略,由CombinedBlockCache完成。

    Cache的常规配置

    除了缓存它自己的实现以外,我们也可以设置一些常规的配置选项,用于控制cache的行为。具体可以参考CacheConfig的文档:

    https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/io/hfile/CacheConfig.html

    在设置或修改了任何属性后,需要重启HBase集群以让配置文件生效。若是遇到异常,可以进一步查看HBase中的报错。

    LruBlockCache的设计

    LruBlockCache是一个LRU缓存,包括三种优先级,以适应于于:scan-resistance 以及 in-memory ColumnFamilies场景。三种优先级分别为:

    1. Single Access 优先级:当一个数据块第一次从HDFS读取时,它会具有这种优先级,并且在缓存空间需要被回收(置换)时,它属于优先被考虑范围内。它的优点在于:一般被扫描(scanned)读取的数据块,相较于之后会被用到的数据块,更应该被优先清除
    2. Multi Access优先级:如果一个数据块,属于Single Access优先级,但是之后被再次访问,则它会升级为Multi Access优先级。在缓存里的内容需要被清除(置换)时,这部分内容属于次要被考虑的范围
    3. In-memory Access优先级:如果数据块族被配置为“in-memory”,则会具有这种优先级,并且与它被访问的次数无关。HBase Catalog便是被配置的这个优先级。在缓存里的内容需要被置换时,这部分内容属于最后被考虑的范围。若是需要将一个列族标注为此优先级:
      1. 在Java中可以调用: HColumnDescriptor.setInMemory(true);
      2. 在hbase shell 中创建或修改一个表时,可以使用 IN_MEMORY => true,例如:create ‘t’, {NANME => ‘f’, IN_MEMORY => ‘true’}

    若是想了解更具体的信息,可以参考LruBlockCache 源码

    LruBlockCache 的使用

    一般来说,BlockCache在所有用户表中默认是开启的,也就是说,任何的读操作均会加载LRU Cache。这个方案可能适用于大部分场景,但是,如果需要达到更优的performance,仍需要做一些调整。其中一个很重要的概念是working set size(或WSS),它的意思是:为了解决一个问题所需要的资源(内存)大小。对于一个网站来说,这个便是在短时间内响应请求所需要的数据量。计算在HBase中到底有多少内存可供cache的方法为:

    number of region servers * heap size * hfile.block.cache.size * 0.99

    block.cache的默认值是0.4,表示可用堆内存的40%。最后一个值(99%)是:在缓存内存收回开始后,默认可被回收的比率(这里若是值是100%,则不太现实,因为在置换时也会有新块写入?)。下面是使用这个公式的一些例子:

    1. 一个region server,设置了1GB的堆内存,使用默认的block.cache参数,则会有405MB的block cache可用
    2.  20个region server,设置了8GB大小的堆内存,使用默认的block.cache参数,则会有63GB大小的block cache可用(20 × 8 × 0.4 × 0.99)
    3. 100 个region server,设置了24GB 大小的堆内存,使用block.cache=0.5,则会有1.16TB的可用block cache

    当然,被存在block cache中的并不仅仅只是你的数据,下面是其他一些需要被考虑到的地方:

    1. Catalog :hbase:meta 表被强制载入block cache,并且具有in-memory 优先级,也就是说,它的内容几乎很少会被从缓存移除。(根据regions的数量,hbase:meta 表会占据一小部分内存)
    2. HFile 索引:HFile是HBase用于在HDFS上存储数据数据文件格式。它包含多级索引,可以让HBase在不需要读入整个文件的情况下找到目标数据。决定这些索引大小的因素是:数据块大小(默认是64KB),你的key大小,以及存储数据的量。对于大数据集来说,一个region server 的cache中包含大约1GB的索引大小也属正常,尽管不是所有的索引均会被放入cache(因为LRU会将那些不常用的索引移除)。
    3. Keys:The values that are stored are only half the picture, since each value is stored along with its keys (row key, family qualifier, and timestamp).
    4. Bloom Filter:如HFile的索引一样,那些数据结构(若是在enabled之后),会被存在LRU

    当前来说,衡量HFile索引以及Bloom filter 大小的一种推荐的方法是:查看region server UI,并查看相关指标。对于键来说,获取抽样时,可以使用HFile的命令行工具,并查看键的平均大小。从HBase 0.98.3以后,可以在UI界面中的Block Cache部分,查看BlockCache的详细状态以及指标。

    一般来说,如果当前可用内存并不足以支持WSS,则不建议使用block caching。举个例子:假设在集群中一共有40GB的可用内存分布于各个region server的block cache,但是你需要处理1TB的数据,则这种场景不太适合使用block caching。其中一个原因是:回收(置换)缓存会打乱内存分布,并触发更多本没必要的垃圾回收。下面是两个场景:

    1. 完全随机的读模式:这种场景一般是,在短时间内,应用几乎不会重复读取表中同一行的内容,所以在这种情况下,命中cache的机会基本接近于0。在这个表中设置block caching属于浪费内存以及CPU的时间片。不仅如此,它还会产生更多的JVM垃圾回收事件。
    2. Mapping a table:比如在某个MapReduce任务中,任务的输入是一张表。每一行仅会被读取一次,所以就没必要将这些数据放入block cache。在Java中,Scan对象有一个关闭block cache的功能:setCaching(设置为false)。当然,如果你需要快速的随机读访问,也可以在这个表上保持开启block caching功能。

    仅缓存META数据块(DATA 数据块在fscache

    一个有趣的设置是:仅缓存 META数据块,每次在读取数据时,均去访问DATA数据块。在这种情况下如果DATA数据块适应于fscache,且当访问数据的操作在一个很大的集群中完全是随机时,这种设置是可取的。若要开启这个设置,可以直接修改表:对某个列族,设置BLOCKCACHE => ‘false’。这样就关闭了对这个列族的BlockCache。不过,META数据块的block caching无法被关闭,即使它的被关闭了,META数据块也仍会被载入缓存。

    堆外(Off-heapBlock Cache

    如何开启BucketCache

    一个通常的部署BucketCache方式是通过一个管理类,它设置两级缓存:一个堆内的缓存,由LruBlockCache实现;以及第二层缓存,由BucketCache实现。默认管理的类为CombinedBlockCache。简单的说,这个类实现的缓存规则是:将meta数据块(INDEX以及BLOOM)放在堆内缓存(LruBlockCache层),而将DATA数据放入BucketCache层。

    在HBase-2.00 版本之前

    在hbase 2.00 版本以前,从BucketCache取数据时都会比较慢(对比使用堆内存的LruBlockCache)。然而,从表现来看,读操作的延迟时间基本趋于稳定。因为在使用BucketCache时,会有较少的垃圾回收(BucketCache管理BlockCache的分配,而不是GC)。如果BucketCache被部署为堆外(off-heap)模式,则这部分内存根本不会被GC管理。这就是为什么你在2.0.0版本之前的HBase使用BucketCache时,延迟时间基本趋于稳定,并可以减轻GC以及堆内存碎片的影响,这样可以安全的使用更多内存。如果你希望缓存不被GC管理,可以使用BucketCache。

    在2.0.0版本前,在配置了BucketCache后,可以减少LruBlockCache置换的影响。所有数据以及index块首先被缓存在L1。当L1中发生缓存清除(置换)时,被置换出的数据块会被移动到L2。在Java中,可以通过HColumnDescriptor.setCacheDataInL1(true)设置cacheDataInL1;在hbase shell中可以设置CACHE_DATA_IN_L1 为true,例如:create ‘t1’, {NamE => ‘t1’, CONFIGURATION => {CACHE_DATA_IN_L1 => ‘true’}}

    HBase-2.0.0 版本之后

    HBASE-11425改变了HBase的数据读取路径,实现了直接从堆外读取数据。off-heap的延迟可以接近于on-heap的延迟,因为off-heap并不会引起GC操作。

    从HBase 2.0.0 开始,L1与L2的概念便被弃用。当BucketCache启用时,数据块(DATA blocks)会一直保存于BucketCache;INDEX/BLOOM块会保存于LRUBlockCache的堆内存。cacheDetaInL1 的配置也被移除。

    BucketCache的块缓存可以被部署为off-heap,文件,或mmaped文件这三种模式(通过hbase.bucketcache.ioengine配置)。设置为offheap会让BucketCache在堆外内存管理block cache。设置为file:PATH_TO_FILE(EMR里默认为files:/mnt/hbase/bucketcache),会直接让BucketCache使用文件缓存(如果卷是SSD之类的高速盘的话,会比较有用)。从2.0.0 开始,使用多个文件路径也是可以的。若是在需要较大Cache 的场景下,这个配置很有用。在设置时的基本配置为:files:PATH_TO_FILE1, PATH_TO_FILE2, PATH_TO_FILE3。BucketCache可以也可以配置为使用一个mmapped文件。配置ioengine为mmap:PATH_TO_FILE即可。

    在hbase 2.0.0之前,也可以设置多级缓存(绕过CombinedBlockCache策略),将BucketCache设置为严格的L2 缓存,LruBlockCache为L1缓存。在这种配置中,设置 hbase.bucketcache.combinedcache.enable 为false即可。在这种模式下,当L1缓存内容被清除(置换)时,会将置换出的块放入L2。当一个块被缓存时,首先被缓存在L1。当我们去查询一个缓存块时,首先在L1查,若是没找到,则再搜索L2。我们将此部署方法称为Raw L1+L2。需要注意的是,这个L1+L2模式已经在hbase 2.0.0 以后被移除了。当BucketCache被使用时,它会严格的将DATA块缓存放入BucketCache,而INDEX/META块则被放入LruBlockCache。

    其他BucketCache的配置包括:指定cache被持久化的路径以在重启后仍存在、写cache的线程数量,等等。在检查它是否开启时,可以查看日志内容,会包含cache的设置;它会详细的记录BucketCache被部署的信息。也可以通过UI,它可以看到详细的cache层级以及它们的配置。

    BucketCache示例配置

    这个例子提供了为一个4GB堆外BucketCache、1GB堆内缓存的配置。配置过程在RegionServer上实施。

    设置hbase.bucketcache.ioengine,并设置 hbase.bucketcache.size > 0,开启CombinedBlockCache。这里我们假设当前RegionServer配置了5GB的堆内存(HBASE_HEAPSIZE=5g)

    1. 首先,编辑RegionServer的hbase-env.sh 文件,并设置HBASE_OFFHEAPSIZE,需要高于所需的off-heap的大小。在这个案例中需要的off-heap为4GB,所以我们设置此值为5GB。这里4GB被用于我们的off-heap缓存,剩余的1G被其他用户使用(因为会有其他用户也会使用off-heap内存;例如RegionServer中的DFSClient会使用堆外内存,参考下面的Direct Memory Usage in HBase)。HBASE_OFFHEAPSIZE=5G
    2. 然后,在hbase-site.xml 下设置以下配置:

    <property>

      <name>hbase.bucketcache.ioengine</name>

      <value>offheap</value>

    </property>

    <property>

      <name>hfile.block.cache.size</name>

      <value>0.2</value>

    </property>

    <property>

      <name>hbase.bucketcache.size</name>

      <value>4196</value>

    </property>

    1. 重启集群,若出现任何问题,检查日志

    上面的配置文件中,我们设置了BucketCache为4G,配置on-heap LruBlockCache为20%的RegionServer的堆内存(0.2 × 5G = 1G)。

    HBASE-10641以后,HBase 0.98及以后的版本,引入了可以为BucketCache配置多个bucket以及它们的大小的功能。配置多个bucket sieze,可以设置hbase.bucketcache.bucket.sizes为一系列块大小设置,从小到大。它的目的是根据你数据访问模式,优化bucket sizes。下面是一个示例配置,大小从4096到8192:

    <property>

      <name>hbase.bucketcache.bucket.sizes</name>

      <value>4096,8192</value>

    </property>

    Direct Memory Usage in HBase

    默认最大可以使用的direct memory因JVM的不同而不同。传统下是64M,或者通过直接分配堆内存(-Xmx),或完全没有限制(如JDK7)。HBase服务器使用direct memory,特别是short-circuit reading(读数据不经过DataNode,客户端直接读文件),RegionServer上的DFSclient会分配direct memory buffers。DFSClient会使用的内存大小并不容易量化;它是由:打开的HFile文件数量 × hbase.dfs.client.read.shortcircuit.buffer.size 决定。hbase.dfs.client.read.shortcircuit.buffer.size在HBase中设置为128k(参考hbae-default.xml默认配置)。如果需要使用off-heap block caching,则需要使用到直接内存(direct memory)。在RPC Server中,也会使用一个ByteBuffer池,从hbase 2.0.0开始,这些缓冲区为off-heap ByteBuffers。在启动JVM时,确保 -XX:MaxDirectMemorySize 的设置(在hbase-env.sh)考虑到了off-heap BlockCache(hbase.bucketcache.size)、DFSClient的使用量,以及RPC端的ByteBufferPool的最大总和大小。Direct memory 的大小应该比 off-heap BlockCache + max ByteBufferPool 的大小更大。在一般情况下,可以在基于所需的direct memory大小情况下,额外再分配多1-2GB的空间。Direct memory属于Java进程堆的一部分,与对象堆(由-Xmx分配)分离。MaxDirectMemorySize的大小必须小于物理的RAM大小,并且小于所有可用的RAM大小(由于内存的其他用处,以及系统的限制)。

    你可以在UI中的Server Metrics: Memory 栏看到一个RegionServer配置的内存量(on-heap以及off-heap/direct memory)。这部分数据也可以通过JMX获取。

    BlockCache 压缩

    HBASE-11331引入了lazy BlockCache decompression。在开启此功能后,DATA(以及ENCODED_DATA)数据块会以它们on-disk的形式缓存到BlockCache。与默认的模式不同点在于:默认情况下,在缓存一个数据块时,会先解压缩、解密,然后存入缓存(因为数据块是从HDFS取)。而lazy BlockCache decompression 直接将数据块存入缓存。

    如果一个RegionServer存储的数据过多,无法适当的将大部分数据放入缓存,则开启这个功能(使用SNAPPY压缩)后,实验证明:可以提升50%的throughput,30%的平均延迟上升,增加80%垃圾回收,以及2%的整体CPU负载。

    对于一个RegionServer,如果它的数据量已经适合cache的大小,或者你的应用对额外的CPU或GC的负载格外敏感,则这个选项不会有太大用处。

    默认情况下,这个功能是关闭的,若要开启,可以在所有的RegionServer中的hbase-site.xml文件里设置 hbase.block.data.cachecompressed 为 true

    References:

    http://hbase.apache.org/book.html#arch.overview

  • 相关阅读:
    Asp.net 动态添加Meta标签
    【转】在SharePoint Server 2010中更改“我的网站”
    SPQuery DateTime 类型查询
    Asp.net Web Application 打开 SharePoint 2010 Site 错误 The Web application at could not be found
    How To Create SharePoint 2010 Site Collection In Its Own DB
    C# 文件打印
    面试题 java集合
    《深入理解Java虚拟机》(六)堆内存使用分析,垃圾收集器 GC 日志解读
    《深入理解Java虚拟机》(五)JVM调优
    《深入理解Java虚拟机》(四)虚拟机性能监控与故障处理工具
  • 原文地址:https://www.cnblogs.com/zackstang/p/10061379.html
Copyright © 2011-2022 走看看