我之前参与过一个日志系统的开发,存储用HBase。我简单罗列下用到的HBase优化,备忘。以后把它整理成更友好的介绍性文章。
系统简介
表结构相关优化
-
多条日志打包,压缩保存。
- 我们发现,如果简单地将一条日志保存为HBase表中的一行,会导致HBase表记录数很大,row key数量很大,region数量很多,HBase元数据开销很大。这会造成HBase集群不稳定。
- 于是,我们决定将多条日志打成一个包(chunk)。一个包作为HBase表中的一行保存,大大减少了HBase表的记录数,减轻了HBase元数据的开销。打包是通过写入日志时,在应用服务器的内存中收集日志直到字节数超出一定阈值来实现的。
- 除了打包,压缩也是在应用服务器端完成的,而不是依靠HBase (column family的compression配置)完成。因为我们希望压缩不仅能节省HBase存储空间,还要能节省应用服务器和HBase之间的网络流量。
- 打包时,根据row key的语义,正确设置row key。比如开始时间是第一条日志的开始时间,结束时间是最后一条日志的结束时间。
- 时间上相邻的日志,其正文内容往往很相似。因此,将多条日志打包压缩,比一条一条日志分开压缩,能获得更高的压缩率,节省网络传输带宽和存储。
- 打包意味着查询时的解包。为此我们部署了coprocessor在HBase集群中,充分利用HBase集群的CPU解包。
-
fuzzy row filter
系统配置类优化
-
HBase客户端优化——牺牲可靠性,提高日志写入的吞吐量
- 关闭WAL,直接写入日志。
- 适当增加writer buffer大小。
- 批量Put,传入List;关闭autoFlush。
- 压缩算法改用snappy,牺牲压缩率,降低CPU消耗,提高吞吐量。
-
调整HFile data block大小。data block越大,索引粒度越粗,顺序访问吞吐量越高。
- 在一个data block内部,通常只能顺序遍历,看我们搜索的Key是否存在。但是,也可以启用bloom filter,很快地告诉我们,一个Key是否一定不在这个data block中。
-
关闭某些column family的block cache缓存。
- 查询时,只有重复性的、随机的访问,才能命中block cache。如果某一个列只是用于海浪数据的顺序访问,那么对它缓存没有意义,相反,它能冲掉block cache本应该缓存的其他列的数据。对于这样的列, 应该关闭block cache缓存。
-
日志归档
* 写入HBase的日志,TTL设为7天。HBase用于实时的web查询,只能看这7天的日志。
- 这个系统有另外一路,实时写日志进HDFS,供HIVE分析。这是用于离线查询7天以前的历史日志的。
- HBase默认存三个版本的cell,对日志来说没有必要,只需要存一个版本。
顺便提下与HBase无关的其他优化
- 通常我们会用先进先出的队列保存临时积压的日志。但是,在日志系统的场景中,当日志积压时,让用户看到最新写入的日志,比让用户看到历史日志更重要。因此我们用先进后出的栈,保存临时积压的日志。