暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Doris的数据模型

叶同学专栏 2023-03-30
4817

Doris主要支持的表类型即为数据模型,主要分为3类:

  • Aggregate,聚合模型

  • Unique,主键唯一模型

  • Duplicate,明细模型

建表语法

通用的建表语法如下

CREATE TABLE [IF NOT EXISTS] [database.]table
(
  column_definition_list,
  [index_definition_list]
)
[engine_type]
[keys_type]
[table_comment]
[partition_info]
distribution_desc
[rollup_list]
[properties]
[extra_properties]

常用参数说明:

  • engine_type , 表引擎类型。默认是OLAP ,其它类型的目前支持有 MYSQL、BROKER、HIVE、ICEBERG 、HUDI

    olap引擎第一个字段不能是float, double, string类型,可以使用decimal 或者varchar

  • key_type ,数据模型

    • UPLICATE KEY(默认):其后指定的列为排序列。

    • AGGREGATE KEY:其后指定的列为维度列。

    • UNIQUE KEY:其后指定的列为主键列。

  • table_comment ,表注释

  • partition_desc ,分区信息

  • distribution_desc ,数据分桶定义

    • Hash 分桶 语法:DISTRIBUTED BY HASH (k1[,k2 ...]) [BUCKETS num]
      说明:使用指定的 key 列进行哈希分桶。

    • Random 分桶 语法:DISTRIBUTED BY RANDOM [BUCKETS num]
      说明:使用随机数进行分桶。

  • rollup_list 。建表的同时可以创建多个物化视图(ROLLUP)

  • properties ,表属性设置,有以下参数项

    • replication_num 。副本数,默认为3,新版本使用参数 replication_allocation 指定

    • in_memory  , Doris 是没有内存表 ,这个属性设置成 true
      , Doris 会尽量将该表的数据块缓存在存储引擎的 PageCache 中,已减少磁盘IO。但这个属性不会保证数据块常驻在内存中,仅作为一种尽力而为的标识。

    • compression ,压缩方式默认压缩方式是 LZ4

数据模型

Aggregate

聚合模型 ,与clickhouse的AggregatingMergeTree引擎原理一样,但没有clickhouse支持的聚合函数多

目前支持以下四种聚合方式:

  1. SUM:求和,多行的 Value 进行累加。

  2. REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。

  3. MAX:保留最大值。

  4. MIN:保留最小值。

使用例子

创建一张聚合模型的表,定义了 AGGREGATE KEY 和 AGGREGATE VALUE(last_visit_date,cost,max_dwell_time,min_dwell_time)

CREATE TABLE IF NOT EXISTS 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"
);

多次插入数据然后查看数据内容

insert into example_tbl values(10000,'2017-10-01','广州','18','1','2023-03-24 10:03:35',100,10,20);
select * from example_tbl
10000 2017-10-01 广州 18 1 2023-03-24 10:03:35 100 10 20

insert into example_tbl values(10000,'2017-10-01','广州','18','1','2023-03-24 11:03:35',100,100,200);
select * from example_tbl
10000 2017-10-01 广州 18 1 2023-03-24 11:03:35 200 100 20

在第二次插入后,数据按照我们定义的聚合方式进行了汇总。

也可以对聚合模型的数据进行删除

delete from example_tbl where user_id =10000;

注意,对于删除的语法,这个版本只支持指定的值删除,不支持关联表删除,例如

--
delete from example_tbl
where user_id in(select user_id from example_tbl2 where user_id=10000)
SQL 错误 [1105] [HY000]: errCode = 2, detailMessage = Child of in predicate should be value

--
delete from example_tbl a
using example_tbl2 b
where b.user_id=a.user_id and b.user_id=10000
SQL 错误 [1105] [HY000]: errCode = 2, detailMessage = Syntax error in line 2:

读时合并

聚合模型在数据写入时并在最后的模型聚合会有一定的延迟性,所以查最终数据时,需要与clickhouse的聚合模型方法一样,对最终的模型表再进行聚合操作后的数据才能保证正确。

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 KEY关键字

与下面建表sql一样

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) REPLACE COMMENT "用户所在城市",
  `age` SMALLINT REPLACE COMMENT "用户年龄",
  `sex` TINYINT REPLACE COMMENT "用户性别",
  `phone` LARGEINT REPLACE COMMENT "用户电话",
  `address` VARCHAR(500) REPLACE COMMENT "用户地址",
  `register_time` DATETIME REPLACE COMMENT "用户注册时间"
)
AGGREGATE KEY(`user_id`, `username`)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

使用AGGREGATE聚合模型,指定聚合函数为REPLACE

写时合并

Unique模型的写时合并实现没有聚合模型的局限性 ,可以实现在数据写入时就进行合并,但这功能默认是关闭的,需要添加下面的property来开启 ,且只能在建表时通过指定property的方式打开

"enable_unique_key_merge_on_write" = "true"


Duplicate

明细模型,与clickhouse的MergeTree引擎一样。

DUPLICATE KEY是用来指明底层数据按照那些列进行排序 ,相当于ck的排序键。

DUPLICATE KEY注意事项:

  1. 并且指定的字段顺序要放在建表字段前顺序一致。

  2. 字段类型不能为String

  3. 如果没有指定,默认为第一个

建表示例

drop table table_hash;
CREATE TABLE table_hash
(
  k1 DATE,
  k2 DECIMAL(10, 2) DEFAULT "10.5",
  k3 CHAR(10) COMMENT "string column",
  k4 INT NOT NULL DEFAULT "1" COMMENT "int column"
)
DUPLICATE KEY(k1, k2)
COMMENT "my first table"
PARTITION BY RANGE(k1)
(
  PARTITION p1 VALUES LESS THAN ("2020-02-01"),
  PARTITION p2 VALUES LESS THAN ("2020-03-01"),
  PARTITION p3 VALUES LESS THAN ("2020-04-01")
)
DISTRIBUTED BY HASH(k1) BUCKETS 32
PROPERTIES (
   "replication_num" = "1"
);

插入3行数据

insert into table_hash values('2020-02-01',1.2,'a',1);
insert into table_hash values('2020-02-01',1.3,'a',1);
insert into table_hash values('2020-02-02',1.4,'b',2);

MySQL [test]> select * from table_hash;
+------------+------+------+------+
| k1         | k2   | k3   | k4   |
+------------+------+------+------+
| 2020-02-01 | 1.20 | a   |   1 |
| 2020-02-01 | 1.30 | a   |   1 |
| 2020-02-02 | 1.40 | b   |   2 |
+------------+------+------+------+

删除明细表的数据

  • 如果是分区表,需要指定分区

  • 如果不是分区表,直接delete可以成功

MySQL [test]> delete from table_hash where k4=1; 
ERROR 1105 (HY000): errCode = 2, detailMessage = errCode = 2, detailMessage = This is a range or list partitioned table. You should specify partition in delete stmt, or set delete_without_partition to true
MySQL [test]> delete from table_hash PARTITION p2 where k4=1;
Query OK, 0 rows affected (0.05 sec)
{'label':'delete_311df3bf-38d9-453b-907f-915c3baeb777', 'status':'VISIBLE', 'txnId':'315674'}

MySQL [test]> select * from table_hash;
+------------+------+------+------+
| k1         | k2   | k3   | k4   |
+------------+------+------+------+
| 2020-02-02 | 1.40 | b   |   2 |
+------------+------+------+------+

删除所有数据,要使用truncate

MySQL [test]> delete from table_hash ; 
ERROR 1105 (HY000): errCode = 2, detailMessage = Where clause is not set

MySQL [test]> truncate table table_hash ;
Query OK, 0 rows affected (0.05 sec)

数据模型的选择建议

因为数据模型在建表时就已经确定,且无法修改。所以,选择一个合适的数据模型非常重要

  1. Aggregate 模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。但是该模型对 count(*) 查询很不友好。同时因为固定了 Value 列上的聚合方式,在进行其他类型的聚合查询时,需要考虑语意正确性。

  2. Unique 模型针对需要唯一主键约束的场景,可以保证主键唯一性约束。但是无法利用 ROLLUP 等预聚合带来的查询优势。

    1. 对于聚合查询有较高性能需求的用户,推荐使用自1.2版本加入的写时合并实现。

    2. Unique 模型仅支持整行更新,如果用户既需要唯一主键约束,又需要更新部分列(例如将多张源表导入到一张 doris 表的情形),则可以考虑使用 Aggregate 模型,同时将非主键列的聚合类型设置为 REPLACE_IF_NOT_NULL。

  3. Duplicate 适合任意维度的 Ad-hoc 查询。虽然同样无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(只读取相关列,而不需要读取所有 Key 列)。


文章转载自叶同学专栏,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论