Doris【数据模型】

本文详细介绍了Doris中的数据模型,包括明细模型(Duplicate)、唯一性模型(Unique)和聚合模型(Aggregate),以及如何通过FlinkSql和DataGen进行数据操作和写入,强调了不同模型在性能和适用场景上的特点。
摘要由CSDN通过智能技术生成

一、数据模型简介

在 Doris 中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。

Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。Doris的key列是建表语句中指定的列,建表语句中的关键字'unique key'或'aggregate key'或'duplicate key'后面的列就是 Key 列,除了 Key 列剩下的就是 Value 列。

Doris 的数据模型主要分为3类:

  • Aggregate
  • Unique
  • Duplicate

下面我们分别介绍。

二、明细模型【Duplicate】

建表

CREATE TABLE order_info (
  order_date date NOT NULL COMMENT '下单日期',
  order_id int(11) NOT NULL COMMENT '订单id',
  buy_num tinyint(4) NULL COMMENT '购买件数',
  user_id int(11) NULL COMMENT '[-1223371, 1223371]',
  create_time datetime NULL COMMENT '创建时间',
  update_time datetime NULL COMMENT '更新时间'
) ENGINE=OLAP
DUPLICATE KEY(order_date, order_id)
COMMENT 'OLAP'
DISTRIBUTED BY HASH(order_id) BUCKETS 2
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);

通过Flink Sql自带的datagen生成测试数据:

CREATE TABLE order_info_source (
    order_date DATE,
    order_id     INT,
    buy_num      INT,
    user_id      INT,
    create_time  TIMESTAMP(3),
    update_time   TIMESTAMP(3)
) WITH (
  'connector' = 'datagen',
  'rows-per-second' =  '10',
  'fields.order_id.min' = '99999',
  'fields.order_id.max' = '10001',
  'fields.user_id.min' = '10001',
  'fields.user_id.max' = '90001',
  'fields.buy_num.min' = '1',
  'fields.buy_num.max' = '200',
  'number-of-rows' = '10000'
)

datagen参数

'rows-per-second' = '1000' : 每秒发送1000条数据
'fields.order_id.min' = '99999': order_id最小值为99999
'fields.order_id.max' = '10001': order_id最大值为10001
'fields.user_id.min' = '10001': user_id最小值为10001
'fields.user_id.max' = '90001': user_id最大值为90001
'fields.buy_num.min' = '1': buy_num最小值为1
'fields.buy_num.max' = '200': buy_num最大值为200
'number-of-rows' = '10000': 共发送10000条数据, 不设置的话会无限量发送数据

参考文档:DataGen | Apache FlinkDataGen SQL Connector # Scan Source: Bounded Scan Source: UnBoundedThe DataGen connector allows for creating tables based on in-memory data generation. This is useful when developing queries locally without access to external systems such as Kafka. Tables can include Computed Column syntax which allows for flexible record generation.The DataGen connector is built-in, no additional dependencies are required.Usage # By default, a DataGen table will create an unbounded number of rows with a random value for each column.icon-default.png?t=N7T8https://nightlies.apache.org/flink/flink-docs-release-1.15/docs/connectors/table/datagen/

注册Sink 表

CREATE TABLE order_info_sink (  
order_date DATE,  
order_id INT,  
buy_num INT,
user_id INT,
create_time TIMESTAMP(3),
update_time TIMESTAMP(3)
)  
WITH (
'connector' = 'doris',   
'fenodes' = '192.168.56.XXX:8030',   
'table.identifier' = 'test.order_info_example',   
'username' = 'test123',   
'password' = 'passwd123',   
'sink.label-prefix' = 'sink_doris_label_8'
)

写入Sink 表

insert into order_info_sink select * from order_info_source

通过mysql客户端查看Doris Sink表数据 

mysql> select * from  test.order_info_example limit 10;
+------------+----------+---------+---------+---------------------+---------------------+
| order_date | order_id | buy_num | user_id | create_time         | update_time         |
+------------+----------+---------+---------+---------------------+---------------------+
| 2024-02-22 |    30007 |      10 |   10560 | 2024-02-22 07:42:21 | 2024-02-22 07:42:21 |
| 2024-02-22 |    30125 |      16 |   17591 | 2024-02-22 07:42:26 | 2024-02-22 07:42:26 |
| 2024-02-22 |    30176 |      17 |   10871 | 2024-02-22 07:42:24 | 2024-02-22 07:42:24 |
| 2024-02-22 |    30479 |      16 |   19847 | 2024-02-22 07:42:25 | 2024-02-22 07:42:25 |
| 2024-02-22 |    30128 |      16 |   19807 | 2024-02-22 07:42:24 | 2024-02-22 07:42:24 |
| 2024-02-22 |    30039 |      13 |   18237 | 2024-02-22 07:42:28 | 2024-02-22 07:42:28 |
| 2024-02-22 |    30060 |      10 |   18309 | 2024-02-22 07:42:24 | 2024-02-22 07:42:24 |
| 2024-02-22 |    30246 |      18 |   10855 | 2024-02-22 07:42:24 | 2024-02-22 07:42:24 |
| 2024-02-22 |    30288 |      19 |   12347 | 2024-02-22 07:42:26 | 2024-02-22 07:42:26 |
| 2024-02-22 |    30449 |      17 |   11488 | 2024-02-22 07:42:23 | 2024-02-22 07:42:23 |
+------------+----------+---------+---------+---------------------+---------------------+
10 rows in set (0.05 sec)

三、Unique模型

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

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

  • 读时合并(merge-on-read)。在读时合并实现中,用户在进行数据写入时不会触发任何数据去重相关的操作,所有数据去重的操作都在查询或者compaction时进行。因此,读时合并的写入性能较好,查询性能较差,同时内存消耗也较高。
  • 写时合并(merge-on-write)。在1.2版本中,我们引入了写时合并实现,该实现会在数据写入阶段完成所有数据去重的工作,因此能够提供非常好的查询性能。

Unique建表

CREATE TABLE IF NOT EXISTS example_db.example_tbl_unique
(
    `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"
);

 这是一个典型的用户基础信息表。这类数据没有聚合需求,只需保证主键唯一性。(这里的主键为 user_id + username)。

写时合并

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

【注意】

  1. Unique表的实现方式只能在建表时确定,无法通过schema change进行修改。
  2. 旧的Merge-on-read的实现无法无缝升级到Merge-on-write的实现(数据组织方式完全不同),如果需要改为使用写时合并的实现版本,需要手动执行insert into unique-mow-table select * from source table.

四、聚合模型【Aggregate】 

CREATE DATABASE IF NOT EXISTS example_db;

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

表中的列按照是否设置了 AggregationType,分为 Key (维度列) 和 Value(指标列)。没有设置 AggregationType 的,如 user_iddateage ... 等称为 Key,而设置了 AggregationType 的称为 Value

当我们导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的 AggregationType 进行聚合。 AggregationType 目前有以下几种聚合方式和agg_state:

  1. SUM:求和,多行的 Value 进行累加。
  2. REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
  3. MAX:保留最大值。
  4. MIN:保留最小值。
  5. REPLACE_IF_NOT_NULL:非空值替换。和 REPLACE 的区别在于对于null值,不做替换。
  6. HLL_UNION:HLL 类型的列的聚合方式,通过 HyperLogLog 算法聚合。
  7. BITMAP_UNION:BIMTAP 类型的列的聚合方式,进行位图的并集聚合。

数据导入

insert into example_db.example_tbl_agg1 values
(10000,"2017-10-01","北京",20,0,"2017-10-01 06:00:00",20,10,10),
(10000,"2017-10-01","北京",20,0,"2017-10-01 07:00:00",15,2,2),
(10001,"2017-10-01","北京",30,1,"2017-10-01 17:05:45",2,22,22),
(10002,"2017-10-02","上海",20,1,"2017-10-02 12:59:12",200,5,5),
(10003,"2017-10-02","广州",32,0,"2017-10-02 11:20:00",30,11,11),
(10004,"2017-10-01","深圳",35,0,"2017-10-01 10:00:15",100,3,3),
(10004,"2017-10-03","深圳",35,0,"2017-10-03 10:20:22",11,6,6);

 查询数据

 五、数据模型的选择建议

因为数据模型在建表时就已经u企鹅人,且无法修改。所以,选择一个何时的数据模型非常重要。

  1. Aggregate模型可以通过预聚合,极大的降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表查询场景。但是该模型对count(*)查询不是很友好。同时因为固定了Value列上的聚合方式,在进行其他类型的聚合查询时,需要考虑语意正确性。
  2. Unique模型针对需要唯一主键约束的场景,可以保证主键唯一性约束。但是无法利用RollUp等预聚合带来的查询优势。对于聚合查询有较高性能需求的用户,推荐使用1.2版本加入的写时合并实现。
  3. Duplicate适合任意维度的ad-hoc查询。虽然同样无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(只读取相关列,而不需要读取所有的列)。
  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Doris是一个开源的分布式数据仓库,支持实时数据分析和查询。在选择Doris数据模型时,需要考虑以下几个因素: 1. 数据结构和查询需求: Doris支持两种主要的数据模型:OLAP(Online Analytical Processing)和OLTP(Online Transaction Processing)。OLAP模型适用于复杂的分析查询,支持大规模聚合、多维分析和快速查询。OLTP模型适用于实时的事务处理,支持高并发、低延迟的读写操作。根据实际的业务需求和查询场景,选择适合的数据模型。 2. 数据规模和性能要求: Doris可以处理大规模的数据集,并提供高性能的查询和分析能力。如果需要处理海量数据,并且对查询性能有较高的要求,可以选择Doris的OLAP模型。它使用列存储和多维索引等技术,可以实现更快速的查询响应。 3. 数据更新频率: 如果数据更新频率较高,例如每秒或每分钟都会有大量的数据写入,那么OLTP模型可能更适合。它支持实时的数据写入和查询,适合需要快速响应和实时分析的场景。而如果数据更新频率较低,以批量或定期方式进行更新,OLAP模型可能更适合。 4. 数据一致性和可靠性: Doris提供了强一致性和高可靠性的数据存储和处理能力。根据业务的要求,选择适当的数据模型以确保数据的一致性和可靠性。 总结: 在选择Doris数据模型时,需要考虑数据结构和查询需求、数据规模和性能要求、数据更新频率以及数据一致性和可靠性等因素。根据实际情况选择适合的OLAP或OLTP模型,以满足业务需求并获得良好的性能和可扩展性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大数据松松

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值