本文来自于2018-09-15在深圳举办第五次HBase Meetup会议,毕杰山,华为云CloudTable(表格存储服务)主任工程师,长期聚焦于HBase及其它开源NoSQL技术,对各种分布式存储技术(KeyValue存储,文档存储,图存储,搜索引擎,时序/时空数据库等)抱有浓厚的兴趣
RowKey设计可以说是一个非常基础的话题,因为每一个HBase的使用/开发人员,都是从表/RowKey设计着手的。但细究起来,RowKey设计也有很多难点,尤其是如何与应用特点很好的结合起来。
这篇演讲的第一部分,首先简单介绍HBase的基础内容,而后探讨RowKey在读写流程中所发挥的关键作用。HBase的基础内容大部分已经在下面的文章里面详细探讨:
RowKey与索引设计,需要紧密结合业务需求场景。索引的设计目标是为HBase提供更多维度的查询能力。在实际应用中应该通过构建尽量少的索引,来满足更多的查询场景。因此第二部分介绍设计前需求调研的几个关键维度包括负载特点、查询场景以及数据特点。
-
负载特点
-
读写TPS;读写比重
-
重写轻读?重读轻写?读写相当?
-
-
数据负载均衡与高效读取时常是矛盾的。
-
在重读轻写的大数据场景中,RowKey设计应该更侧重于如何高效读取。
-
而在重写轻读的大数据场景中,在满足基本查询需求的前提下,应该更关注整体的吞吐量,这就对数据的负载均衡提出了很高的要求。
-
-
查询场景
-
需要支持哪些查询场景?时延要求?
-
最高频的查询场景是什么?
-
最有价值的数据排序场景是什么
-
-
是否有其它维度的价值查询场景?频度?
-
是否是组合字段场景?
-
各个字段的匹配类型?
-
Equal? Prefix Match? Wildcard? Text-Search?
-
-
-
数据特点
-
查询条件字段的离散度信息?
字段A的离散度 = (字段A的可能的枚举值数目)/数据总记录条数
-
字段离散度的定义:
-
-
查询条件字段的数据分布特点?
-
数据分布影响RowKey的设计,更进一步影响如何合理的划分Region信息
-
-
数据生命周期?
-
影响到一个表的一次Major Compaction发生时涉及到的最大数据量
-
-
第三部分介绍RowKey设计的几点技巧,二级索引RowKey设计的方法,组合字段RowKey/索引的适用场景/设计原则,字段组合的合理顺序等等。
-
影响查询性能的关键因素
-
基于某一个索引/RowKey进行查询时,影响查询的最关键因素在于能否将扫描的候选结果集限定在一个合理的范围内。
-
知识点备注:查询驱动条件与查询过滤条件:直接影响数据扫描范围的查询条件,称之为查询驱动条件。而其它的能够起到过滤作用的查询条件,则称之为查询过滤条件。影响查询的关键因素在于如何合理的设置查询驱动条件。
-
-
RowKey字段的选取
-
遵循的最基本原则:
-
唯一性: RowKey必须能够唯一的识别一行数据。
-
无论应用是什么样的负载特点,RowKey字段都应该参考最高频的查询场景。数据库通常都是以如何高效的读取和消费数据为目的,而不是数据存储本身。
-
而后,结合具体的负载特点,再对选取的RowKey字段值进行改造,组合字段场景下需要重点考虑字段的顺序。
-
-
-
避免数据热点的方法 - Reversing
-
如果经初步设计出的RowKey在数据分布上不均匀,但RowKey尾部的数据却呈现出了良好的随机性,此时,可以考虑将RowKey的信息翻转,或者直接将尾部的bytes提前到RowKey的前部。
-
缺点:更场景利于Get但不利于Scan,因为数据在原RowKey上的自然顺序已被打乱。
-
-
避免数据热点的方法 - Salting
-
Salting的原理是在原RowKey的前面添加固定长度的随机bytes,随机bytes能保障数据在所有Regions间的负载均衡。
-
缺点:既然是随机bytes,基于原RowKey查询时无法获知随机bytes信息是什么,也就需要去各个可能的Regions中去查看。可见,Salting对于读取是利空的。
-
-
避免数据热点的方法 - Hashing
-
基于RowKey的完整或部分数据进行Hash,而后将Hashing后的值完整替换原RowKey或部分替换RowKey的前缀部分。
-
缺点:与Reversing类似,Hashing也不利于Scan,因为打乱了原RowKey的自然顺序。
-
“Hash分片”方式:尽管可以保证所有数据到各个“桶”的分布均匀性。但打乱了数据原有的顺序,从而使得按顺序读取方式的性能偏低
“Range分片”方式: 每一个Region负责管辖一个Key值范围。这样的存储,保留了数据原有的顺序,从而按顺序读取方式的性能很高
-
分布式数据库的常见数据分片方式
-
有两种常见的基础数据分片方式:Hash分片和Range分片
-
原理与区别如图所示。在实际应用中,两者还可以结合应用。
-
HBase采用了基于RowKey分区的方式。
-
-
实现Hash+Range分片的方法
-
RowKey构成: ID + OrderTime
-
查询场景:给定ID,查找该ID关联的所有订单
-
-
-
达成的效果:不同ID的数据可均匀打散到各个Region中,而同一个ID的数据则是相邻存放的。
-
HBase基于Range的方式可以为使用者带来很大的灵活度,但却提升了使用门槛!!!
-
-
二级索引RowKey设计常见方法-无Schema模式
-
-
这是常见的设计思路,如果原数据RowKey中已经包含了索引列的信息,该设计容易导致数据冗余。
-
-
二级索引RowKey设计常见方法-有Schema模式
-
-
当原数据RowKey中的列与索引列有重叠时,该设计能避免一个列在索引列中被重复存储。但该设计需要事先支持Schema,也就是需要事先定义原数据的RowKey结构以及索引的结构信息。
-
-
二级索引字段的选取
-
对所有的价值查询场景进行详细分析,基于确实能够缩小查询范围的一部分列来构建二级索引。即,我们应该基于离散度较好的一些列来构建索引。
-
-
组合索引适用场景/构建原则
-
组合索引的创建,取决于对用户查询场景的详细分析。组合索引的确可极大的优化这些字段组合时的查询场景,但却会带来相对较大的数据膨胀。在不了解用户数据特点以及用户查询场景的情形下,盲目的构建组合索引,是要坚决避免的。
-
-
组合索引中字段组合的顺序
-
-
先导列的选取:
-
被选作先导列的列,一定是经常被用到的列
-
应选择设置了EQUALS查询条件的列作为先导列
-
先导列应该具备较好的离散度
-
尽量不要重复选择其它索引的先导列作为本索引的先导列
-
-
附加列的选取:
-
提供了EQUALS查询条件的列,应该放在前面部分
-
由于列的组合顺序将会影响到数据的排序,我们也应该考虑到业务场景关于排序的诉求
-
结合各个列的离散度进一步分析,将这些列组合之后,能否将数据限定在一个合理的范围之内?如果不能,需要结合查询场景设置更合理的组合列。
-
-
-
关于索引的其它建议
-
严格控制二级索引的数量
-
每一个索引所关联的索引数据总条数,与用户数据的总条数是1:1的。因此,在为一个用户表定义二级索引时,应该要考虑多个索引所带来的存储空间膨胀以及性能下降问题。建议在充分分析了各种查询场景的情况下,通过构建尽量少的索引,来满足更多的查询场景
-
-
Global Index在高并发/小批次查询场景中更有利
-
查询一个Local Index时,需要去查看每一个Index Region才能获取到符合条件的完整结果集,这对高并发查询并不利。但Local Index对于并发分析场景却是有利的
-
-
全文检索需求可考虑与Elasticsearch/Solr对接
-
Elasticsearch/Solr是专业的索引引擎,但不适合作为数据主存系统,因此,将核心数据存放在HBase中,而通过ES/Solr构建全文索引能力,是一种常见的组合应用场景
-
-
最后一部分围绕着OpenTSDB,JanusGraph以及GeoMesa三个典型的HBase生态技术,结合了它们各自的数据模型、查询场景,探讨它们的RowKey设计方案。
附上HBase RowKey与索引设计PPT:
备注:历届HBase Meetup会议的PPT: http://hbase.group/slides/
NoSQL主要泛指一些分布式的非关系型数据存储技术,这其实是一个非常广泛的定义,可以说涉及到分布式系统技术的方方面面。随着人工智能、物联网、大数据、云计算以及区块链技术的不断普及,NoSQL技术将会发挥越来越大的价值。
大家工作学习遇到HBase技术问题,把问题发布到HBase技术社区论坛http://hbase.group,欢迎大家论坛上面提问留言讨论。想了解更多HBase技术关注HBase技术社区公众号(微信号:hbasegroup),非常欢迎大家积极投稿。