更新时间:2024-11-29 GMT+08:00

Doris数据模型介绍

基本概念

在Doris中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row即用户的一行数据,Column用于描述一行数据中不同的字段。Column可以分为Key和Value两大类。从业务角度看,Key和Value可以分别对应维度列和指标列。

Doris的数据模型主要分为以下三类:

  • Aggregate
  • Unique
  • Duplicate

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 "用户年龄",

`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"

);

当导入数据时,对于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 "用户年龄",

    `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"

    );

  • 写时合并

    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,

    `sex` 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"

    );

    在开启了写时合并选项的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列)。