在完成创建HBase数据库后我们后续最重要的工作就是如何设计和构建出合理高效的hbase表。那么接下来本文就将介绍一下如何设计及优化HBase数据库的表。OK,废话不再多说,接下来我们进入今天的正题~
一、设计HBase表的原则
1.1 Row Key
HBase中row key用来检索表中的记录,支持以下三种方式:
通过单个row key访问:即按照某个row key键值进行get操作;
通过row key的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描;
全表扫描:即直接扫描整张表中所有行记录。
在HBase中,row key可以是任意字符串,最大长度64KB,实际应用中一般为10~100bytes,存为byte[]字节数组,一般设计成定长的。
row key是按照字典序存储,因此,设计row key时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。
举个例子:如果最近写入HBase表中的数据是最可能被访问的,可以考虑将时间戳作为row key的一部分,由于是字典序排序,所以可以使用Long.MAX_VALUE - timestamp作为row key,这样能保证新写入的数据在读取时可以被快速命中。
1.2 Column Family
不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3个column family的表。因为某个column family在flush的时候,它邻近的column family也会因关联效应被触发flush,最终导致系统产生更多的I/O。感兴趣的同学可以对自己的HBase集群进行实际测试,从得到的测试结果数据验证一下。
1.3 In Memory
创建表的时候,可以通过HColumnDescriptor.setInMemory(true)将表放到RegionServer的缓存中,保证在读取的时候被cache命中。
1.4 Max Version
创建表的时候,可以通过HColumnDescriptor.setMaxVersions(int maxVersions)设置表中数据的最大版本,如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1)。
1.5 Time To Live
创建表的时候,可以通过HColumnDescriptor.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)。
1.6 Compact & Split
在HBase中,数据在更新时首先写入WAL 日志(HLog)和内存(MemStore)中,MemStore中的数据是排序的,当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。于此同时, 系统会在zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了(minor compact)。
StoreFile是只读的,一旦创建后就不可以再修改。因此Hbase的更新其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并(major compact),将对同一个key的修改合并到一起,形成一个大的StoreFile,当StoreFile的大小达到一定阈值后,又会对 StoreFile进行分割(split),等分为两个StoreFile。
由于对表的更新是不断追加的,处理读请求时,需要访问Store中全部的StoreFile和MemStore,将它们按照row key进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内存中索引,通常合并过程还是比较快的。
实际应用中,可以考虑必要时手动进行major compact,将同一个row key的修改进行合并形成一个大的StoreFile。同时,可以将StoreFile设置大些,减少split的发生。
二、HBase数据库表的优化原则
本篇文章先按照hbase表中的rowkey、columnfamily、column、timestamp几个方面进行一些分析。最后结合分析如何设计一种适合应用的高效表结构。
1、表的属性
(1)最大版本数:通常是3,如果对于更新比较频繁的应用完全可以设置为1,能够快速的淘汰无用数据,对于节省存储空间和提高查询速度有效果。不过这类需求在海量数据领域比较小众。
(2)压缩算法:可以尝试一下最新出炉的snappy算法,相对lzo来说,压缩率接近,压缩效率稍高,解压效率高很多。
(3)inmemory:表在内存中存放,一直会被忽略的属性。如果完全将数据存放在内存中,那么hbase和现在流行的内存数据库memorycached和redis性能差距有多少,尚待实测。
(4)bloomfilter:根据应用来定,看需要精确到rowkey还是column。不过这里需要理解一下原理,bloomfilter的作用是对一个region下查找记录所在的hfile有用。即如果一个region下的hfile数量很多,bloomfilter的作用越明显。适合那种compaction赶不上flush速度的应用。
2、rowkey
rowkey是hbase的key-value存储中的key,通常使用用户要查询的字段作为rowkey,查询结果作为value。可以通过设计满足几种不同的查询需求。
(1)数字rowkey的从大到小排序:原生hbase只支持从小到大的排序,这样就对于排行榜一类的查询需求很尴尬。那么采用rowkey = Integer.MAX_VALUE-rowkey的方式将rowkey进行转换,最大的变最小,最小的变最大。在应用层再转回来即可完成排序需求。
(2)rowkey的散列原则:如果rowkey是类似时间戳的方式递增的生成,建议不要使用正序直接写入rowkey,而是采用reverse的方式反转rowkey,使得rowkey大致均衡分布,这样设计有个好处是能将regionserver的负载均衡,否则容易产生所有新数据都在一个regionserver上堆积的现象,这一点还可以结合table的预切分一起设计。
3、columnfamily
columnfamily尽量少,原因是过多的columnfamily之间会互相影响。
4、column
对于column需要扩展的应用,column可以按普通的方式设计,但是对于列相对固定的应用,最好采用将一行记录封装到一个column中的方式,这样能够节省存储空间。封装的方式推荐protocolbuffer。
合理设计及优化HBase数据库表入门
最新推荐文章于 2024-01-28 14:32:43 发布