Paimon 删除向量
RowKind
可以标记删除,但它和 DeletionVector
(删除向量)是为解决不同场景下的问题而设计的两种机制,它们工作在不同的层面。
简单来说:
RowKind
是“逻辑层”的变更指令,主要用于primary-key
表的 LSM-Tree 合并过程。DeletionVector
是“物理层”的读时过滤优化,用于在不重写数据文件的前提下,快速地“标记”某些行为无效,极大地提升了DELETE
/UPDATE
操作的性能,并使其能应用于所有表类型。
RowKind
是附加在每一行数据上的一个字段,有 +I
(INSERT), -U
(UPDATE_BEFORE), +U
(UPDATE_AFTER), -D
(DELETE) 四种类型。
- 工作机制:它完全依赖于
primary-key
表的 Merge-Tree (LSM) 结构和其合并引擎(Merge Engine)。- 当执行
DELETE FROM T WHERE pk = 1
时,Paimon 会写入一条(pk=1, ..., RowKind=-D)
的新记录。 - 这条
-D
记录和原来的数据记录可能存在于不同的数据文件(不同的 Level)。 - 只有在读取时或者后台 Compaction(合并)时,Paimon 的合并引擎才会将主键相同的一组记录(比如一个
+I
和一个-D
)放在一起进行计算,最终将这条记录“消除”,使其在查询结果中不可见。
- 当执行
- 局限性:
- 必须有主键和合并引擎:
RowKind
机制只在primary-key
表中生效,因为它需要根据主键来找到所有相关的记录进行合并。对于没有主键的 Append-Only 表,RowKind
毫无意义。 - 读时开销:在 Compaction 发生之前,读取数据需要同时读取多个层级的文件,并在内存中进行合并计算,以判断哪些数据是有效的,这会带来一定的读取开销。
- Append-Only 表的难题:对于普通的 Append-Only 表,如果你想删除某一行,是没有办法的。因为没有主键和合并引擎,你无法通过写入
-D
记录来抵消已有的数据。传统的做法是找出包含要删除数据的整个文件,重写这个文件,把要删除的数据剔除掉,这个代价极其高昂。
- 必须有主键和合并引擎:
DeletionVector
的出现:解决性能和普适性问题
DeletionVector
的出现就是为了解决上述 RowKind
的局限性,尤其是非主键表。它是一种Merge-on-Write (MOW) 思想的实现。
工作机制:
- 定位:当执行
DELETE FROM T WHERE ...
时,Paimon 首先会扫描数据,找到满足条件的行所在的物理位置(即哪个数据文件,以及是文件中的第几行)。 - 标记:Paimon 不会重写数据文件,也不会写入
-D
记录。取而代之,它会维护一个位图(Bitmap),这个位图就是删除向量。如果文件file_A
的第 100 行和第 200 行被删除了,Paimon 就会在这个文件的删除向量中,将第 100 和 200 位标记为1
。 - 持久化:这个删除向量(位图)会作为一个非常小的独立索引文件(
DELETION_VECTORS_INDEX
)被保存下来。 - 读时过滤:当查询数据时,Paimon 会先检查是否存在删除向量索引。如果存在,就会在读取数据文件之前,先加载这个位图。当读取
file_A
时,它会根据位图直接跳过第 100 行和第 200 行的解析,就像它们不存在一样。
- 定位:当执行
优势:
- 普适性:它不依赖主键和合并引擎,因此可以用于所有表类型,包括 Append-Only 表。这使得 Append-Only 表也具备了高效
DELETE
/UPDATE
的能力。 - 高性能:
DELETE
或UPDATE
操作的开销非常小,因为它只涉及读数据和写一个极小的索引文件,避免了重写庞大数据文件带来的巨大 I/O 开销。 - 读取高效:在读取时,只是多了一步加载位图的操作,然后就可以直接跳过无效数据,避免了
RowKind
机制中复杂的内存合并计算。
- 普适性:它不依赖主键和合并引擎,因此可以用于所有表类型,包括 Append-Only 表。这使得 Append-Only 表也具备了高效
结论:
DeletionVector
并不是要替代 RowKind
,而是对 Paimon 更新删除能力的一个巨大增强和优化。它将昂贵的“写时复制/合并”(Copy-on-Write / Merge-on-Read)操作,变成了一个轻量级的“写时标记”(Merge-on-Write),极大地提升了 DELETE
/UPDATE
的性能和适用范围,是 Paimon 实现高性能实时湖仓的关键技术之一。