0. 问题
最近碰到一个问题:
hbase使用bulkload方式, 两次把全量数据load到hbase表,并且手动进行大合并,发现hbfile的大小差不多变成2倍了(表的version设置为1了),因为version为1,所以同样的数据load两次+大合并应该是只有1份数据,而且文件大小应该是第一次的大小
排查方案:
新发现 | 假设 | 排查方法 | 结果 |
---|---|---|---|
有多份数据存在? | 根据key来查数据,看是不是能查出来多个版本的数据 | 只有1个版本的数据 | |
发现做hfile的时候,生成KeyValue 的时候使用的时间戳都是同一个值HConstants.LATEST_TIMESTAMP |
是不是因为timestampe 值相同,造成大合并的时候相同的数据无法删除 |
生成KeyValue 的时候使用当前的时间戳,再导2次数据+大合并 |
文件还是2倍,但是导第三次的时候+大合并,文件保持了2倍 |
合并完的hfile多了BloomFilter 部分多数据 |
BloomFilter 的部分造成数据double |
hbase hfile -m -f path 直接看出BloomFilter 部分的大小 |
直接排除 |
注意到我们的hfile的compress的值为none ,但我们建表带时候指定了压缩格式为SNAPPY ,而且hbase UI上看到的属性为: {NAME=>c} , 而不是 {NAME=>'c', COMPRESSION=>'SNAPPY'} |
数据没有启用压缩 | 使用alter 命令修改压缩格式+大合并 |
文件大小减小到一半 |
当然在中间过程中查看了一些源码,但没啥效果
1. 原因
1. 代码美学反推
建表的时候压缩格式的设置没有起作用,当时的设置属性的语句:
val tableDescriptor = new HTableDescriptor(table)
tableDescriptor.addFamily(new HColumnDescriptor(familyName))
tableDescriptor.setConfiguration("COMPRESSION", "SNAPPY")
tableDescriptor.setConfiguration("VERSIONS", "1")
tableDescriptor.setConfiguration("BLOCKCACHE", "FALSE")
tableDescriptor.setConfiguration("BLOOMFILTER", "ROW")
tableDescriptor.setConfiguration("DATA_BLOCK_ENCODING", "FAST_DIFF")
当时在查这段代码的时候,有点奇怪,因为HTableDescriptor
居然没有直接设置这些属性的方法,后来才明白,原来用的有问题,正确的版本应该是:
val columnDescriptor = new HColumnDescriptor(familyName)
columnDescriptor.setCompressionType(Compression.Algorithm.SNAPPY)
// 还有另一个API是设置大合并的压缩格式的,没有设置的时候,使用的是列簇的压缩格式
tableDescriptor.addFamily(columnDescriptor)
当看到这个版本的代码之后,瞬间觉得这个是对的,而且这个API
特别方便,不像第一个需要指定字符串,这种hard-code
的东西果然容易出问题,因为再不济应该有类似这样的常量或者枚举出现才好:
val CompressionType = "COMPRESSION"
为啥不把默认格式压缩格式设置为SNAPPY呢?
2. hbase设计原理
只能说代码里面将HColumnDescriptor
和HTableDescriptor
做了区分,而且压缩格式是以列簇来指定的,这样更灵活,当然也更麻烦一点
2. 修复问题
alter 'table', {NAME='c', COMPRESSION='snappy'}
major_compact 'table'
那么问题来了, 修改表压缩格式的时候,是否需要disable
表,当前表为只有读操作