一、HFile详解
HFile文件分为以下六大部分
序号 |
名称 |
描述 |
1 |
数据块 |
由多个block(块)组成,每个块的格式为: [块头] + [key长] + [value长] + [key] + [value]。 |
2 |
元数据块 |
元数据是key-value类型的值,但元数据快只保存元数据的value值,元数据的key值保存在第五项(元数据索引块)中。 该块由多个元数据值组成。 |
3 |
fileInfo块 |
该块保存与HFile相关的一些信息。 fileInfo是以key值排序key-value类型的值。基本格式为: keyValue元素的个数 + (key + value类型id + value) + (key + value类型id + value) + …… |
4 |
数据索引块 |
该块的组成为: 索引块头 + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个key) + (数据块在文件中的偏移 + 数据块长 + 数据块的第一个key) + …… |
5 |
元数据索引块 |
该块组成格式同数据块索引,只是某部分的意义不一样,组成格式: 索引块头 + (元数据在文件中的偏移 + 元数据value长 + 元数据key) + (元数据在文件中的偏移 + 元数据value长 + 元数据key) + …… |
6 |
HFile文件尾 |
该块记录了其他各块在hfile文件中的偏移信息和其他一些元信息。组成格式如下: 文件尾 + Fileinfo偏移 + 数据块索引偏移 + 数据块索引个数 + 元数据索引偏移 + 元数据索引个数 + 数据块中未压缩数据字节数 + 数据块中全部数据的key-value个数 + 压缩代码标识 + 版本标识 |
1.1.数据块详解
数据块组成图如下:
Data Block Magic {'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 } |
|||
key长 |
value长 |
key |
value |
key长 |
value长 |
key |
value |
…… |
|||
key长 |
value长 |
key |
value |
Data Block Magic {'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 } |
|||
key长 |
value长 |
key |
value |
key长 |
value长 |
key |
value |
…… |
|||
key长 |
value长 |
key |
value |
Blocks …… |
数据块部分由多个block块组成,每个数据块由块头 + 多个cell(key-value对)集合组成,如上图。每个数据块的大小在创建表的列族的时候可以指定,默认为(64 * 1024)。
1. 块大小的设定由HColumnDescriptor.setBlockSize(int)指定,默认(64 * 1024)。
2. 块大小设置,块设置的越小,访问速度越快,但数据块索引越大,消耗的内存越多。因为在加载HFile时会把数据块索引全部加载到内存中。
数据块组成说明:
1. Data Block Magic – 数据块头,8字节,固定字节如下:{'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 }。
2. key长 – 4字节整型,记录每个cell的key的长度。
3. value长 – 4字节整型,记录每个cell的value的长度。
4. key – cell的key值,byte[]类型,组成如下:
rowKey的长(2字节)+ rowKey + family的长(1字节) + family + qualify + timestampe(8字节) + keyType类型(1字节)
1)rowKey的长度不能大于0x7fff(32767).
2)rowKey不能为空。
3)family(列族)的长度不能大于0x7f(127)
4)qualify(限定符)的长度不能大于(0x7fffffff(2147483647) – row长度 – family长度)。
5. value – cell的value值,byte[]类型,value值不能为空。
例如:在Hbase中有一个表(student),其中有一个列族(info),该列族不压缩。其中的rowkey用学号表示,现在插入一个记录(rowkey='033', qualify='age', value='19')。那么该记录将被表示成一个cell(key-value对)保存到HFile中,那么该cell在HFile中的内容如下:
项 |
字节表示 |
key长 |
{0,0,0,24} |
value长 |
{0,0,0,2} |
key |
{0,3}+{'0', '3', '3'}+{4}+{'i', 'n', 'f', 'o'}+{'a', 'g', 'e'}+{0,0,0,0,0,0,0,8}+{4} |
value |
{'1', '9'} |
问题:
1. 块大小的设置策略?
2. keyType的说明?
3. compress压缩的说明?
1.2.元数据块详解
每个元数据是key-value类型的值,新增的元数据会按照从小到大的顺序排序。
在StoreFile中,如果使用BloomFilter,则StoreFile将会把BloomFilter的信息保存到HFile中的元数据中, 元数据块中只保存元数据的value值,key值保存在元数据索引块中。格式如下:
Meta Block Magic {'M', 'E', 'T', 'A', 'B', 'L', 'K', 99 } |
Meta Data Value |
Meta Block Magic {'M', 'E', 'T', 'A', 'B', 'L', 'K', 99 } |
Meta Data Value |
…… |
每个元数据由元数据头+元数据值组成。
1.3.FileInfo详解
fileInfo中保存的信息为key-value类型的值,其中key与value都是byte[]类型。每个新增的值在内部都以值key顺序从小到大进行排序。fileInfo保存了与该HFile相关的一些信息,其中有系统保留的一些固定的值,这些值的key以”hfile.”为前缀。也可以保存用户自定义的一些值,但这些值的key不能以”hfile.”开头。其中系统内部保留的一些值如下:
项 |
key(字符串表示,实际以二进制存储) |
value |
LASTKEY |
hfile.LASTKEY |
该HFile中的数据块中的最后一个值的key, 该值如果为空则不进行保存 |
AVG_KEY_LEN |
hfile.AVG_KEY_LEN |
该HFile中的数据块中的所有值key的平均长度。 |
AVG_VALUE_LEN |
hfile.AVG_VALUE_LEN |
该HFile中的数据块中的所有值value的平均长度。 |
COMPARATOR |
hfile.COMPARATOR |
在HFile中的数据块中的值都是以值的key进行排序来存放的,而key的组成比较复杂,这就需要一个key的比较器类,而该值保存了key值比较器的类的名称。 |
fileInfo在HFile中的格式如下:
filInfo中所有值(key-value对)的个数,整型 |
||
key |
value类型标识 |
value |
key |
value类型标识 |
value |
…… |
fileInfo各项说明:
1. filInfo中所有值(key-value对)的个数,整型,四字节。
2. key值,保存fileInfo中值得key值。在HFile中的组成为
key长+key
其中key长以压缩的整型保存,整型类型包括(byte,short,int,long),如果该整数用i表示,详细说明如下:
1. 当-112 <= i <= 127 时,用一个字节保存实际值。
2. 其他情况下,第一个字节表示该整型与正负与该整数占字节长度,随后存储的是从该整数补码的高位算起的第一个非0字节的所有值。如果第一个字节为v,详细说明如下:
a) 当-120<=i<=-113时,表示该值为正数,该数所占字节为-(v+112)
b) 当-128<=i<=-121时,表示该值为负数,该数所占字节为-(v+120)
例如:
原始值 |
压缩后,以字节表示 |
说明 |
-87 |
{-87} |
第一种情况 |
127 |
{127} |
第一种情况 |
-1246 |
{-122} + {4, -35} |
第二种情况的b类型。{-122}表示该数为负数,并且所占字节长度为-(-122+120)=2字节。其中{4,-35}保存的是-1246的补码1245的第一个非0字节开始的所有字节。1245的16进制为0x04DD,非0字节共2个,第一个为0x04(4),第二个为0xDD(-35),组合一起为{-122, 4,-35} |
130 |
{-113} + {-126} |
第二种情况的a类型。{-113}表示该数为正数,并且所占字节长度为-(-113+112)=1字节。其中{-126}保存的是130的补码130的第一个非0字节开始的所有字节。130的16进制为0x04DD,非0字节共2个,第一个为0x04(4),第二个为0x82(-126),组合一起为{-113, -126} |
3. value值,保存fileInfo中值的value值。在HFile中的组成为
value长+value
其中value长以压缩的整型保存,压缩整型具体格式参考key值中关于压缩整型的说明。
1.4.数据块索引
数据块索引保存的是每一个数据块在HFile文件中的位置、大小信息以及每个块的第一个cell的key值。格式如下:
Index Block Magic {'I', 'D', 'X', 'B', 'L', 'K', 41, 43 } |
||
block offset |
block size |
block first key |
block offset |
block size |
block first key |
…… |
||
block offset |
block size |
block first key |
格式各项说明:
1. block offset 块在HFile中偏移,long(8字节)。
2. block size 块大小,int(4字节)。
3. block first key 块中第一个cell(key-value)值得key.该值的组成为(key的长(压缩整型表示)+key值)
1.5.元数据块索引
该数据块的格式与数据库索引相同,元数据块索引保存的是每一个元数据在HFile文件中的位置、大小信息以及每个元数据的key值。格式如下:
Index Block Magic {'I', 'D', 'X', 'B', 'L', 'K', 41, 43 } |
||
meta offset |
meta size |
meta name |
meta offset |
meta size |
meta name |
…… |
||
meta offset |
meta size |
meta name |
格式各项说明:
1. meta offset 元信息在HFile中偏移,long(8字节)。
2. meta size 元信息数据大小,int(4字节)。
3. meta name 元信息中的key值.该值的组成为(key的长(压缩整型表示)+key值)
1.6.文件尾
文件尾主要保存了该HFile的一些基本信息。格式比较简单,如下:
Trailer Block Magic {'T', 'R', 'A', 'B', 'L', 'K', 34, 36 } |
FileInfo Offset (long) |
Data Index Offset (long) |
Data Index Count (int) |
Meta Index Offset (long) |
Meta Index Count (int) |
Total Uncompressed Bytes (long) |
Entry Count (int) |
Compression Codec (int) |
Version (int) |
说明如下:
1. FileInfo Offset – FileInfo信息在HFile中的偏移。long(8字节)。
2. DataIndex Offset – 数据块索引在HFile中的偏移。long(8字节)。
3. DataIndex Count – 数据块索引的个数。int(4字节)。
4. MetaIndex Offset – 元数据索引块在HFile中的偏移。long(8字节)。
5. MetaIndex Count – 元数据索引块的个数。int(4字节)。
6. TotalUncompressedBytes – 未压缩的数据块部分的总大小。long(8字节)。
7. Entry Count – 数据块中所有cell(key-value)的个数。int(4字节)
8. Compression Codec – 压缩算法为enum类型,该值表示压缩算法代码。(LZO-0,GZ-1,NONE-2),int(4字节)
9. Version – 版本信息。当前该版本值为1. int(4字节)。
二、预写日志
原文:http://www.larsgeorge.com/2010/01/hbase-architecture-101-write-ahead-log.html
什么是预写日志WAL? 之前的文章我们简单介绍了HBase的存储结构。其中提到了预写日志。这里,我们要介绍它的实现细节,所有的描述都基于HBase 0.20.3.
WAL最重要的作用是灾难恢复。和MySQL 的BIN log类似,它记录所有的数据改动。一旦服务器崩溃,通过重放log,我们可以恢复崩溃之前的数据。这也意味如果写入WAL失败,整个操作将认为失败。
我们先看看HBase是如何做到的。首先,客户端初始化一个可能对数据改动的操作,如put(Put),delete(Delete) 和incrementColumnValue()。这些操作都将被封装在一个KeyValue对象实例中,通过RPC 调用发送给HRegionServer(最好是批量操作)。 一旦达到一定大小,HRegionServer 将其发送给HRegion。这个过程中,数据会首先会被写入WAL,之后将被写到实际存放数据的MemStore中。
当MemStore到达一定大小,或者经过一段时间后,数据将被异步地写入文件系统中。然而,在两次写入文件系统之间的数据,是保留在内存中的。如果这个时候系统崩溃,那数据···,别急,我们有WAL!
我们先来看看WAL几个重要的类。
HLog
HLog是实现WAL的类。一个HRegionServer对应一个HLog实例。当HRegion初始化时,HLog将作为一个参数传给HRegion的构造函数。
HLog最核心的是调用doWrite的append() 方法,前面提到的可能对数据改动的操作都就将首先调用这个方法。出于性能的考虑,put(), delete() 和incrementColumnValue()有一个开关函数setWriteToWAL(boolean) , 设为false将禁用WAL。这是为什么上图中向下的箭头是虚线的原因。默认时候当然需要WAL,但是假如你运行一个数据导入的MapReduce Job,你可以通过关闭WAL获得性能上的提升。
另一个重要的特性是HLog将通过“sequence number”追踪数据改变。它内部使用AtomicLong保证线程安全。sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。Region打开存储文件,读取每个HFile中的最大的sequence number,如果该值大于HLog 的sequence number, 就将它作为HLog 的sequence number的值。最后,HLog将得到上次存入文件和继续记log的点。过会,我们将看到它的应用。
上图表示了3个不同的region,每一个负责一段rowkey的范围。这些region将共享同一个HLog实例,我们可以看出,从不同region来的数据写入WAL的顺序是不确定的。在后面我们会再详细的介绍。
最后,Hlog利用HMaster恢复和切分一个由一个崩溃的HRegionServery遗留下来的Log。之后,重新部署regions。
HLogKey
WAL使用Hadoop的SequenceFile,它将记录存储为key/values 的数据集。对于WAL,key是一个HLogKey的实例。如前一篇文章中提到的, KeyValue不仅包括row,column family, qualifier, timestamp, value, 还包括“Key Type”—派上用场啦, 这里,可以用Key Type代表一个“put”或“delete”操作。
但是,哪里去存放KeyValue的归属信息,比如region或者表名呢?这些存放在HLogKey中。同时还包括 sequence number,和“写入时间”, 是一个记录数据何时写入到log的时间戳。
LogFlusher
前面提到,数据以KeyValue形式到达HRegionServer,将写入WAL,之后,写入一个SequenceFile。看过去没问题,但是因为数据流在写入文件系统时,经常会缓存以提高性能。这样,有些本以为在日志文件中的数据实际在内存中。这里,我们提供了一个LogFlusher的类。它调用HLog.optionalSync(),后者根据“hbase.regionserver.optionallogflushinterval”(默认是10秒),定期调用Hlog.sync()。另外,HLog.doWrite()也会根据“hbase.regionserver.flushlogentries”(默认100秒)定期调用Hlog.sync()。Sync() 本身调用HLog.Writer.sync(),它由SequenceFileLogWriter实现。
LogRoller
Log的大小通过$HBASE_HOME/conf/hbase-site.xml 的“hbase.regionserver.logroll.period”限制,默认是一个小时。所以每60分钟,会打开一个新的log文件。久而久之,会有一大堆的文件需要维护。首先,LogRoller调用HLog.rollWriter(),定时滚动日志,之后,利用HLog.cleanOldLogs()可以清除旧的日志。它首先取得存储文件中的最大的sequence number,之后检查是否存在一个log所有的条目的“sequence number”均低于这个值,如果存在,将删除这个log。
这里解释下你可能在log中看到的令人费解的内容:
2009-12-15 01:45:48,427 INFO org.apache.hadoop.hbase.regionserver.HLog: Too
many hlogs: logs=130, maxlogs=96; forcing flush of region with oldest edits:
foobar,1b2dc5f3b5d4,1260083783909
这里,我们看到,log file的数目超过了log files的最大值。这时,会强制调用flush out 以减少log的数目。
“hbase.regionserver.hlog.blocksize”和“hbase.regionserver.logroll.multiplier”两个参数默认将在log大小为SequenceFile(默认为64MB)的95%时回滚。所以,log的大小和log使用的时间都会导致回滚,以先到达哪个限定为准。
Replay
当HRegionServer启动,打开所管辖的region,它将检查是否存在剩余的log文件,如果存在,将调用Store.doReconstructionLog()。重放一个日志只是简单地读入一个日志,将日志中的条目加入到Memstore中。最后,flush操作将Memstore中数据flush到硬盘中。
旧日志往往由region server 崩溃所产生。当HMaster启动或者检测到region server 崩溃,它将日志文件拆分为多份文件,将其存储在region所属的文件夹。之后,根据上面提到的方法,将日志重放。需要指出的是,崩溃的服务器中的region只有在日志被拆分和拷贝之后才能被重新分配。拆分日志利用HLog.splitLog()。旧日志被读入主线程内存中,之后,利用线程池将其写入所有的region文件夹中,一个线程对应于一个region。
问题
1. 为什么要一个RegionServer 对应于一个HLog。为什么不是一个region对应于一个log file?
引用BigTable中的一段话,
如果我们每一个“tablet”(对应于HBase的region)都提交一个日志文件,会需要并发写入大量的文件到GFS,这样,根据每个GFS server所依赖的文件系统,写入不同的日志文件会造成大量的磁盘操作。
HBase依照这样的原则。在日志被回滚和安全删除之前,将会有大量的文件。如果改成一个region对应于一个文件,将会不好扩展,迟早会引发问题。
2. 潜在问题
1) 当server崩溃,HBase需要将其log切分成合适的片。然而,由于所有的条目混杂在日志中,HMaster只有在将log完全分配到每一个server后,才能将崩溃server中的region重新分配。这个时间可能很长。
2) 数据安全。你希望能保存你所有的数据,虽然你能将flush的时间调到尽可能的低,你依然依赖于上面提到的文件系统。那些用于存储数据依旧有可能没写到磁盘而发生数据丢失。
很明显,需要log来保证数据安全。最好是能让一个日志保持1个小时(或长)的打开状态。当数据来时,将新的key/value对写入SequenceFile中,并定期flush数据到磁盘中。但是Hadoop不是这样工作的。他提供了一个API,允许打开一个文件,写入大量的数据,然后马上关闭文件,成为一个对其他人只读的文件。只有当文件关闭时才是对其他人可读的。那么,如果一个进程在写入文件时僵死,那么,数据很可能会丢失。因此,我们需要一个功能,能取到一个离崩溃服务器写入数据尽可能近的点。
插曲: HDFS append,hflush,hsync,sync···
这些都起源于HADOOP-1700。Hadoop 0.19没有能解决这个问题。这个问题后来又成为HADOOP-4379或HDFS-200,并实现了syncFS(),后者可以同步文件的改变。同时,HBase-1470中,将这个API开放,但是依然没有解决这个问题。
之后是HDFS-265,重新提出append的方案,并引入Syncable的接口,开放hsync()和hflush()。
SequenceFile.Writer.sync()和上面的并不相同。 它只是将一个同步标记写入文件,以方便数据恢复。
虽然append对于HDFS很有用,但并没有用在HBase中。HBase用了hflush,它可以在log写完成后将所有数据写入磁盘。当服务器崩溃,我们可以安全地将“脏”文件读到最后一次改动。Hadoop 0.19.0中,利用Hadoop fsck /可以根据HBase打开的日志文件数目报告DFS的破损程度。
结论是,在Hadoop 0.21.0之前,你非常容易遇到数据丢失。在Hadoop 0.21.0之后,你将得到顶尖的系统(有点吹牛啦,译者注)。
改善计划
在HBase0.21.0中,WAL构架会有相当大的改进,这里强调几点
1. 替换SequenceFile
WAL的核心之一就是存储格式。SequenceFile有不少问题,最大的性能问题就是所有的写入都是同步的(HBase-2105)。
在HBase 0.20.0中,HFile替换了MapFile,所以,可以考虑一个完全的替换。首先要做的是HBase独立于底层的文件系统。HBase-2059使log的实现方式可配。
另一个想法是完全换一种序列化方式。HBase-2055提出利用Hadoop的Avro作为底层系统。Avro也可能成为Hadoop新的RPC格式,希望有越来越多的人熟悉它。
2. Append/Sync
频繁的调用hflush()会导致系统变慢。之前的测试发现,如果每一个记录都调用旧的syncFs(),将大大降低系统的性能。HBase-1939提出一种“组提交”的方法,可以批量flush数据。另外,HBase-1944中提出将“延时flush log”最为一个Column Family 的参数。设为“true”,将同步改动到log的工作交给新的LogSyncer类和线程。最后,在HBASE-2041中,将flushlogentries 设置为1, optinallogflushinterval 设为1000毫秒。.META.每次改变都会被同步,用户表可以根据需要类配置。
3.分布式日志切分
之前谈过,当region需要被重新分配时,切分日志将成为一个问题。一个方法就是在zookeeper中维护一个regions的列表。这个方法至少能保证所有“干净”的region能被快速地分配。剩下那些需要等待日志被切分的条目。
剩下的问题就是如何更快地切分日志。这里,BigTable有如下的描述:
一种方案是对于每一个新的tablet server(类似regionserver),需要读取整个日志文件,然后,分配那些需要被修复的条目。然而,在这种情况下,如果将一个失败的tablet server重新分配到100台机器,那么每个日志文件需要读100次。
改进方案
我们根据key对log条目进行排序。之后,所有对一个特定tablet的改动都将是连续的,顺序读可以有效地提高磁盘检索的效率。为了并行地排序,我们将日志切分为64MB的分片。每个排序进程都将有Master协调管理,并在tablet server给出它将从那些日志文件中恢复时初始化。
这就是他的原理。在HMaster rewrite (HBASE-1816)中,也提到了日志拆分。HBASE-1364中,将日志拆分打包成一个问题。但我觉得会引入更多的需要讨论的细节。
三、日志回放
WAL负责保存各种操作日志,以便在服务器出现错误的时候进行恢复,这个恢复过程
称为日志回放。
1.共用日志文件
每个Regionserver上的所有修改操作都写人同一个基于HLo9的日志文件内。原因是
如果为每个Region保存一个单独的日志,那么在文件系统中将会有大量的文件被并发地写
人。由于依赖于物理机器底层的文件系统,这些写人会因为需要写人不同的物理日志文件产
生大量的磁盘寻道操作,而造成效率和可扩展性低下。
HBase因为相同的原因而采取了类似策略同时写人太多文件,再加上日志的切换,这
会降低可扩展性。所以说这个设计决定源自底层文件系统。尽管可以替换HBase所依赖的文
件系统,但是最常见的配置采用的都是nDFs.
采用这个日志存储方式,当服务器宕机时,切分成合适的片段对于恢复状态是高效的。
因为上面提到的日志存储方式,将所有的修改操作掺杂在一起,也没有什么索引支持,这样
Master必须等待该宕机服务器的所有日志都分离完毕后才能重新部署其上面的Region。如果
日志的数量非常多,中间需要等待的时间可能会很长。.
2.日志切分
日志回放中的情况一般发生在HBase集群重启或者服务器出现宕机的情况下,这时
Master会检查文件系统上的根目录中的.logs文件是否存在日志文件。这些文件中的内容都 ,
有可能未被持久化,所以需要回放这些日志文件中的内容。日但是在这些文件中包含各种操
作,日志回放并不关心除更新(Put、Delete和Increment)之外的其他操作,这些更新操作在
回访之前,需要把他们按照,Region 剥离出来,这就是日志切分的过程。读取日志然后按照每 条记录所属的Region分组,这些分好组的更新操作将会保存在目标Region附近的一个文件
中,用于后续的恢复。
日志切分的实现在几乎每个HBase版本中都有些不同早期版本通过Master上的单个
进程读取文件。后来对它进行了优化改成了多线程的。在早期HBase 0.92.0版本中,引入了
分布式日志splitting的概念,将实际的工作从Master转移到了所有的Region Server中。
现有版本HBase使用ZooKeeper来将每个被抛弃的日志文件分配给一个Regionserver.
同时通过ZooKeeper进行工作分配,如果Master确定某个日志文件已经准备就绪,这些 Regionserver会进行竞争性选举来处理这个文件
最终一个Regionserver会成功,然后开始 通过单个线程(避免导致Regionserver过载)读取和切分该日志文件。
split过程首先会将修改操作写人HBase根文件夹下的splitlog目录中。
drwxr-xr-x hadoop supergroup 0 2013-09-16 18 20
/hbaselsplitlog/hbase-regionserver-3, 60020, 137 6015661467 hdf s%3A%2F%2Fhbase-
mas ter%3A9000%2 Fhbase%2F. logs %2FJN-pEGASUS-115%2C60020%2c137664 0353958-
spli 上上 ing%2 FJN-PEGASUS-115%252C60020%2 52C1376640353958. 137 9325727980
如上述代码所示,为丁与其他日志文件的切分输出进行区分,该路径已经包含了日志文
件名,同时也包含了表名称、Region名称(哈希值)以及recove·ed.edits目录。最后,切分
文件的名称就是针对相应Region的第一个更新操作的序列号。
.corrupt目录包含那些无法被解析的日志文件,受hbase.hlog.split.skip.errors属性影响,
如果设为true,意味着当无法从日志文件中读出任何修改操作时,会将该文件移入.corrupt
目录。如果设为false,那么此时会抛出一个IOException,同时停止整个的日志切分过程。
旦日志被成功切分后,每个Region对应的文件就会被移入实际的Region目录中。
对于该Region来说它的恢复工作现在才就绪。这也是为什么切分必须要拦截那些受影响
Region的打开操作的原因,因为它必须将那些pending的修改操作进行回放。
3、回放流程
对比日志切分过程,日志回放的操作相对简单一些,下面是整个日志回放的流程
1)HRegionserver启动,初始化Regionserver相关的属性
2)打开所管理的Region、俭查是否存在到余的日志文件。如果存在,调用Store. doReconstructionLog
3)读人一个日志,将日志中的条目加入Nlemstore中,如果存在多个文件,则重复多次 操作。
4)将Memstore中数据flush到硬盘中。
4、日志一致性
HBase的底层存储使用HDFS,当用户根据需要调低日志flush的时间或者每次修改操作
都进行sync后,数据会持久化到HDFS.但是日志流数据的持久化过程具体如何实现,是否
存在数据丢失的问题,这还需要进一步讨论。
比较明确的一点是,系统通过日志来保证数据安全。一个日志文件最好能长时间处于
打开状态。当数据到达时,一个新的Keyvalue对会被写人SequenceFile中,同时间或地被
flush到磁盘。但是Hadoop并不是这样工作的,它之前提供的API,通常都是打开一个文件,
写人大量数据,立即关闭,然后产生一个可供其他所有人读取的不可变文件。只有当文件关
闭之后,对其他人来说它才是可见的和可读的。如果在写人数据到文件的过程中进程死掉,
通常都会丢失数据。为了能够让日志的读取可以读到服务器crash时刻最后写人的那个位置,
或者尽可能接近该位置,这就需要一个feature:append支持。
HBase目前会检测底层的Hadoop库是否支持syncFs()或者hflush()。如果在log writer
中触发一个sync()调用,它就会调用syncFs()或者hflush()中的一个方法,或者不调用任何 方法,如果Hbase工作在
一个 non-durable setup 上的话。Sync()将会使用流水式的write过程来保证日志文件中修改操作的持久性。当服务器crash时,系统就能安全的读取被抛弃的日志文件更新到最后的修改操作。
5、HBase——强一致性详解
Hbase是一个强一致性数据库,不是“最终一致性”数据库,官网给出的介绍:
“Strongly consistent reads/writes: HBase is not an "eventually consistent" DataStore. This makes it very suitable for tasks such as high-speed counter aggregation.”
这里要先提一下分布式系统的CAP原理:
Consistency(一致性), 数据一致更新,所有数据变动都是同步的
Availability(可用性), 好的响应性能
Partition tolerance(分区容错性) 可靠性
定理:任何分布式系统只可同时满足二点,没法三者兼顾。
忠告:架构师不要将精力浪费在如何设计能满足三者的完美分布式系统,而是应该进行取舍。
1.首先来理解下一致性
对于一致性,可以分为从客户端和服务端两个不同的视角。
从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景。
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性
从服务端角度,如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面。对于分布式数据系统:
-
N — 数据复制的份数
-
W — 更新数据时需要保证写完成的节点数
-
R — 读取数据的时候需要读取的节点数
如果W+R>N,写的节点和读的节点重叠,则是强一致性。例如对于典型的一主一备同步复制的关系型数据库,N=2,W=2,R=1,则不管读的是主库还是备库的数据,都是一致的。
如果W+R<=N,则是弱一致性。例如对于一主一备异步复制的关系型数据库,N=2,W=1,R=1,则如果读的是备库,就可能无法读取主库已经更新过的数据,所以是弱一致性。
对于分布式系统,为了保证高可用性,一般设置N>=3。不同的N,W,R组合,是在可用性和一致性之间取一个平衡,以适应不同的应用场景。
-
如果N=W,R=1,任何一个写节点失效,都会导致写失败,因此可用性会降低,但是由于数据分布的N个节点是同步写入的,因此可以保证强一致性。
-
如果N=R,W=1,只需要一个节点写入成功即可,写性能和可用性都比较高。但是读取其他节点的进程可能不能获取更新后的数据,因此是弱一致性。这种情况下,如果W<(N+1)/2,并且写入的节点不重叠的话,则会存在写冲突
2.HBase是强一致性系统
Hbase具有以下特点
-
每个值只出现在一个REGION
-
同一时间一个Region只分配给一个Region服务器
-
行内的mutation操作都是原子的(原子性操作是指:如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行)。
-
put操作要么成功,要么完全失败。
联系上文提到的一致性特点,可以得出HBase是强一致性系统的结论。
当某台region server fail的时候,它管理的region failover到其他region server时,需要根据WAL log(Write-Ahead Logging)来redo(redolog,有一种日志文件叫做重做日志文件),这时候进行redo的region应该是unavailable的,所以hbase降低了可用性,提高了一致性。设想一下,如果redo的region能够响应请求,那么可用性提高了,则必然返回不一致的数据(因为redo可能还没完成),那么hbase就降低一致性来提高可用性了。
3.HBase的强一致性和HDFS的多副本
一开始非常迷惑于HBase的强一致性和HDFS的多副本是怎么协同的。
这一块儿就需要对HBase和HDFS的读写数据流有个比较透彻的理解。
先假设HDFS的副本存储策略,也就是dfs.replication的值为3(默认值就是3)
这样所有存储在HDFS上的文件都有3个副本。那么,HBase的存储实例,也就是HFile也有3个副本。那么当某一个RegionServer崩溃时,并不用担心数据的丢失,因为数据是存储在HDFS上,哪怕崩溃的RegionServer所在的DataNode上有一个副本,在其他DataNode上也还有2个副本。
那么也许你要问,既然有3个副本,如何保证HBase的强一致性呢?
HFile是已经持久化在磁盘上了,而HFile是不能改变的(这个时候暂时把删除数据这个操作放到一边,相关内容请看下面的Note),一旦在某一个DataNode上生成一个HFile后就会异步更新到其他两个DataNode上,这3个HFile是一模一样的。
那也许你又要问,那我的数据是不断更新当中啊!
更新的数据是放在Memstore,只有当Memstore里的数据达到阈值,或者时间达到阈值,就会flush到磁盘上,生成HFile,而一旦生成HFile就是不可改变的(compaction,split就是后话啦)。
这里再提一下WAL的一致性
WAL是Write-Ahead logging,这个是Memstore里的数据在RegionServer崩溃时得以恢复的保证。WAL的实现是HLog,HLog也是存储在HDFS上的,所以HRegionServer崩溃了也不会导致HLog的丢失,它也有备份。
每一次更新都会调用写日志的sync()方法,这个调用强迫写入日志的更新都会被文件系统确认。
当前的sync()的实现是管道写,也就是HDFS写数据、生成副本的默认方式,这意味着当修改被写入时,它会被发送到第一个DataNode进行存储。一旦成功,第一个DataNode就会把修改发送到另一个DataNode来进行相同的工作。只有3个DataNode都已经确认了写操作,客户端才被允许继续进行; 另一种存储修改的方法是多路写,也就是写入被同时送到3台机器上。当所有主机确认了写操作后,客户端才可以继续。 两种方法的优缺点: 管道写需要时间去完成,所以它有很高的延迟,但是它能更好地利用网络带宽;多路写有着比较低的延迟,因为客户端只需要等待最慢的DataNode确认(假设其余都已成功确认)。但是写入需要共享发送服务器的网络带宽,这对于有着很高负载的系统来说是一个瓶颈。 目前有正在进行的工作能让HDFS支持上面两种方式。 |
Note:当客户端提交删除操作的时候,数据不是真正的删除,只是做了一个删除标记(delete marker,又称母被标记),表明给定航已经被伤处了,在检索过程中,这些删除标记掩盖了实际值,客户端读不到实际值。直到发生compaction的时候数据才会真正被删除。
参考文献
【1】http://kabike.iteye.com/blog/2168852?utm_source=tuicool
【2】http://www.blogjava.net/hello-yun/archive/2012/04/27/376744.html
【3】《HBase权威指南》Lars George著
四、HBase中的备份和故障恢复方法
本文将对Apache Hbase可用的数据备份机制和大量数据的故障恢复/容灾机制做简要介绍。
随着HBase在重要的商业系统中应用的大量增加,许多企业需要通过对它们的HBase集群建立健壮的备份和故障恢复(backup and disaster recovery, BDR)机制来保证它们的企业(数据)资产。HBase和Apache Hadoop系统提供了许多内置的机制,可以快速而轻松的完成PB级数据的备份和恢复工作。
在这篇文章中,你将会对在HBase中可用的数据备份机制有一个高层次的简要了解,并且知道多种数据恢复/容灾机制。在阅读了这篇文章之后,你应该能对你的业务需要那种BDR策略有了自己的判断。你也应该明白各种机制各自的优缺点(适用于CDH 4.3.0/HBase 0.94.6及更高版本)。
备份
HBase是一个基于LSM树(log-structured merge-tree)的分布式数据存储系统,它使用复杂的内部机制确保数据准确性、一致性、多版本等。因此,你如何获取数十个region server在HDFS和内存中的存储的众多HFile文件、WALs(Write-Ahead-Logs)的一致的数据备份?
让我们从最小的破坏性,最小的数据占用空间,最小的性能要求机制和工作方式到最具破坏性的逐一讲述:
-
Snapshots
-
Replication
-
Export
-
CopyTable
-
HTable API
-
Offline backup of HDFS data
下面的表格提供了一个关于这些方法的快速比较,具体的细节在下面再详细描述。
Snapshots(快照)
HBase快照功能丰富,有很多特征,并且创建时不需要关闭集群。关于snapshot在文章《apache hbase snapshot介绍》中有更详细的介绍。
快照能通过在HDFS中创建一个和unix硬链接相同的存储文件,简单捕捉你的hbase表的某一时刻的信息(图1)。这些快照在几秒内就可以完成,几乎对整个集群没有任何性能影响。并且,它只占用一个微不足道的空间。除了在metadata文件中存储的极少目录数据,你的数据不会冗余,快照允许你的系统回滚到(创建快照)那个时刻,当然,你需要恢复快照。
图1
通过在HBase shell中运行如下命令来创建一个表的快照:
[plain] view plain copy print?
-
hbase(main):001:0> snapshot 'myTable', 'MySnapShot'
在执行这条命令之后,你将发现在hdfs中有一些小的数据文件。在 /hbase/.snapshot/myTable (CDH4) 或者hbase/.hbase-snapshots (Apache 0.94.6.1),这些文件中存储着快照信息。想要恢复数据只需要执行在shell中执行如下命令:
[plain] view plain copy print?
-
hbase(main):002:0> disable 'myTable'
-
hbase(main):003:0> restore_snapshot 'MySnapShot'
-
hbase(main):004:0> enable 'myTable'
正如你看到的,恢复快照需要对表进行离线操作。一旦恢复快照,那任何在快照时刻之后做的增加/更新数据都会丢失。如果你的业务需求是这样的:你必须有数据的异地备份,你可以用exportSnapshot命令赋值一个表的数据到你的本地HDFS或者你选择的远程HDFS中。
快照是你的表在某一个时刻的完整图像,目前没有增量快照功能可用。
HBase复制(HBase Relication)
HBase赋值是另外一个负载较轻的备份工具。文章《HBase复制概述》有对它的详细描述。总的来说,赋值被定义为列簇级别,可以工作在后台并且保证所有的编辑操作在集群复制链之间的同步。
复制有三种模式:主->从(master->slave),主<->主(master<->master)和循环(cyclic)。这种方法给你灵活的从任意数据中心获取数据并且确保它能获得在其他数据中心的所有副本。在一个数据中心发生灾难性故障的情况下,客户端应用程序可以利用DNS工具,重定向到另外一个备用位置。
复制是一个强大的,容错的过程。它提供了“最终一致性”,意味着在任何时刻,最近对一个表的编辑可能无法应用到该表的所有副本,但是最终能够确保一致。
注:对于一个存在的表,你需要通过本文描述的其他方法,手工的拷贝源表到目的表。复制仅仅在你启动它之后才对新的写/编辑操作有效。
表2 集群复制架构图
导出(Export)
HBase的导出工具是一个内置的实用功能,它使数据很容易从hbase表导入HDFS目录下的SequenceFiles文件。它创造了一个map reduce任务,通过一系列HBase API来调用集群,获取指定表格的每一行数据,并且将数据写入指定的HDFS目录中。这个工具对集群来讲是性能密集的,因为它使用了mapreduce和HBase 客户端API。但是它的功能丰富,支持制定版本或日期范围,支持数据的筛选,从而使增量备份可用。
下面是一个导出命令的简单例子:
[plain] view plain copy print?
-
hbase org.apache.hadoop.hbase.mapreduce.Export <tablename> <outputdir>
一旦你的表导出了,你就可以复制生成的数据文件到你想存储的任何地方(比如异地/离线集群存储)。你可以执行一个远程的HDFS集群/目录作为命令的输出目录参数,这样数据将会直接被导出到远程集群。使用这个方法需要网络,所以你应该确保到远程集群的网络连接是否可靠以及快速。
拷贝表(CopyTable)
拷贝表功能在文章《使用CopyTable在线备份HBase》中有详细描述,但是这里做了基本的总结。和导出功能类似,拷贝表也使用HBase API创建了一个mapreduce任务,以便从源表读取数据。不同的地方是拷贝表的输出是hbase中的另一个表,这个表可以在本地集群,也可以在远程集群。
一个简单的例子如下:
[plain] view plain copy print?
-
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --new.name=testCopy test
这个命令将会拷贝名为test的表到集群中的另外一个表testCopy。
请注意,这里有一个明显的性能开销,它使用独立的“puts”操作来逐行的写入数据到目的表。如果你的表非常大,拷贝表将会导致目标region server上的memstore被填满,会引起flush操作并最终导致合并操作的产生,会有垃圾收集操作等等。
此外,你必须考虑到在HBase上运行mapreduce任务所带来的性能影响。对于大型的数据集,这种方法的效果可能不太理想。
HBase API(比如作为一个Java应用)
由于总是这样使用hadoop,你可以使用公用的api写自己定制的客户端应用程序来直接查询表格。你也可以通过mapreduce任务的批量处理优势,或者自己设计的其他手段。然而,这个方法需要对hadoop开发以及因此对生产集群带来的影响有深入的理解。
离线备份原生的HDFS数据(Offline Backup of Raw HDFS Data)
最强力的备份机制,也是破坏性最大的一个。涉及到最大的数据占用空间。你可以干净的关闭你的HBase集群并且手工的在HDFS上拷贝数据。因为HBase已经关闭,所以能确保所有的数据已经被持久化到HDFS上的HFile文件中,你也将能获得一个最准确的数据副本。但是,增量的数据几乎不能再获得,你将无法确定哪些数据发生了变化。
同时也需要注意,恢复你的数据将需要一个离线的元数据因为.META.表将包含在修复时可能无效的信息。这种方法需要一个快速的,可信赖的网络来传输异地的数据,如果需要在稍后恢复它的话。
由于这些原因,Cloudera非常不鼓励在HBase中这种备份方法。
故障恢复(Disaster Recory)
HBase被设计为一个非常能容忍错误的分布式系统,假设硬件失败很频繁。在HBase中的故障恢复通常有以下几种形式:
-
在数据中心级别的灾难性故障,需要切换到备份位置;
-
需要恢复由于用户错误或者意外删除的数据的之前一个拷贝;
-
出于审计目的,恢复实时点数据拷贝的能力
正如其他的故障恢复计划,业务需要驱动这你如何架构并且投入多少金钱。一旦你确定了你将要选择的备份方案,恢复将有以下几种类型:
-
故障转移到备份集群
-
导入表/恢复快照
-
指向HBase在备份位置的根目录
如果你的备份策略是这样的,你复制你的HBase数据在不同数据中心的备份集群,故障转移将变得简单,仅需要使用DNS技术,转移你的应用程序。
请记住,如果你打算允许数据在停运时写入你的备份集群,那你需要确保在停运结束后,数据可以回到主机群。主<->主或循环的复制架构能自动处理这个过程,但对于一个主从结构来讲,你就需要手动进行干预了。
你也可以在故障时通过简单的修改hbase-site.xml的 hbase.root.dir属性来更改hbase根目录,但是这是最不理想的还原选项,因为你复制完数据返回生产集群时,正如之前提到的,可能会发现.META是不同步的。
总结
综上所述,从某种损失或中断中恢复数据需要一个精心设计的BDR计划。强烈建议你彻底明白你的业务需求,然后明白数据精确度/可用性以及故障恢复的最大时间。有了这些知识,你才能更好的选择满足这些需求的工具。
选择工具仅仅是个开始,你应该对你的BDR策略进行大规模测试,以确保它的在你的基础设施下的功能。并且,你应该是非常熟悉所有的故障恢复步骤。
(翻译的不是很好,请如有错误之处,请见谅。)
原文地址:http://blog.cloudera.com/blog/2013/11/approaches-to-backup-and-disaster-recovery-in-hbase/
转载请注明出处:http://blog.csdn.net/iAm333
五、HBase中的数据压缩
1、压缩算法的比较
以下是Google几年前发布的一组测试数据(数据有些老了,有人近期做过测试的话希望能共享出来):
Algorithm | % remaining | Encoding | Decoding |
GZIP | 13.4% | 21 MB/s | 118 MB/s |
LZO | 20.5% | 135 MB/s | 410 MB/s |
Zippy/Snappy | 22.2% | 172 MB/s | 409 MB/s |
注:来自《HBase: The Definitive Guide》
其中:
1)GZIP的压缩率最高,但是其实CPU密集型的,对CPU的消耗比其他算法要多,压缩和解压速度也慢;
2)LZO的压缩率居中,比GZIP要低一些,但是压缩和解压速度明显要比GZIP快很多,其中解压速度快的更多;
3)Zippy/Snappy的压缩率最低,而压缩和解压速度要稍微比LZO要快一些。
BigTable和HBase中压缩算法的选择
BigTable中采用的是Zippy算法,目标是达到尽可能快的压缩和解压速度,同时减少对CPU的消耗。
HBase中,在Snappy发布之前(Google 2011年对外发布Snappy),采用的LZO算法,目标和BigTable类似;在Snappy发布之后,建议采用Snappy算法(参考《HBase: The Definitive Guide》),具体可以根据实际情况对LZO和Snappy做过更详细的对比测试后再做选择。
实际项目中的实践经验
项目中使用clearspring公司开源的基数估计的概率算法:stream-lib,用于解决去重计算问题,如UV计算等,它的特点在于:
1)一个UV的计算,可以限制在一个固定大小的位图空间内完成(不同大小,对应不同的误差率),如8K,64K;
2)不同的位图可以进行合并操作,得到合并后的UV。
当系统中维护的位图越多的时候,不管是在内存中,还是在存储系统(MySQL、HBase等)中,都会占用相当大的存储空间。因此,需要考虑采取合适的算法来压缩位图。这里分为以下两类情况:
1)当位图在内存中时,此时压缩算法的选择,必须有尽可能快的压缩和解压速度,同时不能消耗过多CPU资源,因此,适合使用LZO或Snappy这样的压缩算法,做到快速的压缩和解压;
2)当位图存储到DB中时,更关注的是存储空间的节省,要有尽可能高的压缩率,因此,适合使用GZIP这样的压缩算法,同时在从内存Dump到DB的过程也可以减少网络IO的传输开销。
总结的话
以上是对GZIP、LZO、Zippy/Snappy压缩算法特点的概括比较,以及一些实践上的方法。如有不对之处,欢迎大家指正,讨论。
原贴: 网址: http://www.cnblogs.com/panfeng412/archive/2012/12/24/applications-scenario-summary-of-compression-algorithms.html 作者: 大圆那些事
六、关于几种压缩算法以及hadoop和hbase中的压缩配置说明
Hadoop中常用的压缩算法有bzip2、gzip、lzo、snappy,其中lzo、snappy需要操作系统安装native库才可以支持
下面这张表,是比较官方一点的统计,不同的场合用不同的压缩算法。bzip2和GZIP是比较消耗CPU的,压缩比最高,GZIP不能被分块并行的处理;Snappy和LZO差不多,稍微胜出一点,cpu消耗的比GZIP少。
通常情况下,想在CPU和IO之间取得平衡的话,用Snappy和lzo比较常见一些。
Comparison between compression algorithms
Algorithm | % remaining | Encoding | Decoding |
---|---|---|---|
GZIP | 13.4% | 21 MB/s | 118 MB/s |
LZO | 20.5% | 135 MB/s | 410 MB/s |
Snappy | 22.2% | 172 MB/s | 409 MB/s |
对于数据格式为TextFile,Sequence,以及其他用户自定义的文件格式的文件,都可以采用以上的压缩算法进行压缩;
TextFile在压缩以后,不能split,压缩出来的数据当做job的输入是一个文件作为一个map。SequenceFile本身是分块的,加上lzo的压缩格式,文件可以实现lzo方式的split操作,可以按照record、block进行压缩,一般采用block效率更高一些。
一、hadoop(Hive)对mapreduce压缩参数设置
1、mapreduce的中间结果对压缩的支持
方法一:
hadoop 中 mapred-site.xml
<property>
<name>mapred.compress.map.output</name>
<value>true</value>
</property>
<property>
<name>mapred.map.output.compression.codec</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
方法二
hive中hive-site.xml
<property>
<name>hive.exec.compress.intermediate</name>
<value>true</value>
<description>Should the outputs of the maps be compressed before being
sent across the network. Uses SequenceFile compression.
</description>
</property>
<property>
<name>hive.intermediate.compression.codec</name>
<value>org.apache.hadoop.io.compress.LzoCodec</value>
<description>If the map outputs are compressed, how should they be
compressed?
</description>
</property>
方法三
hive中shell
set hive.exec.compress.intermediate=true;
set hive.intermediate.compression.codec="org.apache.hadoop.io.compress.LzoCodec";
2、mapreduce的输出结果对压缩的支持
hive-site.xml中配置:
<property>
<name>hive.exec.compress.output</name>
<value>true</value>
<description>Should the job outputs be compressed?
</description>
</property>
<property>
<name>mapred.output.compression.codec</name>
<value>org.apache.hadoop.io.compress.LzoCodec</value>
<description>If the job outputs are compressed, how should they be compressed?
</description>
</property>
或者在hadoop-site.xml中添加:
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.DefaultCodec,org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.BZip2Codec,org.apache.hadoop.io.compress.LzoCodec</value>
<description>A list of the compression codec classes that can be used
for compression/decompression.</description>
</property>
<property>
<name>mapred.output.compress</name>
<value>true</value>
<description>Should the job outputs be compressed?
</description>
</property>
<property>
<name>mapred.output.compression.codec</name>
<value>org.apache.hadoop.io.compress.LzoCodec</value>
<description>If the job outputs are compressed, how should they be compressed?
</description>
</property>
二、Hbase对这三种压缩格式的支持
HBase中可以对HFile进行gzip、lzo、snappy方式的压缩存储。
1、对于gzip压缩的支持
hbase(main):001:0> create 'testtable', { NAME => 'colfam1',
COMPRESSION => 'GZ' }
或者alter 'testtable',不过要先disable table,完成压缩后,再enable table
2、对于lzo的支持,需要系统安装lzo动态库,以及hadoop lzo相关的native库,后把native库jar文件copy到hadoop/lib/native 及 hbase/lib/native中
同时在core-site.xml中,配置lzo压缩
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.GzipCodec,org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec
</value>
</property>
<property>
<name>io.compression.codec.lzo.class</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
org.apache.hadoop.io.compress.DefaultCodec是hadoop默认的zlib压缩
hbase(main):001:0> create 'testtable', { NAME => 'colfam1',
COMPRESSION => 'lzo' }
3、 对于synappy的支持,需要安装snappy,并且 将 hadoop-snappy-0.0.1-SNAPSHOT.tar.gz 的native中的动态静态链接库文件拷到hadoop以及hbase lib的native下面,将hadoop-snappy-0.0.1-SNAPSHOT.jar考到hadoop 以及hbase 的lib下
在core-site.xml中,配置lzo压缩
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.SnappyCodec
</value>
</property>
hbase(main):001:0> create 'testtable', { NAME => 'colfam1',
COMPRESSION => 'synappy' }
来源:http://blog.csdn.net/yangbutao/article/details/8474731
hbase测试压缩效果报告:http://blog.csdn.net/chenyi8888/article/details/16841119