数据更新
Doris 中存储的数据都是以追加(Append)的方式进入系统,这意味着所有已写入的数据是不可变更的。
所以 Doris 采用标记的方式来实现数据更新的目的。即在一批更新数据中,将之前的数据标记为删除,并写入新的数据。
在读取过程中,Doris 会自动处理这些标记数据(Merge-on-Read),保证用户读取到的是最新的数据。同时,Doris 后台的数据合并(Compaction)线程也会不断的对数据进行合并,消除标记数据,以减少在读取过程中需要进行的合并操作,加速查询。
大部分对数据修改的场景仅适用于 Unique Key 数据模型,因为只有该模型可以保证主键的唯一性,从而支持按主键对数据进行更新。
本文档主要介绍如何使用 Unique Key 数据模型来进行数据更新操作。
数据更新
关于 UNIQUE KEY 的说明,请参阅相关文档。这里不再赘述。下面仅举例说明。
-
创建一张 UNIQUE KEY 模型的表
CREATE TABLE order_table ( order_id BIGINT, order_type VARCHAR(8), order_status VARCHAR(32) ) UNIQUE KEY(order_id) DISTRIBUTED BY HASH(order_id) BUCKETS 8;
-
导入第一批数据
1000, TYPE#1, PAID 1001, TYPE#2, PENDING 1002, TYPE#3, PAID
-
将 ID 为 1001 的订单条目的
order_status
字段修改为PAID
,则需导入以下数据:1001, TYPE#2, PAID
Doris 会根据主键 1001,则读取或者后台合并过程中,将这个条目的
order_status
字段替换为 PAID。
更新部分字段
在上一个例子中,我们仅需要更新 order_status
字段,但是导入的数据中却需要包含 order_type
字段。
在某些场景下,用户无法获取全列数据,仅知道主键和部分要更新的字段的值。在这种情况下,我们可以通过 REPLACE_IF_NOT_NULL
这种聚合方式来实现。
REPLACE_IF_NOT_NULL
表示,当遇到 null
值则不更新。举例如下:
-
创建一张 AGGREGATE KEY 模型的表,使用
REPLACE_IF_NOT_NULL
方式。CREATE TABLE order_table ( order_id BIGINT, order_type VARCHAR(8) REPLACE_IF_NOT_NULL, order_status VARCHAR(32) REPLACE_IF_NOT_NULL ) AGGREGATE KEY(order_id) DISTRIBUTED BY HASH(order_id) BUCKETS 8;
注意这里我们需要使用
AGGREGATE KEY
数据模型,并且将所有 Value 列的聚合方式设置为REPLACE_IF_NOT_NULL
。 -
导入第一批数据
1000, TYPE#1, PAID 1001, TYPE#2, PENDING 1002, TYPE#3, PAID
-
将 ID 为 1001 的订单条目的
order_status
字段修改为PAID
,则需导入以下数据:1001, \N, PAID
原始数据中的
\N
即表示 null。Doris 会根据主键 1001,则读取或者后台合并过程中,将这个条目的order_status
字段替换为 PAID。并且因为order_type
字段为 null,所以该字段不会被替换。
更新顺序
Doris 内部仅能够保证两个批次的导入数据中,后一批次的数据覆盖更新前一批次的数据。但是如果在同一批次数据中,如果出现主键相同的多行记录,Doris 是无法识别哪一条才是最终生效数据的。
假设某一批次的导入数据如下:
1000, TYPE#1, PENDING
1001, TYPE#2, PENDING
1000, TYPE#3, PAID
注意第一行和第三行主键相同,因此无法确定哪一条会生效,用户最终查询 1000 这个订单的状态,可能为 PENDING
,也可能为 PAID
。
要解决这个问题,需要业务侧保证在同一批次数据中,没有主键相同的行。或者需参考 Sequence Column 对数据进行适配。