HBase 0.94版本新特性、性能优化详解及使用(一)之数据压缩(DataBlock compression,HLog compression)

众所周知,HBase 0.94对性能做了很多优化,记录一下个人对其实现细节及如何更好应用的理解。


0.94引入了两个在HBase层的数据压缩:

一.DataBlock compression

1.1 作用
DataBlock compression指的是对HFile v2中的Data Block进行压缩,Data Block既存储在Disk上(HDFS中),也会存在于LRU Cache中,所以此压缩特性支持:
a)Disk、Cache上都不开启
b)Disk、Cache上都开启
c)Cache上开启,Disk上开启
但不支持
d)Cache上不开启,Disk开启

由此可见,此压缩特性的作用是:
a)节省LRU Cache的空间使用,提升cache命中率,优化读性能
b)节省Disk空间使用

当然,前者是主要目的,后者我们有对HFile的全局压缩算法,如LZO

1.2 副作用
目前,DataBlock压缩特性默认是关闭的,显然,其能优化某些业务场景,但盲然使用肯定会带来副作用:
a)Data Block解压缩,(如果一个Block(默认64KB)中包含很多KV,这个开销会比较大)
b)某些场景,压缩后数据更大

1.3 压缩算法
a)PREFIX
向Block中写入KV时,和前一条KV比较Key(这里的Key=Row+Family+Qualifier+Timestamp+Type,具体可见附件),对于相同的前缀byte, 则不再重复写入
根据KeyValue结构(见附件),某KV具体压缩后的内容:
*KeyLength压缩为1~5个字节(使用7-bit encoding,见附注)
*ValueLength压缩为1~5个字节
*和上个KV比较Key的内容,写入相同的前缀byte的长度,1~5个字节
*除相同前缀部分,剩余的Key内容
*Value内容

在最坏情况下,一条KV经过此算法压缩后会大小会增加3个字节

b)DIFF
向Block中写入KV时,会和前一条KV进行全面比较(如Key长度,value长度,Row,timestamp,type),对于相同的信息则不再写入,同时KV中的family内容只写一次(因为同个Block中,他们的family肯定是相同的)
某KV具体压缩后的内容:
*一个字节的flag(这个flag的作用后面解释)
*如果和上个KV的Key长度不一样,则写入1~5个字节的KeyLength
*如果和上个KV的Value长度不一样,则写入1~5个字节的KeyLength
*和上个KV比较Key的内容,写入相同的前缀byte的长度,1~5个字节(此比较不包含timestamp和type,而PREFIX算法中是包含的)
*剩余的Row内容(如果相同前缀比较少)
*如果是第一条KV,写入family内容
*剩余的qualifier内容
*写入1~8字节的timestamp或者与上个KV的timestamp的差(是原值还是写与上个KV的差,取决于哪个字节更小)
*如果和上个KV的type不一样,则写入1字节的type
*Value内容

在解压缩时,怎么判断和上个KV的Key长度是否一样,Value长度是否一样,写入的timestamp是原值还是差呢等等,这些都是通过最早写入的1个字节的flag来实现的,
这个字节中的8位bit,含义是:
第0位,如果为1,Key长度一样
第1位,如果为1,Value长度一样
第2位,如果为1,type一样
第3位,如果为1,则写入的timestamp是差值
第4,5,6位,这3位组合起来的值(能表示0~7),表示写入的timestamp或差值的字节数
第7位,如果为1,表示写入的timestamp或差值取反了(比如timestamp差值为负数,为了节省空间,会写入绝对值)

由此可见,DIFF算法是针对KeyValue的结构而量身设计的

c)FAST_DIFF
FAST_DIFF与DIFF的原理一样,只是减小了压缩量,提升了压缩速度,
某KV具体压缩后的内容:
*一个字节的flag
*如果和上个KV的Key长度不一样,则写入1~5个字节的KeyLength
*如果和上个KV的Value长度不一样,则写入1~5个字节的KeyLength
*和上个KV比较Key的内容,写入相同的前缀byte的长度,1~5个字节(此比较不包含timestamp和type,而PREFIX算法中是包含的)
*剩余的Row内容(如果相同前缀比较少)
*如果是第一条KV,写入family内容
*剩余的qualifier内容
*写入1~8字节的timestamp
*如果和上个KV的type不一样,则写入1字节的type
*如果和上个KV的Value内容不一样,则写入Value内容

1个字节的flag的8位bit含义:
第0,1,2位,这3位组合起来的值(能表示0~7),表示写入的timestamp的字节数
第3位,如果为1,Key长度一样
第4位,如果为1,Value长度一样
第5位,如果为1,type一样
第6位,如果为1,Value内容一样
第7位,闲置...

由此可见,相比DIFF,FAST_DIFF只是减少了对timestamp的压缩,但是增加了Value内容是否一样的判断,在某些情景(如value只是用来计数,或者value是一种类型选项值),那么可以大幅减少相同value内容的重复写入

1.4 应用场景及如何配置
上面介绍了3种压缩算法的原理,由此可简单分析下他们的应用场景:
1)PREFIX,这是一个比较公用的压缩算法,其压缩过程也比较简单快速,通用于存在相同的Row前缀的情景

2)DIFF, 相比于PREFIX,同样是需要应用在存在相同的Row前缀的情景,但其只写一份family内容,写入timestamp的差值,比较长度是否一样等特性,使其压缩幅度会更大,当然压缩的CPU开销也会稍大

3)FAST_DIFF,和DIFF类似,但是对于存在相同Value内容的场景,那肯定是使用它了

空口无凭,实验为据,具体场景,数据为准

如何配置?
Data Block的压缩算法选择是表中的一个列族属性,你可以通过api HColumnDescriptor#setDataBlockEncoding来开启或关闭或更改压缩算法,另外在开启压缩时,你可以通过HColumnDescriptor#setEncodeOnDisk决定是否在写入Disk时也采用压缩


二.HLog compression

2.1 为什么使用HLog压缩
为了保证数据安全性,每put一次,都会写入一条hlog记录,每一条hlog记录都会包含:table name、region name、row、family、qualifier等,如果数据写入集中在某表、某region乃至某row、某family,那么HLog的文件中就会存在大量重复的内容,压缩由此而生

2.2实现原理
HLog压缩采用的是词典压缩法,其写入一条log记录时的压缩行为:
*查找table是否在table词典中,如果不是,该table name写入到hlog 文件中,并加入到table词典中,对应一个short值;如果是,则hlog文件中写入一个short值,以代表该table name
*region family qualifier row,也是按照上述方法写入到hlog文件中
所以,写一个hlog文件时,会维护5个词典,即table词典、region词典、family词典、qualifier词典、row词典


2.3 作用
压缩的作用自然是减小hlog记录的写入大小,加快写hlog的速度,提升写性能;但是,个人认为,单条hlog记录压缩后减少几十或上百字节,对于其本身就不大而言,并不能提高多少HBase的写数据速度。
不过,其还存在着一个侧面作用,增加单个hlog文件的数据记录量,这会一定程度上提高memstore的内存使用率,从而提升全局的flush效率、compaction效率。

2.4如何配置及应用场景
Server端修改配置hbase.regionserver.wal.enablecompression为true(默认false)开启HLog压缩特性

应用场景:
*数据写入比较分散在各个表,各个region的,就不要考虑了
*数据写入比较集中的可以考虑开启,但测试数据为准,如果写性能没啥优化,那还是别尝这个鲜了,新特性免不了会有风险


以上只是个人见解,欢迎指正

附注:
7-bit 压缩法,针对Integer数据的一种压缩算法,可以将4个字节的Int型数据压缩为1~5个字节,数值越小,压缩后的字节数越少,所以对值小的Int型数据会有比较好的压缩效果,其具体压缩原理,简单介绍如下:
1.将一个Int型数据转换成32位;
2.0~7位,按原值写入
3.如果原值的第8位及以后存在1,则写入1,否则写入0,结束写入(即压缩为了1个字节)
4.如果没结束,则写入原值的第8~14位,如果原值的第15位及以后存在1,则写入1,否则写入0,结束写入
5...重复上述行为,直到写完

KV结构图:


HFileV2结构

猜你喜欢

转载自zjushch.iteye.com/blog/1585066