大数据框架下,常用的数据更新策略有三种:
COW: copy-on-write, 写时复制;
MOR: merge-on-read, 读时合并;
MOW: merge-on-write, 写时合并;
hudi等数据湖仓框架,常用的是前两种实现数据更新。而Doris则主要用后两种更新数据。
COW
在数据写入的时候,复制一份原来的拷贝,在其基础上添加新数据,创建数据文件的新版本。新版本文件包括旧版本文件的记录以及来自传入批次的记录(全量最新)。
正在读数据的请求,读取的是最近的完整副本,这类似Mysql 的MVCC的思想。
在java的类库中就有一个CopyOnWriteArrayList,而linux的fork子进程的内部机制也是通过COW实现。可以说,COW是比较常用的数据更新方案。
MOR
新插入的数据存储在delta log 中,定期再将delta log合并进行parquet数据文件。读取数据时,会将delta log跟老的数据文件做merge。
这个merge的过程一般是多路归并排序的实现:查询时将重复的 Key 排在一起,并进行聚合操作,其中高版本 Key 的会覆盖低版本的 Key,最终只返回给用户版本最高的那一条记录。
hudi中,数据表的存储类型主要是MOR,参考: Hudi-表的存储类型及比较
MOW
将被覆盖和被更新的数据进行标记删除,同时将新的数据写入新的文件。在查询的时候, 所有被标记删除的数据都会在文件级别被过滤掉,读取出来的数据就都是最新的数据,消除掉了读时合并中的数据聚合过程,并且能够在很多情况下支持多种谓词的下推。
别的大数据框架我没有查到相关的信息,这个的应用主要是在Doris的Unique数据模型中,即通过MOW实现了Unique数据模型下的数据更新。
Doris的MOW的实现方案是: Delete Insert。即在数据写入时通过一个主键索引查找到被覆盖的 Key,将其标记为删除。 参考自微软的 SQL Server 在 2015 年 VLDB 上发表的论文《Real-Time Analytical Processing with SQL Server》中提出的方案。
Delete Insert
这篇论文提出了数据写入时将旧的数据标记删除(使用一个 Delete Bitmap 的数据结构),并将新数据记录在 Delta Store 中,查询时将 Base 数据、Delete Bitmap、Delta Store 中的数据 Merge 起来以得到最新的数据。整体方案如下图所示
其优点是,任何一个有效的主键只存在于一个地方(要么在 Base Data 中,要么在 Delta Store 中),这样就避免了查询过程中的大量归并排序的消耗,同时 Base 数据中的各种丰富的列存索引也仍然有效。
简单来讲,Merge-On-Write 的处理流程是:
- 对于每一条 Key,查找它在 Base 数据中的位置(rowsetid segmentid 行号)
- 如果 Key 存在,则将该行数据标记删除。标记删除的信息记录在 Delete Bitmap中,其中每个 Segment 都有一个对应的 Delete Bitmap
- 将更新的数据写入新的 Rowset 中,完成事务,让新数据可见(能够被查询到)
- 查询时,读取 Delete Bitmap,将被标记删除的行过滤掉,只返回有效的数据
总结
之所以会有这篇文章,主要是想总结一下大数据框架下常用的(准实时/实时)数据更新的常用解决方案,毕竟解决方案是通用的,只是实现方式会有差异。