Apache Doris——数据表的创建

2023-01-01  本文已影响0人  小波同学

前言—Doris 基本概念

Doris 数据分布

Doris 数据可靠性

元数据层面,Doris采用Paxos协议以及Memory + Checkpoint + Journal的机制来确保元数据的高性能及高可靠。

元数据的每次更新,都会遵照以下几步:

Doris 内部自行管理数据的多副本和自动修复。保证数据的高可用、高可靠。在服务器宕机的情况下,服务依然可用,数据也不会丢失。

一、创建用户和数据库

mysql> create user 'test' identified by 'test';
Query OK, 0 rows affected (0.01 sec)

mysql> create database test;
Query OK, 0 rows affected (0.01 sec)

mysql> grant all on test to test;
Query OK, 0 rows affected (0.01 sec)

二、建表示例

基本语法

CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [database.]table_name
(column_definition1[, column_definition2, ...]
[, index_definition1[, index_definition12,]])
[ENGINE = [olap|mysql|broker|hive]]
[key_desc]
[COMMENT "table comment"];
[partition_desc]
[distribution_desc]
[rollup_index]
[PROPERTIES ("key"="value", ...)]
[BROKER PROPERTIES ("key"="value", ...)];

Doris的建表是一个同步命令,命令返回成功,即表示建表成功。

Doris 支持支持单分区和复合分区两种建表方式。

字段类型

聚合模型在定义字段类型后,可以指定字段的agg_type聚合类型,如果不指定,则该列为key列。否则,该列为value列,agg_type类型包括:SUM、MAX、MIN、REPLACE。

示例

Range分区

CREATE TABLE IF NOT EXISTS test.expamle_range_tbl
(
    `user_id` LARGEINT NOT NULL COMMENT "用户 id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `timestamp` DATETIME 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 "用户最小停留时间"
)
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
PARTITION BY RANGE(`date`)
(
    PARTITION `p201701` VALUES LESS THAN ("2017-02-01"),
    PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
    PARTITION `p201703` VALUES LESS THAN ("2017-04-01")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
PROPERTIES
(
    "replication_num" = "1",
    "storage_medium" = "SSD",
    "storage_cooldown_time" = "2022-06-01 12:00:00"
);

List分区

CREATE TABLE IF NOT EXISTS test.expamle_list_tbl
(
    `user_id` LARGEINT NOT NULL COMMENT "用户 id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
    `city` VARCHAR(20) NOT NULL 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 "用户最小停留时间"
)
ENGINE=olap
AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
PARTITION BY LIST(`city`)
(
    PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
    PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
    PARTITION `p_jp` VALUES IN ("Tokyo")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
PROPERTIES
(
    "replication_num" = "1",
    "storage_medium" = "SSD",
    "storage_cooldown_time" = "2022-06-01 12:00:00"
);

三、数据划分

列定义

以AGGREGATE KEY数据模型为例进行说明。更多数据模型参阅Doris数据模型。
列的基本类型,可以通过在mysql-client中执行HELP CREATE TABLE;查看。AGGREGATE KEY数据模型中,所有没有指定聚合方式(SUM、REPLACE、MAX、MIN)的列视为Key列。而其余则为Value列。
定义列时,可参照如下建议:

分区与分桶

Doris 支持两层的数据划分。第一层是Partition,支持Range和List划分方式。第二层是Bucket(Tablet),仅支持Hash的划分方式。也可以仅使用一层分区,此时,只支持Bucket划分。

Partition分区

Partition列可以指定一列或多列。分区类必须为KEY列。

Range 分区

分区列通常为时间列,以方便的管理新旧数据。不可添加范围重叠的分区。

Partition 指定范围的方式

VALUES LESS THAN (...)仅指定上界,系统会将前一个分区的上界作为该分区的下界,生成一个左闭右开的区间。分区的删除不会改变已存在分区的范围,但可能出现数据空洞。
VALUES [...) 指定同时指定上下界,生成一个左闭右开的区间。

通过VALUES [...) 同时指定上下界比较容易理解。这里举例说明,当使用VALUESLESS THAN (...)语句进行分区的增删操作时,分区范围的变化情况:

p201701: [MIN_VALUE, 2017-02-01)
p201702: [2017-02-01, 2017-03-01)
p201703: [2017-03-01, 2017-04-01)
p201701: [MIN_VALUE, 2017-02-01)
p201702: [2017-02-01, 2017-03-01)
p201703: [2017-03-01, 2017-04-01)
p201705: [2017-04-01, 2017-06-01)
p201701: [MIN_VALUE, 2017-02-01)
p201702: [2017-02-01, 2017-03-01)
p201705: [2017-04-01, 2017-06-01)

注意到p201702和p201705的分区范围并没有发生变化,而这两个分区之间,出现了一个空洞:[2017-03-01, 2017-04-01)。即如果导入的数据范围在这个空洞范围内,是无法导入的。

p201701: [MIN_VALUE, 2017-02-01)
p201705: [2017-04-01, 2017-06-01)

空洞范围变为:[2017-02-01, 2017-04-01)

p201701: [MIN_VALUE, 2017-02-01)
p201702new: [2017-02-01, 2017-03-01)
p201705: [2017-04-01, 2017-06-01)

可以看到空洞范围缩小为:[2017-03-01, 2017-04-01)

p201612: [MIN_VALUE, 2017-01-01)
p201702new: [2017-02-01, 2017-03-01)
p201705: [2017-04-01, 2017-06-01)

即出现了一个新的空洞:[2017-01-01, 2017-02-01)

List 分区

分区列支持BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR数据类型,分区值为枚举值。只有当数据为目标分区枚举值其中之一时,才可以命中分区,不可添加范围重叠的分区。

Partition支持通过VALUES IN (...)来指定每个分区包含的枚举值。下面通过示例说明,进行分区的增删操作时,分区的变化。

p_cn: ("Beijing", "Shanghai", "Hong Kong")
p_usa: ("New York", "San Francisco")
p_jp: ("Tokyo")
p_cn: ("Beijing", "Shanghai", "Hong Kong")
p_usa: ("New York", "San Francisco")
p_jp: ("Tokyo")
p_uk: ("London")
p_cn: ("Beijing", "Shanghai", "Hong Kong")
p_usa: ("New York", "San Francisco")
p_uk: ("London")

Bucket

使用复合分区的场景

多列分区

Doris支持指定多列作为分区列,示例如下:

Range分区

PARTITION BY RANGE(`date`, `id`)
(
PARTITION `p201701_1000` VALUES LESS THAN ("2017-02-01", "1000"),
PARTITION `p201702_2000` VALUES LESS THAN ("2017-03-01", "2000"),
PARTITION `p201703_all` VALUES LESS THAN ("2017-04-01")
)

指定date(DATE类型) 和 id(INT类型) 作为分区列。以上示例最终得到的分区如下:

p201701_1000: [(MIN_VALUE, MIN_VALUE), ("2017-02-01", "1000") )
p201702_2000: [("2017-02-01", "1000"), ("2017-03-01", "2000") )
p201703_all: [("2017-03-01", "2000"), ("2017-04-01", MIN_VALUE))

注意,最后一个分区用户缺省只指定了date列的分区值,所以id列的分区值会默认填充MIN_VALUE。当用户插入数据时,分区列值会按照顺序依次比较,最终得到对应的分区。举例如下(数据 --> 分区):

2017-01-01, 200 --> p201701_1000
2017-01-01, 2000 --> p201701_1000
2017-02-01, 100 --> p201701_1000
2017-02-01, 2000 --> p201702_2000
2017-02-15, 5000 --> p201702_2000
2017-03-01, 2000 --> p201703_all
2017-03-10, 1 --> p201703_all
2017-04-01, 1000 --> 无法导入
2017-05-01, 1000 --> 无法导入

List分区

PARTITION BY LIST(`id`, `city`)
(
PARTITION `p1_city` VALUES IN (("1", "Beijing"), ("1","Shanghai")),
PARTITION `p2_city` VALUES IN (("2", "Beijing"), ("2","Shanghai")),
PARTITION `p3_city` VALUES IN (("3", "Beijing"), ("3","Shanghai"))
)

指定id(INT类型) 和city(VARCHAR类型) 作为分区列。最终得到的分区如下:

p1_city: [("1", "Beijing"), ("1", "Shanghai")]
p2_city: [("2", "Beijing"), ("2", "Shanghai")]
p3_city: [("3", "Beijing"), ("3", "Shanghai")]

当用户插入数据时,分区列值会按照顺序依次比较,最终得到对应的分区。举例如下:
数据 —> 分区

1, Beijing ---> p1_city
1, Shanghai ---> p1_city
2, Shanghai ---> p2_city
3, Beijing ---> p3_city
1, Tianjin ---> 无法导入
4, Beijing ---> 无法导入

PROPERTIES

在建表语句的最后 PROPERTIES 中,可以指定replication_num、storage_medium和storage_cooldown_time三个参数。

replication_num

每个Tablet的副本数量。默认为3,建议保持默认即可。在建表语句中,所有Partition中的Tablet副本数量统一指定。而在增加新分区时,可以单独指定新分区中Tablet的副本数量。

副本数量可以在运行时修改。强烈建议保持奇数。

最大副本数量取决于集群中独立IP的数量(注意不是BE数量)。Doris中副本分布的原则是,不允许同一个Tablet的副本分布在同一台物理机上,而识别物理机即通过IP。所以,即使在同一台物理机上部署了3个或更多BE实例,如果这些BE的IP相同,则依然只能设置副本数为1。

对于一些小,并且更新不频繁的维度表,可以考虑设置更多的副本数。这样在Join查询时,可以有更大的概率进行本地数据Join。

torage_medium & storage_cooldown_time

BE的数据存储目录可以显式的指定为SSD或者HDD(通过.SSD或者.HDD后缀区分)。建表时,可以统一指定所有Partition初始存储的介质。注意,后缀作用是显式指定磁盘介质,而不会检查是否与实际介质类型相符。

默认初始存储介质可通过fe的配置文件fe.conf中指定default_storage_medium=xxx,如果没有指定,则默认为HDD。如果指定为SSD,则数据初始存放在SSD上。

如果没有指定storage_cooldown_time,则默认30天后,数据会从SSD自动迁移到HDD上。如果指定了storage_cooldown_time,则在到达storage_cooldown_time时间后,数据才会迁移。

注意,当指定storage_medium时,如果FE参数enable_strict_storage_medium_check为False该参数只是一个“尽力而为”的设置。即使集群内没有设置SSD存储介质,也不会报错,而是自动存储在可用的数据目录中。 同样,如果SSD介质不可访问、空间不足,都可能导致数据初始直接存储在其他可用介质上。而数据到期迁移到HDD时,如果HDD介质不可访问 、空间不足,也可能迁移失败( 但是会不断尝试)。如果FE参数enable_strict_storage_medium_check为True则当集群内没有设置SSD存储介质时,会报错Failed to find enough host in all backends with storage medium is SSD。

ENGINE

本示例中,ENGINE的类型是olap,即默认的ENGINE类型。在Doris中,只有这个ENGINE类型是由Doris负责数据管理和存储的。其他ENGINE类型,如mysql、broker、es等等,本质上只是对外部其他数据库或系统中的表的映射,以保证Doris可以读取这些数据。而Doris本身并不创建、管理和存储任何非olap ENGINE类型的表和数据。

其他

IF NOT EXISTS表示如果没有创建过该表,则创建。注意这里只判断表名是否存在,而不会判断新建表结构是否与已存在的表结构相同。

参考:
https://blog.csdn.net/qq_37475168/article/details/125570856

上一篇下一篇

猜你喜欢

热点阅读