一、表压缩

1、HBase Sanppy

HBase Sanppy
1)配置Haodop压缩
    [beifeng@hadoop-senior hadoop-2.5.0]$ bin/hadoop checknative
2)配置HBase
    >> hadoop-snappy jar  -> 放入到HBASE的lib目录
    [root@hadoop-senior lib]# cp hadoop-snappy-0.0.1-SNAPSHOT.jar /opt/modules/hbase-0.98.6-hadoop2/lib/

    >> 需要将本地库native 
    [root@hadoop-senior lib]# mkdir /opt/modules/hbase-0.98.6-hadoop2/lib/native
    [root@hadoop-senior native]# cd /opt/modules/hbase-0.98.6-hadoop2/lib/native/
    [root@hadoop-senior native]# ln -s /opt/modules/hadoop-2.5.0/lib/native ./Linux-amd64-64
    [root@hadoop-senior native]# ll
    总用量 0
    lrwxrwxrwx. 1 root root 36 5月  27 11:41 Linux-amd64-64 -> /opt/modules/hadoop-2.5.0/lib/native
3) 重启HBASE
    [root@hadoop-senior hbase-0.98.6-hadoop2]# bin/hbase-daemon.sh stop master
    [root@hadoop-senior hbase-0.98.6-hadoop2]# bin/hbase-daemon.sh stop regionserver

    [root@hadoop-senior hbase-0.98.6-hadoop2]# bin/hbase-daemon.sh start master
    [root@hadoop-senior hbase-0.98.6-hadoop2]# bin/hbase-daemon.sh start regionserver

 

2、测试

##regionserver启用snappy压缩,hbase-site.xml
<property>
  <name>io.compression.codecs</name>
  <value>snappy</value>
  <description>A list of the compression codec classes that can be used 
               for compression/decompression.</description>
</property>




##
hbase(main):002:0> create 't_snappy', {NAME => 'f1', COMPRESSION => 'SNAPPY'}
0 row(s) in 0.4710 seconds

=> Hbase::Table - t_snappy

hbase(main):003:0> describe 't_snappy'
DESCRIPTION                                                                                                     ENABLED                                                     
 't_snappy', {NAME => 'f1', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERS true                                                        
 IONS => '1', COMPRESSION => 'SNAPPY', MIN_VERSIONS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS => 'false', BL                                                             
 OCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}                                                                                                            
1 row(s) in 0.0310 seconds

 

二、hbase的缓存机制

1、Memstore & BlockCache

HBase上Regionserver的内存分为两个部分,一部分作为Memstore,主要用来写;另外一部分作为BlockCache,主要用于读。

写请求会先写入Memstore,Regionserver会给每个region提供一个Memstore,当Memstore满64MB以后,会启动flush刷新到磁盘。
当Memstore的总大小超过限制时(heapsize*hbase.regionserver.global.memstore.upperLimit*0.9),会强行启动flush进程,从最大的Memstore开始flush直到低于限制。

读请求先到Memstore中查数据,查不到就到BlockCache中查,再查不到就会到磁盘上读,并把读的结果放入BlockCache。
由于BlockCache采用的是LRU策略,因此BlockCache达到上限(heapsize*hfile.block.cache.size*0.85)后,会启动淘汰机制,淘汰掉最老的一批数据。

在注重读响应时间的应用场景下,可以将BlockCache设置大些,Memstore设置小些,以加大缓存的命中率。

 

参考博文:https://blog.51cto.com/12445535/2363376

hbase两种缓存机制:MemStore和BlockCache

背景:
1、缓存对于数据库来说极其的重要
2、最理想的情况是,所有数据都能够缓存到内存,这样就不会有任何文件IO请求,读写性能必然会提升到极致。
3、我们并不需要将所有数据都缓存起来,根据二八法则,80%的业务请求都集中在20%的热点数据上,
4、把20%的数据缓存起来,将这部分数据缓存起就可以极大地提升系统性能。

HBase在实现中提供了两种缓存结构:MemStore和BlockCache。
MemStore
1、其中MemStore称为写缓存
2、HBase执行写操作首先会将数据写入MemStore,并顺序写入HLog,
//代码中这样,我们的理解为 先顺序写入HLog 再将数据写入MemStore
3、等满足一定条件后统一将MemStore中数据刷新到磁盘,这种设计可以极大地提升HBase的写性能。
4、MemStore对于读性能也至关重要,假如没有MemStore,读取刚写入的数据就需要从文件中通过IO查找,这种代价显然是昂贵的!

BlockCache
1、BlockCache称为读缓存
2、HBase会将一次文件查找的Block块缓存到Cache中,以便后续同一请求或者邻近数据查找请求,可以直接从内存中获取,避免昂贵的IO操作。


##
1、BlockCache是Region Server级别的,
2、一个Region Server只有一个Block Cache,在Region Server启动的时候完成Block Cache的初始化工作。
3、到目前为止,HBase先后实现了3种Block Cache方案,LRUBlockCache是最初的实现方案,也是默认的实现方案;HBase 0.92版本实现了第二种方案SlabCache,见HBASE-4027;HBase 0.96之后官方提供了另一种可选方案BucketCache,见HBASE-7404。 
4、这三种方案的不同之处在于对内存的管理模式,
5、其中LRUBlockCache是将所有数据都放入JVM Heap中,交给JVM进行管理。
6、SlabCache BucketCache 这两种采用了不同机制将部分数据存储在堆外,交给HBase自己管理。
7、这种演变过程是因为LRUBlockCache方案中JVM垃圾回收机制经常会导致程序长时间暂停,而采用堆外内存对数据进行管理可以有效避免这种情况发生。

 

LRUBlockCache

HBase默认的BlockCache实现方案
1、将内存从逻辑上分为了三块:single-access区、mutil-access区、in-memory区,分别占到整个BlockCache大小的25%、50%、25%
2、一次随机读中,一个Block块从HDFS中加载出来之后首先放入signle区,
3、后续如果有多次请求访问到这块数据的话,就会将这块数据移到mutil-access区。
3、而in-memory区表示数据可以常驻内存,一般用来存放访问频繁、数据量小的数据,比如Meta元数据,用户也可以在建表的时候通过设置
列族属性IN-MEMORY= true将此列族放入in-memory区。 //这一部分参考 HBase - 建表语句解析 http://hbasefly.com/2016/03/23/hbase_create_table/ 中
提到的 IN_MEMORY 参数;
4、很显然,这种设计策略类似于JVM中young区、old区以及perm区。

阶段小结:
LRUBlockCache机制:类似于jvm的young区、old区以及perm区,他分为(single-access区、mutil-access区、in-memory区,分别占到整个BlockCache大小
的25%、50%、25%)在一次随机访问数据的时候从hdfs加载出来,放到single-access区,后续如果有多次请求这块的数据,就会放到 mutil-access区, 
而in-memory区表示数据可以常驻内存,一般用来存放访问频繁、数据量小的数据,比如元数据。
//当BlockCache总量达到一定阈值之后就会启动淘汰机制,最少使用的Block会被置换出来,为新加载的Block预留空间。
缺点:使用LRUBlockCache缓存机制会因为CMS GC策略导致内存碎片过多,从而可能引发臭名昭著的Full GC,触发可怕的’stop-the-world’暂停,严重影响上层业务;

 

1、它使用一个ConcurrentHashMap管理BlockKey到Block的映射关系,
2、缓存Block只需要将BlockKey和对应的Block放入该HashMap中,查询缓存就根据BlockKey从HashMap中获取即可。
3、同时该方案采用严格的LRU淘汰算法,当Block Cache总量达到一定阈值之后就会启动淘汰机制,最近最少使用的Block会被置换出来。
在具体的实现细节方面,需要关注以下三点:

(1)缓存分层策略
//将整个BlockCache分为三个部分:single-access、mutil-access和inMemory。需要特别注意的是,HBase系统元数据存放在InMemory区,
因此设置数据属性InMemory = true需要非常谨慎,确保此列族数据量很小且访问频繁,否则有可能会将hbase.meta元数据挤出内存,严重影响所有业务性能。

(2)LRU淘汰算法实现

(3)LRU方案优缺点
//LRU方案使用JVM提供的HashMap管理缓存,简单有效。
但是:会出现full gc 碎片空间一直累计就会产生臭名昭著的Full GC。
尤其在大内存条件下,一次Full GC很可能会持续较长
时间,甚至达到分钟级别。大家知道Full GC是会将整个进程暂停的(称为stop-the-wold暂停),因此长时间Full GC必然会极大影响业务的正常读写请求。

  

SlabCache   //已经被淘汰了

1、为了解决LRUBlockCache方案中因为JVM垃圾回收导致的服务中断,SlabCache方案使用Java NIO DirectByteBuffer技术实现了堆外内存存储,不再由JVM管理数据内存。
2、默认情况下,系统在初始化的时候会分配两个缓存区,分别占整个BlockCache大小的80%和20%,每个缓存区分别存储固定大小的Block块,
3、其中前者主要存储小于等于64K大小的Block,后者存储小于等于128K Block,如果一个Block太大就会导致两个区都无法缓存。
4、和LRUBlockCache相同,SlabCache也使用Least-Recently-Used算法对过期Block进行淘汰。
5、和LRUBlockCache不同的是,SlabCache淘汰Block的时候只需要将对应的bufferbyte标记为空闲,后续cache对其上的内存直接进行覆盖即可。

  

SlabCache 和 DoubleBlockCache 缺点为:

线上集群环境中,不同表不同列族设置的BlockSize都可能不同,很显然,默认只能存储两种固定大小Block的SlabCache方案不能满足部分用户场景,
因此HBase实际实现中将SlabCache和LRUBlockCache搭配使用,称DoubleBlockCache。

1、DoubleBlockCache方案有很多弊端。比如SlabCache设计中固定大小内存设置会导致实际内存使用率比较低,
2、而且使用LRUBlockCache缓存Block依然会因为JVM GC产生大量内存碎片。
3、因此在HBase 0.98版本之后,该方案已经被不建议使用。

  

SlabCache:实现的是堆外内存存储,不在由JVM管理数据内存。
DoubleBlockCache:因此HBase实际实现中将SlabCache和LRUBlockCache搭配使用,称DoubleBlockCache。

为什么要搭配使用呢?或者说是SlabCache的缺点
1、线上集群环境中,不同表不同列族设置的BlockSize都可能不同,很显然,默认只能存储两种固定大小Block的SlabCache方案不能
满足部分用户场景,比如用户设置BlockSize = 256K,简单使用SlabCache方案就不能达到这部分Block缓存的目的。
2、一次随机读中,一个Block块从HDFS中加载出来之后会在两个Cache中分别存储一份;缓存读时首先在LRUBlockCache中查找,
如果Cache Miss再在SlabCache中查找,此时如果命中再将该Block放入LRUBlockCache中。

缺点:
DoubleBlockCache方案有很多弊端。比如SlabCache设计中固定大小内存设置会导致实际内存使用率比较低,而且使用LRUBlockCache缓
存Block依然会因为JVM GC产生大量内存碎片。因此在HBase 0.98版本之后,该方案已经被不建议使用。

  

BucketCache

//阿里设计出来的 cdh用的这种 //可以参考:hbase针对fullgc所做的优化(Memstore所作的优化 针对BlockCache所作优化):
https://blog.51cto.com/12445535/2373223

1、SlabCache方案在实际应用中并没有很大程度改善原有LRUBlockCache方案的GC弊端,还额外引入了诸如堆外内存使用率
低的缺陷。然而它的设计并不是一无是处,至少在使用堆外内存这个方面给予了阿里大牛们很多启发。站在SlabCache的肩膀上,
他们开发了BucketCache缓存方案并贡献给了社区。
2、实际实现中,HBase将BucketCache和LRUBlockCache搭配使用,称为CombinedBlockCache。
3、和DoubleBlockCache不同,系统在LRUBlockCache中主要存储Index Block和Bloom Block,而将Data Block存储在BucketCache中
4、因此一次随机读需要首先在LRUBlockCache中查到对应的Index Block,然后再到BucketCache查找对应数据块。
BucketCache通过更加合理的设计修正了SlabCache的弊端,极大降低了JVM GC对业务请求的实际影响,
5、但也存在一些问题,比如使用堆外内存会存在拷贝内存的问题,一定程度上会影响读写性能。当然,在后来的版本中这个问题也得到了解决,

优点:
而Bucket Cache缓存机制因为在初始化的时候就申请了一片固定大小的内存作为缓存,缓存淘汰不再由 JVM管理,数据Block的
缓存操作只是对这片空间的访问和覆盖,因而大大减少了内存碎片的出现,降低了Full GC发生的频率。
//Bucket Cache 缓存淘汰不再由 JVM管理 降低了Full GC发生的频率。
Bucket Cache 缓存 中有3中模式 heap模式和offheap模式 file模式 
offheap模式因为内存属于操作系统,所以基本不会产生CMS GC,也就在任何情况下都不会因为内存碎片导致触发Full GC。

  

它没有使用JVM 内存管理算法来管理缓存,而是自己对内存进行管理,因此不会因为出现大量碎片导致Full GC的情况发生。

BucketCache配置使用:
BucketCache 的总大小,以 MB 为单位。要配置的大小取决于可供 HBase 使用的内存量或本地 SSD 的大小。如果将 hbase.bucketcache.ioengine 设
为“offheap”,则 BucketCache 会消耗 Java 的直接内存中的已配置内存量。
hbase.bucketcache.size = 1M

提示:
其中heap模式和offheap模式都使用内存作为最终存储介质,内存分配查询也都使用Java NIO ByteBuffer技术,不同的是,heap模式分配
内存会调用byteBuffer.allocate方法,从JVM提供的heap区分配,而后者会调用byteBuffer.allocateDirect方法,直接从操作系统分配。
这两种内存分配模式会对HBase实际工作性能产生一定的影响。影响最大的无疑是GC ,相比heap模式,offheap模式因为内存属于操作系统,
所以基本不会产生CMS GC,也就在任何情况下都不会因为内存碎片导致触发Full GC。
除此之外,在内存分配以及读取方面,两者性能也有不同,比如,内存分配时heap模式需要首先从操作系统分配内存再拷贝到JVM heap,
相比offheap直接从操作系统分配内存更耗时;但是反过来,读取缓存时heap模式可以从JVM heap中直接读取,而offheap模式则需要首先
从操作系统拷贝到JVM heap再读取,显得后者更费时。

file模式和前面两者不同,它使用Fussion-IO或者SSD等作为存储介质,相比昂贵的内存,这样可以提供更大的存储容量,因此可以极大地提升缓存命中率。

 

三、表的Compaction

1、Compaction

随着memstore中的数据不断刷写到磁盘中,会产生越来越多的HFile文件,HBase内部有一个解决这个问题的管家机制,即用合并将多个文件合并成一个较大的文件。
合并有两种类型:minor合并(minor compaction)和major压缩合并(majar compaction)。minor合并将多个小文件重写为数量较少的大文件,减少存储文件的数量,
这个过程实际上是个多路归并的过程。因为HFile的每个文件都是经过归类的,所以合并速度很快,只受到磁盘I/O性能的影响。

major 合并将一个region中一个列族的若干个HFile重写为一个新HFile,与minor合并相比,还有更独特的功能:major合并能扫描所有的键/值对,顺序重写全部的数据,
重写数据的过程中会略过做了删除标记的数据。断言删除此时生效,例如,对于那些超过版本号限制的数据以及生存时间到期的数据,在重写数据时就不再写入磁盘了。


########
HRegoin Server的storefile文件是被后台线程监控的,以确保这些文件保持在可控状态。磁盘上的storefile的数量会随着越来越多的memstore被刷新而变的越来越多,
每次刷新都会生成一个storefile文件。当storefile数量满足一定条件时(可以通过配置参数类调整),会触发文件合并操作minor compaction,将多个比较小的storefile
合并成一个大的storefile文件,直到合并的文件大到超过单个文件配置允许的最大值时会触发一次region的自动分割,即region split操作,将一个region平分成2个。


################
minor compaction
轻量级
将符合条件的最早生成的几个storefile合并生成一个大的storefile文件,它不会删除被标记为“删除”的数据和以过期的数据,并且执行过一次minor合并操作后还会有多个storefile文件。

major compaction
重量级
把所有的storefile合并成一个单一的storefile文件,在文件合并期间系统会删除标记为“删除“标记的数据和过期失效的数据,同时会block(阻塞)所有客户端对该操作所属的region的请求直到合并完毕,最后删除已合并的storefile文件。