前言
Apache Doris 支持标准 SQL 语法,采用 MySQL 网络连接协议,高度兼容 MySQL 相关生态。因此,在数据类型支持方面,尽可能对齐 MySQL 相关数据类型。
数据表设计
1.数据类型
Apache Doris 支持的数据类型比较丰富,完整的类型可以通过官网(https://doris.incubator.apache.org/zh-CN/docs/table-design/data-type
)去了解,我这里只贴了一部分常用的
类型名 | 存储空间(字节) | 描述 |
---|---|---|
BOOLEAN | 1 | 布尔值,0 代表 false,1 代表 true。 |
TINYINT | 1 | 有符号整数,范围 [-128, 127]。 |
SMALLINT | 2 | 有符号整数,范围 [-32768, 32767]。 |
INT | 4 | 有符号整数,范围 [-2147483648, 2147483647] |
BIGINT | 8 | 有符号整数,范围 [-9223372036854775808, 9223372036854775807]。 |
LARGEINT | 16 | 有符号整数,范围 [-2^127 + 1 ~ 2^127 - 1]。 |
FLOAT | 4 | 浮点数,范围 [-3.410^38 ~ 3.410^38]。 |
DOUBLE | 8 | 浮点数,范围 [-1.7910^308 ~ 1.7910^308]。 |
DECIMAL | 4/8/16 | 高精度定点数,格式:DECIMAL(M[,D])。其中,M 代表一共有多少个有效数字(precision),D 代表小数位有多少数字(scale)。有效数字 M 的范围是 [1, 38],小数位数字数量 D 的范围是 [0, precision]。0 < precision <= 9 的场合,占用 4 字节。9 < precision <= 18 的场合,占用 8 字节。16 < precision <= 38 的场合,占用 16 字节 |
DATE | 16 | 日期类型,目前的取值范围是 [‘0000-01-01’, ‘9999-12-31’],默认的打印形式是 ‘yyyy-MM-dd’。 |
DATETIME | 16 | 日期时间类型,格式:DATETIME([P])。可选参数 P 表示时间精度,取值范围是 [0, 6],即最多支持 6 位小数(微秒)。不设置时为 0。取值范围是 [‘0000-01-01 00:00:00[.000000]’, ‘9999-12-31 23:59:59[.999999]’]。打印的形式是 ‘yyyy-MM-dd HH:mm:ss.SSSSSS’。 |
CHAR | M | 定长字符串,M 代表的是定长字符串的字节长度。M 的范围是 1-255。 |
VARCHAR | 不定长 | 变长字符串,M 代表的是变长字符串的字节长度。M 的范围是 1-65533。变长字符串是以 UTF-8 编码存储的,因此通常英文字符占 1 个字节,中文字符占 3 个字节。 |
STRING | 不定长 | 变长字符串,默认支持 1048576 字节(1MB),可调大到 2147483643 字节(2GB)。可通过 BE 配置 string_type_length_soft_limit_bytes 调整。String 类型只能用在 Value 列,不能用在 Key 列和分区分桶列。 |
HLL | 不定长 | HLL 是模糊去重,在数据量大的情况性能优于 Count Distinct。HLL 的误差通常在 1% 左右,有时会达到 2%。HLL 不能作为 Key 列使用,建表时配合聚合类型为 HLL_UNION。用户不需要指定长度和默认值。长度根据数据的聚合程度系统内控制。HLL 列只能通过配套的 hll_union_agg、hll_raw_agg、hll_cardinality、hll_hash 进行查询或使用。 |
BITMAP | 不定长 | Bitmap 类型的列可以在 Aggregate 表、Unique 表或 Duplicate 表中使用。在 Unique 表或 Duplicate 表中使用时,其必须作为非 Key 列使用。在 Aggregate 表中使用时,其必须作为非 Key 列使用,且建表时配合的聚合类型为 BITMAP_UNION。用户不需要指定长度和默认值。长度根据数据的聚合程度系统内控制。BITMAP 列只能通过配套的 bitmap_union_count、bitmap_union、bitmap_hash、bitmap_hash64 等函数进行查询或使用。 |
2.数据模型
在 Doris 中,数据以表(Table)的形式进行逻辑上的描述。一张表包括行(Row)和列(Column)
。Row 即用户的一行数据,Column 用于描述一行数据中不同的字段。
Column 可以分为两大类:Key 和 Value
。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。Doris 的 Key 列是建表语句中指定的列,建表语句中的关键字 unique key 或 aggregate key 或 duplicate key 后面的列就是 Key 列,除了 Key 列剩下的就是 Value 列。
Doris 的数据模型主要分为 3 类:
-
明细模型(Duplicate Key Model):允许指定的 Key 列重复;适用于必须保留所有原始数据记录的情况。
-
主键模型(Unique Key Model):每一行的 Key 值唯一;可确保给定的 Key 列不会存在重复行。
-
聚合模型(Aggregate Key Model):可根据 Key 列聚合数据;通常用于需要汇总或聚合信息(如总数或平均值)的情况
明细模型
明细模型,也称为 Duplicate 数据模型。在明细数据模型中,数据按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留
。而在建表语句中指定的 Duplicate Key,只是用来指明数据存储按照哪些列进行排序。在 Duplicate Key 的选择上,建议选择前 2-4 列即可。案例:
create database db;
CREATE TABLE IF NOT EXISTS db.example_tbl_by_default
(
`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") -- 默认就是明细模型,会安装这2列进行排序
DISTRIBUTED BY HASH(`type`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
-- 查看表结构
> desc db.example_tbl_by_default;
简单理解就是:我往Doris中插入了10条数据,那么Doris中就会有10条数据,明细模型并不会对数据做聚合处理。会保留原生的数据
主键模型
当用户有数据更新需求时,可以选择使用主键数据模型(Unique)。主键模型能够保证 Key(主键)的唯一性
,当用户更新一条数据时,新写入的数据会覆盖具有相同 key(主键)的旧数据
。
主键模型提供了两种实现方式:
- 读时合并 (merge-on-read)。在读时合并实现中,用户在进行数据写入时不会触发任何数据去重相关的操作,所有数据
去重的操作都在查询或者 compaction 时进行
。因此,读时合并的写入性能较好,查询性能较差,同时内存消耗也较高。 - 写时合并 (merge-on-write)。在 1.2 版本中,我们引入了写时合并实现,该实现会在
数据写入阶段完成所有数据去重的工作
,因此能够提供非常好的查询性能。自 2.0 版本起,写时合并已经非常成熟稳定,由于其优秀的查询性能,我们推荐大部分用户选择该实现。自 2.1 版本,写时合并成为 Unique 模型的默认实现。
读时合并的建表语句如下:
CREATE TABLE IF NOT EXISTS 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`) --可以理解为:按照这2列创建唯一索引,默认读时合并
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
写时合并建表语句为:
CREATE TABLE IF NOT EXISTS 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`) --可以理解为:按照这2列创建唯一索引,默认读时合并
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1",
"enable_unique_key_merge_on_write" = "true" --指定为写时合并
);
用户需要在建表时添加下面的 property 来开启写时合并 : “enable_unique_key_merge_on_write” = “true”
聚合模型
聚合数据模型,也称为 Aggregate 数据模型,其实聚合模型就是在写入数据的时候按照某些字段对数据进行统计,比如:最大值max ,最小值 min等。AggregationType 目前有以下几种聚合方式和 agg_state:
- SUM:求和,多行的 Value 进行累加。
- REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
- MAX:保留最大值。
- MIN:保留最小值。
- REPLACE_IF_NOT_NULL:非空值替换。和 REPLACE 的区别在于对于 null 值,不做替换
- HLL_UNION:HLL 类型的列的聚合方式,通过 HyperLogLog 算法聚合。
- BITMAP_UNION:BIMTAP 类型的列的聚合方式,进行位图的并集聚合。
CREATE TABLE IF NOT EXISTS 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`) -- 指定聚合的key列(维度列)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
PROPERTIES (
"replication_allocation" = "tag.location.default: 1"
);
表中的列按照是否设置了 AggregationType,分为 Key (维度列) 和 Value(指标列)。没有设置 AggregationType 的 user_id、date、age、sex 称为 Key,而设置了 AggregationType 的称为 Value。当导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的 AggregationType 进行聚合
。
比如导入下面的数据
insert into 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);
那么当这批数据正确导入到 Doris 中后,Doris 中最终存储如下:
user_id | date | city | age | sex | last_visit_date | cost | max_dwell_time | min_dwell_time |
---|---|---|---|---|---|---|---|---|
10000 | 2017/10/1 | 北京 | 20 | 0 | 2017/10/1 7:00 | 35 | 10 | 2 |
10001 | 2017/10/1 | 北京 | 30 | 1 | 2017/10/1 17:05 | 2 | 22 | 22 |
10002 | 2017/10/2 | 上海 | 20 | 1 | 2017/10/2 12:59 | 200 | 5 | 5 |
10003 | 2017/10/2 | 广州 | 32 | 0 | 2017/10/2 11:20 | 30 | 11 | 11 |
10004 | 2017/10/1 | 深圳 | 35 | 0 | 2017/10/1 10:00 | 100 | 3 | 3 |
10004 | 2017/10/3 | 深圳 | 35 | 0 | 2017/10/3 10:20 | 11 | 6 | 6 |
可以看到,用户 10000 只剩下了一行聚合后的数据。而其余用户的数据和原始数据保持一致。对于用户 10000 聚合后的数据,前 5 列没有变化:
第 6 列值为 2017-10-01 07:00:00。因为 last_visit_date 列的聚合方式为 REPLACE,所以 2017-10-01 07:00:00 替换了 2017-10-01 06:00:00 保存了下来。注意:在同一个导入批次中的数据,对于 REPLACE 这种聚合方式,替换顺序不做保证,如在这个例子中,最终保存下来的,也有可能是 2017-10-01 06:00:00;而对于不同导入批次中的数据,可以保证,后一批次的数据会替换前一批次。
第 7 列值为 35:因为 cost 列的聚合类型为 SUM,所以由 20 + 15 累加获得 35。
第 8 列值为 10:因为 max_dwell_time 列的聚合类型为 MAX,所以 10 和 2 取最大值,获得 10。
第 9 列值为 2:因为 min_dwell_time 列的聚合类型为 MIN,所以 10 和 2 取最小值,获得 2。
经过聚合,Doris 中最终只会存储聚合后的数据。换句话说,即明细数据会丢失,用户不能够再查询到聚合前的明细数据了,有了聚合模型就不需要我们自己去统计或者分组了。
小结
Aggregate 模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。但是该模型对 count(*) 查询很不友好
Unique 模型针对需要唯一主键约束的场景,可以保证主键唯一性约束。但是无法利用 ROLLUP 等预聚合带来的查询优势。对于聚合查询有较高性能需求的用户,推荐使用自 1.2 版本加入的写时合并实现
Duplicate 适合任意维度的 Ad-hoc 查询。虽然同样无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(只读取相关列,而不需要读取所有 Key 列)。
3.分区和分桶(Partition & Tablet)
Doris 支持两层的数据划分。第一层是分区(Partition),第二层是 桶 Bucket(或Tablet)
,在 Doris 的存储引擎中,用户数据被水平划分为若干个数据分片(Tablet,也称作数据分桶)
。分区的方式有, List分区,Range分区, Null分区 。每个 Tablet 包含若干数据行。各个 Tablet 之间的数据没有交集,并且在物理上是独立存储的,下面举个例子
采用两层数据划分的好处:
-
有时间维度或类似带有有序值的维度,可以以这类维度列作为分区列。分区粒度可以根据导入频次、分区数据量等进行评估。
-
历史数据删除需求:如有删除历史数据的需求(比如仅保留最近 N 天的数据)。使用复合分区,可以通过删除历史分区来达到目的。也可以通过在指定分区内发送 DELETE 语句进行数据删除。
-
解决数据倾斜问题:每个分区可以单独指定分桶数量。如按天分区,当每天的数据量差异很大时,可以通过指定分区的分桶数,合理划分不同分区的数据,分桶列建议选择区分度大的列。
如果做过Mysql的分库分表的童鞋就能理解这个和分库分表很像,那既然要分区和分桶,如何分呢?就需要分区算法和分桶算法.
建表举例
Doris 的建表是一个同步命令,SQL 执行完成即返回结果,命令返回成功即表示建表成功。具体建表语法可以参考CREATE TABLE,也可以通过 HELP CREATE TABLE
查看更多帮助。这里给出了一个采用了 Range 分区 和 Hash 分桶
的建表举例。
-- Range Partition
CREATE TABLE IF NOT EXISTS example_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`) --数据表类型,key相同进行聚合
PARTITION BY RANGE(`date`) --使用range范围分区,根据date进行分区,规则如下
(
PARTITION `p201701` VALUES LESS THAN ("2017-02-01"), --小于该时间2017-02-01的放入该分区p201701
PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
PARTITION `p201703` VALUES LESS THAN ("2017-04-01"),
PARTITION `p2018` VALUES [("2018-01-01"), ("2019-01-01"))
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16 --根据用户ID的Hash值进行分桶,16个桶
PROPERTIES
(
"replication_num" = "1" -- 指定副本数量,因为只有一个BE所以只能有1个副本
);
-
PARTITION BY RANGE(
date
) :这里采用了时间范围分区RANGE(date
) ,不同时间范围的数据会分到不同的区, -
DISTRIBUTED BY HASH(
user_id
) BUCKETS 16 :使用用户IDHash分桶HASH(user_id
),不同的user_id的Hash值的数据会分到不同的桶,每个分区指定了16个桶 -
AGGREGATE KEY : 这里采用了聚合模型,除了 (
user_id
,date
,timestamp
,city
,age
,sex
) 这些列之外的其他列会进行聚合。 -
replication_num : 副本数。默认副本数为 3。如果 BE 节点数量小于 3,则需指定副本数小于等于 BE 节点数量。
-
ENGINE 的类型是 OLAP,即默认的 ENGINE 类型。在 Doris 中,只有这个 ENGINE 类型是由 Doris 负责数据管理和存储的。其他 ENGINE 类型,如 MySQL、 Broker、ES 等等,本质上只是对外部其他数据库或系统中的表的映射,以保证 Doris 可以读取这些数据。而 Doris 本身并不创建、管理和存储任何非 OLAP ENGINE 类型的表和数据。
查看分区信息
可以通过 show create table 来查看表的分区信息。
show create table example_range_tbl
可以通过 show partitions from your_table 来查看表的分区信息。
修改分区信息
通过 alter table add partition 来增加新的分区
ALTER TABLE example_range_tbl ADD PARTITION p201704 VALUES LESS THAN(“2020-05-01”) DISTRIBUTED BY HASH(
user_id
) BUCKETS 5;
Range 分区
分区列通常为时间列,以方便的管理新旧数据。Range 分区支持的列类型 DATE, DATETIME, TINYINT, SMALLINT, INT, BIGINT, LARGEINT。
- FIXED RANGE:定义分区的左闭右开区间。
PARTITION BY RANGE(`date`)
(
-- 大于左边,小于右边日期内的数据,放到p201701分区
PARTITION `p201701` VALUES [("2017-01-01"), ("2017-02-01")),
PARTITION `p201702` VALUES [("2017-02-01"), ("2017-03-01")),
PARTITION `p201703` VALUES [("2017-03-01"), ("2017-04-01"))
)
- LESS THAN:仅定义分区上界。下界由上一个分区的上界决定。
PARTITION BY RANGE(`date`)
(
-- 小于该时间的数据放入p201701分区
PARTITION `p201701` VALUES LESS THAN ("2017-02-01"),
PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
PARTITION `p201703` VALUES LESS THAN ("2017-04-01"),
PARTITION `p2018` VALUES [("2018-01-01"), ("2019-01-01")),
PARTITION `other` VALUES LESS THAN (MAXVALUE)
)
- BATCH RANGE:批量创建数字类型和时间类型的 RANGE 分区,定义分区的左闭右开区间,设定步长。
PARTITION BY RANGE(age)
(
-- 年龄分区,1-100之间的步长是10
FROM (1) TO (100) INTERVAL 10
)
PARTITION BY RANGE(`date`)
(
FROM ("2000-11-14") TO ("2021-11-14") INTERVAL 2 YEAR
)
- MULTI RANGE:批量创建 RANGE 分区,定义分区的左闭右开区间。示例如下:
PARTITION BY RANGE(col)
(
FROM ("2000-11-14") TO ("2021-11-14") INTERVAL 1 YEAR,
FROM ("2021-11-14") TO ("2022-11-14") INTERVAL 1 MONTH,
FROM ("2022-11-14") TO ("2023-01-03") INTERVAL 1 WEEK,
FROM ("2023-01-03") TO ("2023-01-14") INTERVAL 1 DAY,
PARTITION p_20230114 VALUES [('2023-01-14'), ('2023-01-15'))
)
List 分区
分区列支持 BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR 数据类型,分区值为枚举值。只有当数据为目标分区枚举值其中之一时,才可以命中分区。Partition 支持通过 VALUES IN (…) 来指定每个分区包含的枚举值。
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")
)
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"))
)
Doris还支持NULL分区,和动态分区,这2中不怎么使用,具体的可以参考官网
Bucket桶
如果使用了Partition,则DISTRIBUTED.语句描述的是数据在各个分区内的划分规则。如果不使用Partition,则描述的是对整个表的数据的划分规则。
- 分桶列可以是多列,但必须为Key列。分桶列可以和Partition列相同或不同。
- 分桶列的选择,是在查询吞吐和查询并发之间的一种权衡,如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低。这个方式适合大吞吐低并发的查询场景。
- 如果仅选择一个或少数分桶列,则对应的点查间可以仅触发一个分桶扫描。此时,当多个点查间并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景。
- 分桶的数量理论上没有上限
下面案例是根据省份和城市进行Hash分桶,桶的数量为4,根据查询条件不同,查询过程如下
下面案例是根据省份进行Hash分桶,根据查询条件不同,查询流程如下
分桶建议
-
一个表的Tablet桶数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
-
单个Tablet的数据量理论上没有上下界,但建议在1G-10G的范围内。如果单个Tablet数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加Schema Change或者Rollup操作失败重试的代价(这些操作失败重试的粒度是Tablet)。分桶应该控制桶内数据量,不易过大或者过小
-
在建表时,每个分区的Bucket数量统一指定。但是在动态增加分区时(ADD PARTITION),可以单独指定新分区的Bucket数量。可以利用这个功能方便的应对数据缩小或膨胀。
-
一个Partition的Bucket数量一旦指定,不可更改。所以在确定Bucket数量时,需要预先考虑集群扩容的情况。比如当前只有3台host,每台host有1块盘。如果Bucket的数量只设置为3或更小,那么后期即使再增加机器,也不能提高并发度。
举例
如果一个表总大小为500MB,则可以考虑4-8个分片。
- 5GB: 8-16个分片。
- 50GB: 32个分片。
- 500GB: 建议分区,每个分区大小在50GB左右,每个分区16-32个分片。
- 5TB:建议分区,每个分区大小在500GB左右,每个分区16-32个分片。
注:表的数据量可以通过SHOW DATA命令查看,结果除以副本数,即表的数据量。
4.properties属性
建表语句后面可以通过properties来执行一些属性,下面展示2个案例,更多可以查看官网
PROPERTIES
(
"replication_num" = "1"
);
- replication_num : 副本数。默认副本数为 3。如果 BE 节点数量小于 3,则需指定副本数小于等于 BE 节点数量。
- storage_medium/storage_cooldown_time : 数据存储介质。storage_medium 用于声明表数据的初始存储介质,而 storage_cooldown_time 用于设定到期时间。示例:
这个示例表示数据存放在 SSD 中,并且在 2020-11-20 00:00:00 到期后,会自动迁移到 HDD 存储上。"storage_medium" = "SSD", "storage_cooldown_time" = "2020-11-20 00:00:00"
5.案例DataBase连接Doris
doris使用的是Mysql的客户端,所以直接使用Mysql的方式进行连接即可,记得端口要使用:9030
打开Console,创建 一个数据库和表如下
create database db_user;
CREATE TABLE IF NOT EXISTS db_user.user_agg
(
`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"
);
这里我采用的是官网的案例,创建了一个用户表,使用的是聚合模型,下面是插入数据和查询数据的SQL
insert into db_user.user_agg 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);
select * from db_user.user_agg
结果是进行过合并的