---
url: /doris/data-model.md
description: Doris有三个数据模型，分别为明细模型 Duplicate、主键模型 Unique、聚合模型 Aggregate
---

# Doris的三个数据模型

本文主要介绍Doris的三个数据模型：

* 明细模型 Duplicate
* 主键模型 Unique
* 聚合模型 Aggregate

根据业务场景选择合适的数据模型则至关重要，所以希望大家在学习Doris的时候可以多多理解，熟练掌握三种模型的应用场景。

## 行（Row）和列（Column）

在介绍数据模型之前，需要先了解在Doris中如何描述一张表：

* 一张表包含行和列，行表示用户的一行数据，列则表示一行数据中不同的列
* 列则分为两类：Key 和 Value，Key列是建表语句中通过关键字`unique key`或`aggregate key`或`duplicate key`指定的列，除了Key列，其它的列都是Value列

## 明细模型

明细模型适用于既没有主键又没有聚合需求的数据。

明细模型的特点：

* 默认模型，即在建表时没有指定Unique、Aggregate或Duplicate时，就会默认创建一个明细模型的表

* Duplicate Key用来指明数据存储时按照哪些列进行排序，如果不指定则会自动按照一定规则选择排序列，排序列一般选择2-4列即可

* 如果没有排序需求，也可以通过`"enable_duplicate_without_keys_by_default" = "true"`配置表属性来取消排序

* 不会对数据进行聚合处理，即使两行数据完全一样也会全部保留

```sql
CREATE TABLE IF NOT EXISTS example_tbl_duplicate_without_keys_by_default
(
    `timestamp` DATETIME NOT NULL COMMENT "日志时间",
    `type` INT NOT NULL COMMENT "日志类型",
    `error_code` INT COMMENT "错误码",
    `error_msg` VARCHAR(1024) COMMENT "错误详细信息",
    `op_id` BIGINT COMMENT "负责人id",
    `op_time` DATETIME COMMENT "处理时间"
)
# 可以通过DUPLICATE KEY来指定排序列
# DUPLICATE KEY(`timestamp`, `type`, `error_code`)
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
# 可以通过以下配置来取消排序列
# "enable_duplicate_without_keys_by_default" = "true"
);
```

## 主键模型

主键模型适用于有更新需求的数据。主键模型能够保证Key（主键）的唯一性，当用户更新一条数据时，新写入的数据回覆盖具有相同Key（主键）的旧数据。

* 使用UNIQUE KEY指定主键，可以有多个

* 有两种方式：读时合并（merge-on-read）和写时合并（merge-on-write）。读时合并是在写入数据时不会触发任何数据去重的操作，而是在查询或compacton时进行。写时合并是在数据写入阶段就完成所有数据去重的工作。读时合并具有更好的写入性能，而写入合并具有更好的查询性能。新版本默认使用写时合并。

* 默认整行更新，也可以配置成部分列更新。

```sql
CREATE TABLE IF NOT EXISTS example_tbl_unique_merge_on_write
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `phone` LARGEINT COMMENT "用户电话",
    `address` VARCHAR(500) COMMENT "用户地址",
    `register_time` DATETIME COMMENT "用户注册时间"
)
# 指定主键
UNIQUE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
# 新版本默认为写时合并，可以通过此配置改为读时合并
"enable_unique_key_merge_on_write" = "false"
);
```

## 聚合模型

聚合模型适用于有聚合需求的数据。

* 通过AGGREGATE KEY指定Key列（维度列），而设置了AggregationType的列则为Value列（指标列）。

* 写入数据时，对于Key列相同的行会聚合成一趟，而Value列会按照设置的 AggregationType进行聚合

聚合方式和agg\_state：

* SUM

求和，多行的 Value 进行累加。

* REPLACE

替代，下一批数据中的 Value 会替换之前导入过的行中的 Value。

* MAX

保留最大值。

* MIN

保留最小值。

* REPLACE\_IF\_NOT\_NULL

非空值替换。和 REPLACE 的区别在于对于 null 值，不做替换。

* HLL\_UNION

HLL 类型的列的聚合方式，通过 HyperLogLog 算法聚合。

* BITMAP\_UNION

BIMTAP 类型的列的聚合方式，进行位图的并集聚合。

```sql
CREATE TABLE IF NOT EXISTS example_tbl_agg1
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `city` VARCHAR(20) COMMENT "用户所在城市",
    `age` SMALLINT COMMENT "用户年龄",
    `sex` TINYINT COMMENT "用户性别",
    `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
    `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
    `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
    `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
)
# 指定维度列
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
```

## 进阶

本文中涉及到的三种数据模型在其它类似的数据产品中也会有所涉及，所以熟练掌握这三种模型的选择和使用技巧，会有更大的帮助。如果你需要进一步学习，可以关注：遇码，回复**doris**获取官方文档。
