
# Doris数据模型概述
#### 基本概念
在Doris中，数据以表（Table）的形式进行逻辑上的描述。 一张表包括行（Row）和列（Column），Row即用户的一行数据，Column用于描述一行数据中不同的字段。Column可以分为Key和Value两大类，从业务角度看，Key和Value可以分别对应维度列和指标列。
Doris的数据模型主要分为以下三类：
- Aggregate
- Unique
- Duplicate
更多Doris数据模型介绍请参见[Doris数据模型](https://doris.apache.org/zh-CN/docs/table-design/data-model/overview)。在MySQL客户端连接Doris后创建表具体操作请参见[快速使用Doris](https://support.huaweicloud.com/cmpntguide-lts-mrs/mrs_01_249185.html)。
#### Aggregate模型
建Aggregate模型表语句示例如下：
```
CREATE TABLE IF NOT EXISTS example_db.example_tbl
(
`user_id` LARGEINT NOT NULL COMMENT "用户id",
`date` DATE NOT NULL COMMENT "数据灌入日期时间",
`city` VARCHAR(20) COMMENT "用户所在城市",
`age` SMALLINT COMMENT "用户年龄",
`gender` 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`, `gender`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
```
- 当导入数据时，对于Key列相同的行会聚合成一行，而Value列会按照设置的AggregationType进行聚合。 AggregationType目前有以下四种聚合方式：
  - SUM：求和，多行的Value进行累加。
  
  - REPLACE：替代，下一批数据中的Value会替换之前导入过的行中的Value。
  
  - MAX：保留最大值。
  
  - MIN：保留最小值。
   
- 表中的列按照是否设置了AggregationType，分为Key (维度列) 和Value（指标列）。例如，没有设置AggregationType的，如user_id、date、age等称为Key，而设置了AggregationType的称为Value。
 
#### Unique模型
- 读时合并 这类表没有聚合需求，只需保证主键（user_id和username）的唯一性。且Unique模型的读时合并实现完全可以用Aggregate模型中的REPLACE方式替代。建表示例如下：
  ```
  CREATE TABLE IF NOT EXISTS example_db.example_tbl
  (
  `user_id` LARGEINT NOT NULL COMMENT "用户id",
  `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
  `city` VARCHAR(20) COMMENT "用户所在城市",
  `age` SMALLINT COMMENT "用户年龄",
  `gender` 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"
  );
  ```
  
 
- 写时合并 Unique模型的写时合并实现与Aggregate模型是完全不同的两种模型，查询性能更接近于Duplicate模型，在有主键约束需求的场景上相比Aggregate模型有较大的查询性能优势，适用于在聚合查询以及需要用索引过滤大量数据的查询场景。
  可以在建表时指定如下property方式开启Unique模型：
  ```
  "enable_unique_key_merge_on_write" = "true"
  ```
  例如：
  ```
  CREATE TABLE IF NOT EXISTS example_db.example_tbl
  (
  `user_id` LARGEINT,
  `username` VARCHAR(50) NOT NULL,
  `city` VARCHAR(20),
  `age` SMALLINT,
  `gender` TINYINT,
  `phone` LARGEINT,
  `address` VARCHAR(500),
  `register_time` DATETIME
  )
  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" = "true"
  );
  ```
  ![](https://support.huaweicloud.com/cmpntguide-lts-mrs/public_sys-resources/note_3.0-zh-cn.png)
  在开启了写时合并选项的Unique表中，数据在导入阶段就会将被覆盖和被更新的数据进行标记删除，同时将新的数据写入到新的文件。在查询时，所有被标记删除的数据都会在文件级别被过滤，读取出来的数据是最新的数据，消除了读时合并中数据聚合的过程，并且支持多种谓词的下推，因此在聚合查询场景下能带来较大的性能提升。
  
#### Duplicate模型
数据既没有主键，也没有聚合需求时，可以使用Duplicate数据模型建表。Duplicate模型数据完全按照导入文件中的数据进行存储，不会有任何聚合。即使两行数据完全相同，也都会保留。 而在建表语句中指定的**DUPLICATE KEY**，只是用来指明底层数据按照指定的列进行排序。
建Duplicate模型​表语句如下：
```
CREATE TABLE IF NOT EXISTS example_db.example_tbl
(
`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(`timestamp`, `type`, `error_code`)
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
```
#### Key列
Duplicate、Aggregate、Unique模型，都会在建表时指定Key列，区别为：
- Duplicate模型：表的Key列只是**排序列**，并非起到唯一标识的作用。
- Aggregate、Unique模型：这两种聚合类型的表，Key列是兼顾**排序列** 和**唯一标识列** ，是真正意义上的**Key列**。
 
#### 数据模型的选择建议**​**
因为数据模型在建表时就已经确定，且无法修改。所以，选择一个合适的数据模型非常重要。
- Aggregate模型可以通过预聚合，极大地降低聚合查询时所需扫描的数据量和查询的计算量，适合有固定模式的报表类查询场景，但是该模型不适用于**count(\*)**查询。同时因为固定了Value列上的聚合方式，在进行其他类型的聚合查询时，需要考虑语义正确性。
- Unique模型针对需要唯一主键约束的场景，可以保证主键唯一性约束。但是无法利用ROLLUP等预聚合带来的查询优势。
  - 对于聚合查询有较高性能需求的用户，推荐使用写时合并实现。
  
  - Unique模型仅支持整行更新，如果用户既需要唯一主键约束，又需要更新部分列（例如将多张源表导入到一张Doris表的场景），则可以考虑使用Aggregate模型，同时将非主键列的聚合类型设置为**REPLACE_IF_NOT_NULL**。
   
- Duplicate适合任意维度的Ad-hoc查询。虽然无法利用预聚合的特性，但是不受聚合模型的约束，可以发挥列存模型的优势（只读取相关列，而不需要读取所有Key列）。
 
