当用户有数据更新需求时,可以选择使用主键数据模型(Unique)。主键模型能够保证 Key(主键)的唯一性,当用户更新一条数据时,新写入的数据会覆盖具有相同 key(主键)的旧数据。

主键模型提供了两种实现方式:

  • 读时合并 (merge-on-read)。在读时合并实现中,用户在进行数据写入时不会触发任何数据去重相关的操作,所有数据去重的操作都在查询或者 compaction 时进行。因此,读时合并的写入性能较好,查询性能较差,同时内存消耗也较高。
  • 写时合并 (merge-on-write)。在 1.2 版本中,我们引入了写时合并实现,该实现会在数据写入阶段完成所有数据去重的工作,因此能够提供非常好的查询性能。自 2.0 版本起,写时合并已经非常成熟稳定,由于其优秀的查询性能,我们推荐大部分用户选择该实现。自 2.1 版本,写时合并成为 Unique 模型的默认实现。

数据更新的语意

  • Unique 模型默认的更新语意为整行UPSERT,即 UPDATE OR INSERT,该行数据的 key 如果存在,则进行更新,如果不存在,则进行新数据插入。在整行UPSERT语意下,即使用户使用 insert into 指定部分列进行写入,Doris 也会在 Planner 中将未提供的列使用 NULL 值或者默认值进行填充。
  • 部分列更新。如果用户希望更新部分字段,需要使用写时合并实现,并通过特定的参数来开启部分列更新的支持。请查阅数据操作/数据更新部分。

下面以一个典型的用户基础信息表,来看看如何建立读时合并和写时合并的主键模型表。这个表数据没有聚合需求,只需保证主键唯一性。(这里的主键为 user_id + username)。

ColumnNameTypeIsKeyComment
user_idBIGINTYes用户 id
usernameVARCHAR(50)Yes用户昵称
cityVARCHAR(20)No用户所在城市
ageSMALLINTNo用户年龄
sexTINYINTNo用户性别
phoneLARGEINTNo用户电话
addressVARCHAR(500)No用户住址
register_timeDATETIMENo用户注册时间

读时合并

读时合并的建表语句如下:

  1. CREATE TABLE IF NOT EXISTS example_tbl_unique
  2. (
  3. `user_id` LARGEINT NOT NULL COMMENT "用户id",
  4. `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
  5. `city` VARCHAR(20) COMMENT "用户所在城市",
  6. `age` SMALLINT COMMENT "用户年龄",
  7. `sex` TINYINT COMMENT "用户性别",
  8. `phone` LARGEINT COMMENT "用户电话",
  9. `address` VARCHAR(500) COMMENT "用户地址",
  10. `register_time` DATETIME COMMENT "用户注册时间"
  11. )
  12. UNIQUE KEY(`user_id`, `username`)
  13. DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
  14. PROPERTIES (
  15. "replication_allocation" = "tag.location.default: 1"
  16. );

写时合并

写时合并建表语句为:

  1. CREATE TABLE IF NOT EXISTS example_tbl_unique_merge_on_write
  2. (
  3. `user_id` LARGEINT NOT NULL COMMENT "用户id",
  4. `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
  5. `city` VARCHAR(20) COMMENT "用户所在城市",
  6. `age` SMALLINT COMMENT "用户年龄",
  7. `sex` TINYINT COMMENT "用户性别",
  8. `phone` LARGEINT COMMENT "用户电话",
  9. `address` VARCHAR(500) COMMENT "用户地址",
  10. `register_time` DATETIME COMMENT "用户注册时间"
  11. )
  12. UNIQUE KEY(`user_id`, `username`)
  13. DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
  14. PROPERTIES (
  15. "replication_allocation" = "tag.location.default: 1",
  16. "enable_unique_key_merge_on_write" = "true"
  17. );

用户需要在建表时添加下面的 property 来开启写时合并。

  1. "enable_unique_key_merge_on_write" = "true"

主键模型 - 图1警告

在 2.1 版本中,写时合并将会是主键模型的默认方式。

对于新用户,强烈推荐使用 2.0 及其以上版本。在 2.0 版本中,写时合并的性能和稳定性都有大幅的提升和优化。

对于 1.2 的用户

  • 建议使用 1.2.4 及以上版本,该版本修复了一些 bug 和稳定性问题。
  • 在 be.conf 中添加配置项:disable_storage_page_cache=false。不添加该配置项可能会对数据导入性能产生较大影响

使用注意

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

  • 旧的 Merge-on-Read 的实现无法无缝升级到 Merge-on-Write 的实现(数据组织方式完全不同),如果需要改为使用写时合并的实现版本,需要手动执行 insert into unique-mow-table select * from source table 来重新导入。

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

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