HBase表数据倾斜治理_HBase快照映射到HDFS过程中HBase工具类解码与十六进制解码的区别
1.背景
在hbase倾斜治理过程中,将hbase快照数据的读取和bulkload分成两步,第一步读取hbase数据存储到hdfs系统的text文件中,第二步再读取hdfs系统的text文件数据bulkload到hbase中。
2.HBase内置编解码算法
HBase内置的Bytes工具类中定义了对String、boolean、double、float、int、long、short七种类型数据进行编解码的方法:
- String使用UTF-8算法进行编码
- boolean编码为长度为1的byte[]
- double编码为长度为8的byte[]
- float编码为长度为4的byte[]
- int编码为长度为4的byte[]
- long编码为长度为8的byte[]
- short编码为长度为2的byte[]
3.使用HBase内置算法解码
3.1读取思路
1 | public class GetHbaseMapper extends TableMapper<Text, Text> { |
- mapper输出数据到Text文件中时key和value之间的默认分隔符就是”\t”。
- “\n”是换行符;”\t”是制表符,也就是Tab;”\r”是回车符;”\f”是换页符。
写Text文件的思路就是key是行首,与value之间使用制表符分隔,每个value之间也是用制表符分隔。
3.2读取结果
读取结果Text文件:
1 | UV4�% ChangeRate: ImpactIndex: OrdCustNum: � OrdCustNumDaliyRate: ?�Y��C�� Value: |
可以看出每一行数据存在比较严重的乱码,甚至出现了乱码中存在换行符的情况,导致一行数据变成了两行。而我们还要将这个Text文件读取回写到hbase当中去,那么读取该Text文件时按照什么规则来分隔每行数据中的rowkey和每个value根本无法统一。而且之前读取hbase快照时将\n|\r|\t|\1|\N等转化为空格的逻辑也直接篡改了数据。
3.3原因分析
造成上述错误的根本原因就是,我们的预计算数据的rowkey和value中很多数据都是直接从int、long等非String类型编码成byte[]保存到hbase中。如果直接按String类型使用UTF-8算法来解码,一定会解码出很多具有其他特殊含义的特殊字符,导致解码出的数据无法按照我们预想的格式来存储。
如果存储在hbase当中的rowkey和value都是String类型的,那么我们按照上述的解码方式得到的就是本身存储的可以看出意义的原数据,就不会出现无意义的乱码和特殊字符。包括解码出来的\n|\r|\t|\1|\N等分隔符号也是我们本来就想保存成这样,那么把它们替换成空格也不会影响原数据的意义。
那么为什么我们存储预计算数据时将int、long类型数据直接编码,而不是转化成String类型再编码?第一点,当int、long类型数据较大或较长时直接编码为长度为4或8的byte[]可以节约存储;第二点,rowkey都由int、long等非String类型数据直接编码得到的byte[]组成可以实现长度固定,且使用scan进行范围搜索时非常准确,不会出现长度不一致导致的误拼接脏数据。
3.4解决方案
思路一:
可以另外存储一份hbase表的元数据信息,保存rowkey和value的字段数据类型信息。在解析读取hbase快照时获取该表的元数据信息,针对每个不同数据类型的字段使用不同的方法进行解码,这样就可以还原出rowkey和value具有意义的原数据。
思路二:
反正我们本来也不是想把数据回写到Text文件中然后直接读该该Text文件中的数据,而是要再回写到hbase当中去,所以Text文件中的数据有没有意义也不重要,只要在回写到hbase时能够被正确分隔就行。那么可以使用十六进制字符串编码格式来对hbase快照中的数据进行解码,这样解码出来的rowkey和value就全都是0~f的字符,不会存在特殊分隔符号。
4.使用十六进制解码
4.1读取思路
1 | public class ReverseRowkeyMapper extends TableMapper<Text, Text> { |
自定义的十六进制编解码算法:
1 | public class StringHexUtils { |
4.2读取结果
读取结果Text文件:
1 | 19691900000000000000000255560000000001348b350000000100000001 ChangeRate:000000010000000000000000 ImpactIndex:00000001000000000000000OrdCustNum:000000010000000000000000 OrdCustNumDaliyRate:000000010000000000000000 Value:000000010000000000000000 |
可以看出非常整齐规律,回写到hbase时使用如下mapper即可正确读写:
1 | public class LoadHbaseMapper extends Mapper<LongWritable, Text, ImmutableBytesWritable, Put> { |