Iceberg V3 的删除向量是什么?为什么比 Position Delete 文件好?

第 1099 题 约 3 分钟读完

本文目录
  1. 面试怎么回答
  2. 先回顾:V2 的行级删除怎么做
  3. Position Delete 的问题
  4. V3 的删除向量
  5. 使用时要注意

面试怎么回答

Iceberg V2 的 Merge-on-Read 用 Position Delete 文件记录哪个数据文件的第几行被删,频繁更新时每次提交都可能新增删除文件,删除文件越积越多,读一个数据文件要找出多个删除文件合并,读放大严重,只能靠频繁 Compaction。V3 引入删除向量,把一个数据文件被删的行压成 Roaring 位图存在 Puffin 文件里,并且规定每个数据文件最多一个删除向量,新删除和旧位图合并后替换,读的时候只读一个位图按位过滤。这样小文件和读放大都缓解了。用之前要把表升到 V3,所有读写引擎都要支持;它只替代 Position Delete,Equality Delete 的 CDC 场景仍要做 Compaction。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

Iceberg 表格式 V3 规范里有一个很实用的改进:删除向量(Deletion Vector)。面试官问这个,是想看你理解 Merge-on-Read 模式下删除和更新的真实开销。

先回顾:V2 的行级删除怎么做

Iceberg V2 支持 Merge-on-Read:更新或删除时不重写数据文件,而是另外写一个删除文件,读的时候再把删除合并掉。删除文件有两种:

  • Equality Delete:记录「哪些主键值被删了」,Flink CDC 写入时常用;
  • Position Delete:记录「哪个数据文件的第几行被删了」,Spark 的 UPDATE / DELETE / MERGE 常用。

Position Delete 的问题

  • 小文件爆炸:每次提交都可能为同一个数据文件写一个新的删除文件,频繁更新的表,删除文件数量很快比数据文件还多;
  • 读放大:读一个数据文件时,可能要找出好几个相关的删除文件,逐个读出来再合并,查询越来越慢;
  • 一个删除文件可能对应很多数据文件,读的时候还要筛出跟自己相关的部分。

最后只能靠频繁的 Compaction 来救,运维成本高。

V3 的删除向量

删除向量把「这个数据文件里哪些行被删了」压缩成一个位图(Roaring Bitmap),存在 Puffin 文件里。关键规则是:

  1. 每个数据文件最多对应一个删除向量。再有新的删除时,把旧位图和新删除合并成一个新的位图,替换掉旧的,而不是再加一个文件;
  2. 读数据文件时只要读这一个位图,按位过滤就行,不用合并多个删除文件;
  3. 位图非常紧凑,删除很多行也只占很小的空间。

所以小文件问题和读放大都大幅缓解,更新频繁的表查询性能更稳定。这个设计和 Delta Lake 的删除向量思路一致,也方便不同格式之间互通。

使用时要注意

  • 需要把表升级到 format-version 3,并且读写这个表的所有引擎版本都要支持 V3,否则老引擎读不了;
  • 删除向量只替代 Position Delete,Equality Delete 依然存在,CDC 场景还是要定期 Compaction;
  • V3 还带来了 Variant 类型、行血缘(Row Lineage)、列默认值等,升级前一起评估。