hbase的内部使用KeyValue的形式存储,其key时rowKey:family:column:logTime,value是其存储的内容。html
其在region内大多以升序的形式排列,惟一的时logTime是以降序的形式进行排列。java
因此,rowKey里越靠近左边的信息越容易被检索到。其设计时,要考虑把重要的信息放左边,不重要的信息放到右边。这样能够提升查询数据的速度。最重要的提升索引速度的就是设计合适的rowKey。git
在作RowKey设计时,请先考虑业务是读比写多,仍是读比写少,HBase自己是为写优化的,即使是这样,也可能会出现热点问题,而若是咱们读比较多的话,除了考虑以上RowKey设计原则外,还能够考虑HBase的Coprocessor甚至elasticSearch结合的方法,不管哪一种方式,都建议作实际业务场景下数据的压力测试以获得最优结果。github
rowKey是一个二进制,RowKey的长度被不少开发者建议说设计在10~100个字节,以byte[]形式保存,最大不能超过64kb。建议越短越好,不要超过16个字节。sql
太长的影响有几点点:数据库
注意:不只RowKey的长度是越短越好,并且列簇名、列名等尽可能使用短名字,由于HBase属于列式数据库,这些名字都是会写入到HBase的持久化文件HFile中去,过长的RowKey、列簇、列名都会致使总体的存储量成倍增长。设计模式
保证rowKey的惟一性。因为在HBase中数据存储是Key-Value形式,若HBase中同一表插入相同RowKey,则原先的数据会被覆盖掉(若是表的version设置为1的话)。缓存
设计的RowKey应均匀分布在各个HBase节点上。如RowKey是按系统时间戳的方式递增,RowKey的第一部分若是是时间戳的话,将形成全部新数据都在一个RegionServer堆积的热点现象,也就是一般说的Region热点问题,热点发生在大量的client直接访问集中在个别RegionServer上(访问多是读、写或者其余操做),致使单个RegionServer机器自身负载太高,引发性能降低甚至Region不可用,常见的是发生jvm full gc或者显示region too busy异常状况。网络
当往HBase表写入大量数据时,须要在RegionServer上分散负载来进行优化。这并不难,可是你可能不得不在读模式优化上付出代价。好比,时间序列数据的例子,若是你的数据直接使用时间戳作行健,在写入时在单个region上会遇到热点问题。并发
许多使用场景下,并不须要基于单个时间戳访问数据。你可能要运行一个做业在一个时间区间上作聚合计算,若是对时间延迟不敏感,能够考虑跨多个region作并行扫描来完成任务。但问题是,应该如何把数据分散在多个region上呢?有几个选项能够考虑,答案取决于你想让行健包含什么信息。
每次当你须要访问以这个散列值为键的行时,须要精确知道“TheRealMT”。时间序列数据通常不这样处理。当你访问数据时,可能记住了一个时间范围,但不大可能知道精确的时间戳。可是有些状况下,可以计算散列值从而找到正确的行。为了获得一种跨全部region的、优秀的分布策略,你可使用MD五、SHA-1或者其余提供随机分布的散列数。
2.salting。当你思考行健的构成时,salting是另外一种技巧。让咱们考虑以前的时间序列数据例子。假设你在读取时知道时间范围,但不想作全表扫描。对时间戳作散列运算而后把散列值做为行健的作法须要作全表扫描,这是很低效的,尤为是在你有办法限制扫描范围的时候。使用散列值做为行健在这里不是办法,可是你能够在时间戳前面加上一个随机数前缀。
例如,你能够先计算时间戳的散列码,而后用RegionServer的数量取模来生成随机salt数:
取到salt数后,加到时间戳的前面生成行健:
如今行健以下所示:
你能够想到,这些行将会基于键的第一部分,也就是随机salt数,分布在各个region。
0|timestamp1,0|timestamp5和0|timestamp6将进入一个region,除非发生region拆分(拆分的状况下会分散到两个region)。1|timestamp2,1|timestamp9进入另外一个不一样的region,2|timestamp4,2|timestamp8进入第三个region。连续时间戳的数据散列进入了多个region。
但并不是一切都是完美的。如今读操做须要把扫描命令分散到全部region上来查找相应的行。由于它们再也不存储在一块儿,因此一个短扫描不能解决问题了。这是一种权衡,为了搭建成功的应用你须要作出选择。这是一个利用信息的位置来得到跨region分布的经典例子。
3. Reverse反转。针对固定长度的RowKey反转后存储,这样可使RowKey中常常改变的部分放在最前面,能够有效的随机RowKey。反转RowKey的例子一般以手机举例,能够将手机号反转后的字符串做为RowKey,这样就避免了以手机号那样比较固定开头致使热点问题。这样作的缺点是牺牲了RowKey的有序性。
时间戳反转。一个常见的数据处理问题是快速获取数据的最新版本,使用反转的时间戳做为RowKey的一部分对这个问题十分有用,能够用Long.Max_Value - timestamp追加到key的末尾。举例,在设计推帖流表时,你的焦点是为读优化行健,目的是把推帖流里最新的推帖存储在一块儿,以便于它们能够被快速读取,而不用作开销很大的硬盘搜索。在推贴流表里,你使用倒序时间戳(Long.MAX_VALUE - 时间戳)而后附加上用户ID来构成行健。如今你基于用户ID扫描紧邻的n行就能够找到用户须要的n条最新推帖。这里行健的结构对于读性能很重要。把用户ID放在开头有助于你设置扫描,能够轻松定义起始键。
设计模式:反转+时间戳反转
RowKey:reverser(order_id) + (Long.MAX_VALUE - timestamp)
这样设计的好处一是经过reverse订单号避免Region热点,二是能够按时间倒排显示,能够获取到最新的订单。
一样适用于须要保存一个用户的操做记录,按照操做时间倒序排序。设计的rowKey为:reverser(userId) + (Long.MAX_VALUE - timestamp)。若是须要查询某段时间的操做记录,startRow是[userId反转][Long.MAX_VALUE - 起始时间],stopRow是[userId反转][Long.MAX_VALUE - 结束时间]。
HBase只存储了最近10分钟的热数据
设计模式:salt加盐
RowKey:两位随机数Salt + eventId + Date + kafka的Offset
这样设计的好处是:设计加盐的目的是为了增长查询的并发性,假如Salt的范围是0~n,那咱们在查询的时候,能够将数据分为n个split同时作scan操做。通过咱们的屡次测试验证,增长并发度可以将总体的查询速度提高5~20倍以上。随后的eventId和Date是用来作范围Scan来使用的。在咱们的查询场景中,大部分都是指定了eventId的,所以咱们在eventId放在了第二个位置上,同时呢,经过Salt + eventId的方式能够保证不会造成热点。把date放在RowKey的第三个位置上能够实现date作scan,批量Scan性能甚至能够作到毫秒级返回。
这样的RowKey设计可以很好的支持以下几个查询场景:
数据库查询可简单分解为两个步骤:1)键的查找;2) 数据的查找
因这两种数据组织方式的不一样,在RDBMS领域有两种常见的数据组织表结构:
索引组织表:键与数据存放在一块儿,查找到键所在的位置则意味着查找到数据自己。
堆表:键的存储与数据的存储是分离的。查找到键的位置,只能获取到数据的物理地址,还须要基于该地址去获取数据。
HBase数据表实际上是一种索引组织表结构:查找到RowKey所在的位置则意味着找到数据自己。所以,RowKey自己就是一种索引。
若是提供的查询条件可以尽量丰富的描述RowKey的前缀信息,则查询时延越能获得保障。以下面几种组合条件场景:
* Name + Phone + ID
* Name + Phone
* Name
若是查询条件不能提供Name信息,则RowKey的前缀条件是没法肯定的,此时只能经过全表扫描的方式来查找结果。
一种业务模型的用户数据RowKey,只能采用单一结构设计。但事实上,查询场景多是多纬度的。例如,在上面的场景基础上,还须要单独基于Phone列进行查询。这是HBase二级索引出现的背景。即,二级索引是为了让HBase可以提供更多纬度的查询能力。
注:HBase原生并不支持二级索引方案,但基于HBase的KeyValue数据模型与API,能够轻易的构建出二级索引数据。Phoenix提供了两种索引方案,而一些大厂家也都提供了本身的二级索引实现。
从0.94版本,HBase官方文档已经提出了HBase上面实现二级索引的一种路径:
业界比较知名的基于Coprocessor的开源方案:
Apache Phoenix在目前开源的方案中,是一个比较优的选择,主打SQL On HBase,基于SQL能完成HBase的CRUD操做,支持JDBC协议。
选择不基于Coprocessor开发,自行在外部构建和维护索引关系也是另一种方式。
常见的是采用底层基于Apache Lucene的ElasticSearch(下面简称ES)或Apache Solr,来构建强大的索引能力、搜索能力,例如支持模糊查询、全文检索、组合查询、排序等。
其实对于在外部自定义构建二级索引的方式,有本身的大数据团队的公司通常都会针对本身的业务场景进行优化,自行构建ES/Solr的搜索集群。例如数说故事企业内部的百亿级数据全量库,就是基于ES构建海量索引和检索能力的案例。主要有优化点包括:
下面显示了数说基于ES作二级索引的两种构建流程,包含:
数据查询流程:
HBase表设计一般能够是宽表(wide table)模式,即一行包括不少列。一样的信息也能够用高表(tall table)形式存储,一般高表的性能比宽表要高出50%以上,因此推荐你们使用高表来完成表设计。表设计时,咱们也应该要考虑HBase数据库的一些特性:
参考资料:
《HBase实战》
https://www.cnblogs.com/parent-absent-son/p/10200202.html
https://blog.csdn.net/wangshuminjava/article/details/80575864
https://www.cnblogs.com/yuguoshuo/p/6265649.html
http://www.nosqlnotes.com/technotes/hbase/hbase-rowkey/
https://zhuanlan.zhihu.com/p/43972378