跳到主要内容

主键模型

在需要有数据更新时,可以选择使用主键模型(Unique Key Model)。主键模型可以保证 Key 列的唯一性,当用户插入或更新数据时,新写入的数据会覆盖具有相同 Key 列的旧数据,从而保持最新的数据记录。与其他数据模型相比,主键模型适用于数据的更新场景,可以在插入过程中进行主键级别的更新覆盖。

主键模型有以下特点:

  • 基于主键完成 UPSERT :在插入数据时,主键重复的数据会更新,主键不存在的记录会插入;

  • 基于主键进行去重:主键模型中的 Key 列具有唯一性,会对根据主键列对数据进行去重操作;

  • 可以进行高频数据更新:支持高频数据更新场景,同时平衡数据更新性能与查询性能。

使用场景

  • 高频数据更新:上游 OLTP 数据库维度表高频的进行谁更新,通过主键表可以实时同步上游更新记录,完成高效的 UPSERT 操作;

  • 数据高效去重:如广告投放营销、客户关系管理系统中,需要针对用户做去重操作,使用主键模型,可以保证基于用户 ID 做高效去重操作;

  • 需要部分记录更新:在部分业务场景中,只需要对某几列进行更新,如画像标签场景需要变更频繁改动的动态标签,消费订单场景需要改变交易的状态。通过主键模型部分列更新能力可以完成某几列的变更操作。

实现方式

在 Doris 中主键模型有两种实现方式:

  • 写时合并(merge-on-write):在 1.2 版本后,Doris 中的主键模型默认使用写时合并模式,数据在写入时立即进行相同 Key 的合并,确保每次写入后的数据存储状态是唯一键的最终合并结果,只存储最新结果。写实合并可以很好的兼顾查询与写入的性能,在查询时避免多个版本的数据合并,保证谓词下推到存储层。绝大部分的场景都推荐使用写时合并模型;

  • 读时合并(merge-on-read):在 1.2 版本前,Doris 中的主键模型默认使用读时合并模式,数据在写入时并不进行合并,以增量的方式被追加存储,在 Doris 内保留多个版本。查询或 Compaction 时,会对数据进行相同 Key 的版本合并。读时合并适合写多读少的场景,在查询是需要进行多个版本合并,谓词无法下推,可能会影响到查询速度。

在 Doris 中基于主键模型更新有两种语义:

  • Unique 模型默认的更新语义为整行UPSERT,即 UPDATE OR INSERT,该行数据的 Key 如果存在,则进行更新,如果不存在,则进行新数据插入。在整行UPSERT语义下,即使用户使用 Insert Into 指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充。

  • 部分列更新。如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅文档部分列更新

写时合并

创建写时合并表

在建表时,使用 UNIQUE KEY 关键字可以指定主键表。通过显示开启 enable_unique_key_merge_on_write 属性可以指定写实合并模式。自 Doris 2.1 版本以后,默认开启读时合并:

CREATE TABLE IF NOT EXISTS example_tbl_unique
(
user_id LARGEINT NOT NULL,
user_name VARCHAR(50) NOT NULL,
city VARCHAR(20),
age SMALLINT,
sex TINYINT
)
UNIQUE KEY(user_id, user_name)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES (
"enable_unique_key_merge_on_write" = "true"
);

读时合并

创建读时合并表

在建表时,使用 UNIQUE KEY 关键字可以指定主键表。通过显示关闭 enable_unique_key_merge_on_write 属性可以指定写实合并模式。在 Doris 2.1 版本之前,默认开启读时合并:

CREATE TABLE IF NOT EXISTS example_tbl_unique
(
user_id LARGEINT NOT NULL,
username VARCHAR(50) NOT NULL,
city VARCHAR(20),
age SMALLINT,
sex TINYINT
)
UNIQUE KEY(user_id, username)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES (
"enable_unique_key_merge_on_write" = "false"
);

数据插入与存储

在明细表中,Key 列不仅会进行排序,还会基于 Key 列进行去重,插入数据后,新写入的数据会覆盖 Key 列相同的记录。

unique-key-model-insert

如在上例中,原表中有 4 行数据,在插入两行后,新插入的两行基于主键进行了更新操作:

-- insert into raw data
INSERT INTO example_tbl_unique VALUES
(101, 'Tom', 'BJ', 26, 1),
(102, 'Jason', 'BJ', 27, 1),
(103, 'Juice', 'SH', 20, 2),
(104, 'Olivia', 'SZ', 22, 2);

-- insert into data to update by key
INSERT INTO example_tbl_unique VALUES
(101, 'Tom', 'BJ', 27, 1),
(102, 'Jason', 'SH', 28, 1);

-- check updated data
SELECT * FROM example_tbl_unique;
+---------+----------+------+------+------+
| user_id | username | city | age | sex |
+---------+----------+------+------+------+
| 101 | Tom | BJ | 27 | 1 |
| 102 | Jason | SH | 28 | 1 |
| 104 | Olivia | SZ | 22 | 2 |
| 103 | Juice | SH | 20 | 2 |
+---------+----------+------+------+------+

注意事项

  • 建议在 Doris 1.2.4 以后使用写时合并模式,在 1.2 版本使用写时合并需在 be.conf 中添加配置项 disable_storage_page_cache=false。如不开启该选项可能会对导入性能产生较大影响。2.0 版本后默认开启;

  • Unique 表的实现方式只能在建表时确定,无法通过 schema change 进行修改;

  • 在整行 UPSERT 语义下,即使用户使用 insert into 指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充;

  • 部分列更新。如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅文档部分列更新获取相关使用建议。