Kudu、Hudi和Delta Lake的比较

 

Kudu、Hudi和Delta Lake的比较

kudu、hudi和delta lake是目前比较热门的支持行级别数据增删改查的存储方案,本文对三者之间进行了比较。

 

存储机制

 
kudu

kudu的存储机制和hudi的写优化方式有些相似。

kudu的最新数据保存在内存,称为MemRowSet(行式存储,基于primary key有序),
当MemRowSet写满(默认1G或者120s)后flush到磁盘,形成DiskRowSet(列式存储)。

DiskRowSet包含baseData与DeltaStores两部分,DeltaStores包含一个DeltMemStore和多个DeltaFile,
后续的更新数据存放在DeltMemStore中,增长到一定程度后flush成DeltaFile文件。

kudu会定期执行compaction操作,将DeltaFile中的更新合并到DiskRowSet,或者合并DiskRowSet,清除已删除的数据,并减少DiskRowSet的数量。

 
hudi

hudi维护了一个时间轴,记录了在不同时刻对数据集进行的所有操作。

hudi拥有2种存储优化,读优化适合读多写少的场景,写优化适合读少写多的场景。

  • 读优化(Copy On Write):在每次commit后都将最新的数据compaction成列式存储(parquet);
  • 写优化(Merge On Read):对增量数据使用行式存储(avro),后台定期将它compaction成列式存储。

 
delta lake

delta lake的存储机制和hudi的读优化方式相似。

delta lake的数据不会保存在内存中,而是直接写到新的数据文件中(parquet格式),同时在commit log中添加AddFile这种FileAction,快照新建/更新时读取事务日志,会加载新的数据文件信息。

 

读数据

 
kudu

先根据要扫描数据的主键范围,定位到目标的tablets,然后读取tablets中的DiskRowSet。

在读取每个DiskRowSet时,先根据主键过滤要scan的范围,然后加载范围内的baseData,再找到对应的DeltaStores,
应用所有变更,最后union上MemRowSet中的内容,最后返回数据给client。

kudu提供range分区和hash分区两种分区方式,通过多种索引(主键范围索引、bloomfilter、主键索引),支持随机读取数据和高效的批量读取数据。

 
hudi

hudi也维护着一个索引,以此将key快速映射到对应的fileId。索引的实现是插件式的,默认是bloomFilter,也可以使用HBase。

hudi提供3种查询视图。

  • 读优化视图:仅提供compaction后的列式存储的数据;
  • 增量视图:仅提供一次compaction/commit前的增量数据;
  • 实时视图:包括列式存储数据和写优化的行式存储数据。

 
delta lake

通过读取事务日志的checkpoint文件(parquet格式)和之后版本的commit文件(json格式),
建立当前最新的快照,该快照包含了当前版本所有数据文件的地址,然后使用spark读取数据。

 

更新数据

 
kudu

client向master发出请求,通过索引定位到具体的tablet,然后根据元数据连接tablet对应的tserver。

若数据在磁盘(DiskRowSet)上,则将更新信息写入DeltMemStore中,若数据在内存(MemRowSet)中,则将信息写入所在行的mutation链表中。

 
hudi

hudi没有传统意义的更新,只有append和重写。

hudi写数据的时候需要指定以下3个key。

  • RECORDKEY_FIELD_OPT_KEY:每条记录的唯一id,支持多个字段;
  • PRECOMBINE_FIELD_OPT_KEY:在数据合并的时候使用到,当RECORDKEY_FIELD_OPT_KEY相同时,默认取PRECOMBINE_FIELD_OPT_KEY属性配置的字段最大值所对应的行;
  • PARTITIONPATH_FIELD_OPT_KEY:用于存放数据的分区字段。

hudi更新数据和插入数据很相似(写法几乎一样),更新数据时,会根据以上三个字段对数据进行Merge。

 
delta lake

delta lake更新数据时会先定位待更新数据所在的文件,使用spark join获得结果数据集,将更新后的数据和文件中其他不需要更新的数据一起写入到新的文件里,同时在commit log中记录AddFile(新文件)和RemoveFile(旧文件)两种action。

 

其他

 

--- kudu hudi delta lake
行级别更新 支持 支持 支持
schema修改 支持 支持 支持
批流共享 支持 支持 支持
使用索引
多作业并发写 允许 不允许 允许
元数据位置 master 根目录文件夹 根目录文件夹
版本回滚 不支持 通过设置增量视图的INSTANTTIME范围,支持版本回滚 自带Time Travel功能,支持版本回滚
实时性 kudu使用内存存储新增数据,实时性相对较高 hudi有写优化存储方式,能达到1-5分钟延迟的近实时处理 delta lake必须完成commit提交才能查询到新增数据,实时性差
支持hadoop文件系统 不支持,kudu通过raft管理自己的存储服务器 支持 支持
缺省列处理 默认为null hudi在插入时必须指定所有字段,否则报错 默认为null
并发读写 支持 不支持多客户端同时写 支持
兼容性 与impala集成支持sql,支持spark读写 支持spark、presto、hive、mapreduce,兼容性较好 深度依赖spark,目前有限支持hive、presto,有待完善

 

如何选择合适的存储方案

 
kudu

  1. 不同于hudi和delta lake是作为数据湖的存储方案,kudu设计的初衷是作为hive和hbase的折中,因此它同时具有随机读写和批量分析的特性。

  2. kudu允许对不同列使用单独的编码和压缩格式,拥有强大的索引支持,搭配range分区和hash分区的合理划分,
    对分区查看、扩容和数据高可用性的支持都非常好,适用于既有随机访问,也有批量数据扫描的复合场景。

  3. kudu可以和impala、spark集成,支持sql操作,除此之外,kudu能够充分发挥高性能存储设备的优势。

  4. 相比较其他两者,kudu不支持云存储,也不支持版本回滚和增量处理。

 
hudi

  1. hudi的产生背景是为了解决Uber的增量更新问题,它提供了丰富的视图和存储优化方式,
    可以适配批量访问场景,增量处理场景,以及近实时查询场景,无论是读多写少还是读少写多,hudi都能提供对应的优化方案,
    用户可以根据自身场景灵活选择合适的配置。

  2. 三者之中,hudi的兼容性最好,它原生支持spark、presto、hive和mapreduce等大数据生态系统,并且读写底层文件实现了自己的InputFormat,更容易与其它系统做兼容。

  3. hudi目前还不支持通过sql操作数据,(19年12月)社区已经将其作为下一步的方向,但完成时间不确定。

  4. hudi不存在锁机制,因此不支持多客户端同时写一张表,这是需要注意的一点。

 
delta lake

  1. delta lake深度绑定spark,支持版本回滚,在写入的时候对数据进行合并,因此对读取比较友好。

  2. delta lake有乐观锁机制,允许并发写操作。但目前源码中对冲突检测很严格,对多用户同时更新的场景支持并不好,适用于写少读多(或者only append写多更新少)的场景。

  3. delta lake开源版目前基本不支持sql操作数据(0.5版本支持hive和presto读数据),应该在spark3.0发布后才会支持delete、update和merge操作。

  4. 开源版目前还存在小文件合并问题(商业版有优化,但是因为涉及到Databricks的Runtime功能,所以不准备开源,因此得自己手动合并小文件)。

  5. 总体来说,delta lake是一个较为优秀的数据湖存储方案,但直接使用开源版本还需要用户稍微进行一些针对性的优化。

posted @ 2020-01-05 19:58  夜飞辰  阅读(8755)  评论(0编辑  收藏  举报