不改造HBase就能应对复杂查询场景?这个索引组件亮了丨自研出圈

设计要点

四大关键设计的权衡

前面讲了 Pharos 的外部特性,接下来我会着重讲一些关键设计时的tradeoff,希望对大家的研发工作有所帮助。

存储策略

HBase是没有索引概念的,我们首先要解决的是如何存储索引。

Pharos采用的方式是在数据表增加一个“独立列族”用于存储索引信息,利用列族对应独立文件的特点,形成独立的索引文件,不会直接受到原始数据量的影响,降低磁盘I/O开销。这个设计另一个好处在于,索引与数据同分布,不必干预HBase的Region分布策略。后续的所有设计都是在同Region基础上,这大大简化了实现的复杂度。

这种索引与数据同分布的模式,可以简称为“分区索引”; 而索引与数据各自存储的方式,则成为“全局索引”。 “全局索引”的缺点在于无法完成索引查询的下推,优点是可以进行全局控制,例如唯一索引。 选择“分区索引”是因为我们期望实现低延时目标,当然同时也就放弃了对“唯一索引”的支持,至少目前是不支持的。

存储模型

索引记录的Key部分,最开始是Region头信息,保证与数据的同分布,而后是被索引字段的信息,接下来是存储索引名称等信息,数据记录的Key则被拼接为尾部信息;value部分则存储反序列化的元数据。

可以看到,这样设计的优点是索引检索速度快,存储成本也比较小。 所以,当查询条件较复杂需要建立多个索引时,整体存储成本依然是可接受的。

分页机制

传统的后台分页方法是由应用服务记录每页的末尾记录主键,这个主键通常是由数据库提供的row number,数据库本身并不感知分页动作。对于分布式存储的HBase,每个Region都可能存在分页断点,如果延续该思路,应用端要记录大量断点信息,不仅增加传输数据量,也增大了开发的难度。在Pharos的设计中,我们通过Client作为汇聚点,缓存每个Region的断点信息,在Pharos的Client中增加Session概念,应用端仅需持有Session ID即可顺利完成翻页操作。

索引与数据的事务一致性(实验版本)

根据Pharos的存储设计,我们可以知道索引实质上是与数据行前缀相同的另一行记录,保证索引与数据的一致性要使用跨行事务,但HBase本身不支持跨表、跨行事务,这就成为一个死结,也是几乎所有二级索引方案都不支持索引事务一致性的原因。

这个问题的解决比较复杂,所以我们先简单介绍下HBase内部机制。HBase采用LSM-Tree模型,在联机写入时,数据在日志和内存中各保留一份,两者通过MVCC与日志的协调机制可以保证事务一致性。我画了一张图来表示HBase 1.2.6的事务控制逻辑,具体如下:

HBase内部会监控WAL的异常,但在Coprocessor事件体系中并未开“回滚标志位”,第三方开发者也就无法回滚相关数据行。

按说,到这里问题已经无解了,不过某天恰好受到了Percolator事务模型的启发,找到了另一种解决问题的思路。既然无法在写入过程通过回滚控制异常情况,那我们可以延后在读取过程中来补充对异常的操作,也就是说在下一次查询操作中再次确认并维护索引的一致性。

具体处理过程是,在首次写入索引信息时,置事务标志位为“不确定”,数据行更新完成后,将该标志改为“成功”;如果出现回滚,则标志位保留“不确定”状态。查询操作中一旦发现“不确定”标志,则根据索引查找相应的数据行是否存在,如存在,则将该标志更新为“成功”。

我们用流程图来体现处理过程,如下图:

该方式付出的代价是写入时需要两次更新事务标志,相对于仅写入数据行(无索引)肯定增加了一些开销,在测试环境下我们发现损耗在15%左右,主要是指延时; 在查询环节虽然存在确认索引事务的可能性,但因为其发生的概率极低,不会对查询产生实质性影响。

未来展望

彻底解决Region分裂问题

目前Pharos主要还是在内部使用场景中测试,收集需求和问题,近期我们会发布V0.3版本。新版本会推出一些让人激动的特性,主要仍是围绕查询性能的提升,推出Pharos自有的数据组织形式,提供更加丰富的查询加速手段,完成从二级索引组件到一个完整的技术中间件的演进。

其中,特别重要的一点是彻底解决了Region分裂问题,我要迫不及待地做个剧透。

Region分裂是HBase数据再平衡的手段,保证每个数据节点上的数据量分布大致平均,也会有一些打散热点的效果。但是,分裂机制却破坏了索引与数据的同分布,在同类方案中通常采用很重的更新操作来调整索引的位置,追随数据的分布情况。

这种方式显得过于笨拙,并且更新过程会影响整体方案的可用性。因此,Pharos在V0.22版本中是建议索引延后加载,这样在数据更新导致的重分布完成后再更新索引。

实际操作中,这种方式要重复读取数据文件,延长了整体数据加载周期,对运维操作来说不够友好。在V0.3中,我们加入了Pharos自有的数据组织方式,实现在Region分裂时维持索引的同分布效果。

考虑到篇幅的限制,就介绍到这里。几个月后,我会择机与大家分享在新版本的改进,使用了更多独创性的方法,敬请期待。

优秀自研项目征集:dbaplus社群【自研出圈】专栏欢迎广大技术人员和技术团队投稿参与,分享自研成果和经验。优先收录开源或内部自用工具、平台、系统。

参与方式: 发送文章至 editor@dbaplus.cn,详情咨询可加微信 Sunny9095