英文原文:https://hudi.apache.org/blog/hudi-indexing-mechanisms/html
Apache Hudi使用索引来定位更删操做所在的文件组。对于Copy-On-Write表,索引能加快更删的操做,由于避免了经过链接整个数据集来决定哪些文件须要重写。对于Merge-On-Read表,这个设计,对于任意给定的基文件,能限定要与其合并的记录数量。具体地,一个给定的基文件只须要和其所包含的记录的更新合并。相比之下,没有索引的设计(好比Apache Hive ACID),可能会致使须要把全部基文件与全部更删操做合并。apache
从高角度看,索引把一个记录的键加一个可选的分区路径映射到存储上的文件组ID(更多细节参考这里)。在写入过程当中,咱们查找这个映射而后把更删操做导向基文件附带的日志文件(MOR表),或者导向最新的须要被合并的基文件(COW表)。索引也使得Hudi能够根据记录键来规定一些惟一性的限制。缓存
记录更新(黄色块)和基文件(白色块)合并消耗的对比运维
目前Hudi已经支持了几种不一样的索引机制,而且在工具库中不断地完善和增长更多机制,在本文余下的篇幅中将根据咱们的经验介绍几种不一样做业场景下索引机制的选择。咱们也会穿插一些对已有限制、即将开展的工做、以及优化和权衡方面的评论。工具
目前Hudi支持如下几种索引类型。优化
布隆索引
(默认):使用以记录的键生成的布隆过滤器,也能够用记录键对可能对应的文件进行剪枝操做。简单索引
:对更删的记录和存储上的表里提取的键进行轻量级的链接。HBase索引
:使用外部的Apache HBase表来管理索引映射。写入器能够经过hoodie.index.type
来设置以上的类型。此外,自定义的索引实现能够经过hoodie.index.class
来配置。对Apache Spark写入器需提供SparkHoodieIndex
的子类。设计
另外一个须要了解的关键点是区分全局索引和非全局索引。布隆索引
和简单索引
都有一个全局选项,分别是hoodie.index.type=GLOBAL_BLOOM
和hoodie.index.type=GLOBAL_SIMPLE
。HBase索引
本质上就是全局索引。日志
因为数据可能有着不一样的体量、速度和读取规律,不一样索引会适用于不一样的做业场景。接下来让咱们分析几个不一样的场景来讨论如何选择适合的Hudi索引。code
许多公司会在NoSQL数据存储中存放大量的交易数据。例如共享出行的行程表、股票买卖记录的表、和电商的订单表。这些表一般一直在增加,且大部分的更新随机发生在较新的记录上,而对旧记录有着长尾分布型的更新。这一般是源于交易关闭或者数据更正的延迟性。换句话说,大部分更新会发生在最新的几个分区上而小部分会在旧的分区。htm
典型的事实表的更新样式。
对于这样的做业模式,布隆索引
就能表现地很好,由于查询索引能够靠设置得当的布隆过滤器来剪枝不少数据文件。另外,若是生成的键能够以某种顺序排列,参与比较的文件数会进一步经过范围剪枝而减小。Hudi用全部文件的键域来构造区间树,这样能来高效地依据输入的更删记录的键域来排除不匹配的文件。
为了高效地把记录键和布隆过滤器进行比对,即尽可能减小过滤器的读取和均衡执行器间的工做量,Hudi缓存了输入记录并使用了自定义分区器和统计规律来解决数据的偏斜。有时,若是布隆过滤器的伪正率太高,查询会增长数据的打乱操做。Hudi支持动态布隆过滤器(设置hoodie.bloom.index.filter.type=DYNAMIC_V0
)。它能够根据文件里存放的记录数量来调整大小从而达到设定的伪正率。
在不久的未来,咱们计划引入一个更快的布隆索引机制。该机制会在Hudi内部的元数据表中跟踪记录布隆过滤器和取值范围从而进行快速的点查询。这能够避免因从基文件中读取布隆过滤器和取值范围而致使的查询的局限性。(整体设计请参考RFC-15)
事件流无处不在。从Apache Kafka或其余相似的消息总线发出的事件数一般是事实表大小的10-100倍。事件一般把时间(到达时间、处理时间)做为首类处理对象,好比物联网的事件流、点击流数据、广告曝光数等等。因为这些大部分都是仅追加的数据,插入和更新只存在于最新的几个分区中。因为重复事件可能发生在整个数据管道的任一节点,在存放到数据湖前去重是一个常见的需求。
上图演示了事件表的更新分布状况。
总的来讲,低消耗去重一个很是挑战的工做。尽管甚至能够用一个键值存储来实现去重(即HBase索引
),但索引存储的消耗会随着事件数增加而线性增加以致于变得不可行。事实上,有范围剪枝功能的布隆索引
是最佳的解决方案。咱们能够利用做为首类处理对象的时间来构造由事件时间戳和事件id(event_ts+event_id
)组成的键,这样插入的记录就有了单调增加的键。这会在最新的几个分区里大幅提升剪枝文件的效益。
这种类型的表一般包含高纬度的数据和数据连接,好比用户资料、商家信息等。这些都是高保真度的表。它们的更新量一般很小但所接触的分区和数据文件会不少,范围涉及从旧到新的整个数据集。有时由于没有很好的分区条件,这些表也会不分区。
上图演示了维度表的更新分布状况。
正如以前提到的,若是范围比较不能剪枝许多文件的话,那么布隆索引
并不能带来很好的效益。在这样一个随机写入的做业场景下,更新操做一般会触及表里大多数文件从而致使布隆过滤器依据输入的更新对全部文件标明真正(true positive)。最终会致使,即便采用了范围比较,也仍是检查了全部文件。使用简单索引
对此场景更合适,由于它不采用提早的剪枝操做,而是直接和全部文件的所需字段链接。若是额外的运维成本能够接受的话,也能够采用HBase索引
,其对这些表能提供更加优越的查询效率。
当使用全局索引时,用户也能够考虑经过设置hoodie.bloom.index.update.partition.path=true
或者hoodie.simple.index.update.partition.path=true
(Global Bloom)hoodie.hbase.index.update.partition.path=true
或者hoodie.hbase.index.update.partition.path=true
或者来处理分区路径须要更新的状况;例如对于以所在城市分区的用户表,会有用户迁至另外一座城市的状况。这些表也很是适合采用Merge-On-Read表型。
未来咱们计划在Hudi内实现记录层的索引机制,以此提升索引查询效率,同时也将省去维护额外系统(好比HBase)的开销。
若没有索引功能,Hudi就不可能在超大扩展规模上实现更删操做。但愿这篇文章为目前的索引机制提供了足够的背景知识和对不一样权衡取舍的解释。
如下是一些颇具意义的相关工做:
在接下来的开发中,项目组会对这个领域保持积极的投入。咱们始终期待更多贡献者的加入以及推动路线图中的项目。若是有意参与,欢迎与咱们的社区联系。