Doris的分区Partition和分桶Bucket介绍

news/2024/11/7 7:44:28/

Doris的分区Partition和分桶Bucket介绍

Doris数据库是一个分布式的列式存储数据库,它支持分区和分桶两种数据划分方式

  1. 分区:Doris数据库支持水平分区,即将数据按照某个字段的值进行分区,相同的值会被分配到同一个分区中。这种方式可以将数据均匀地分布到不同的节点上,提高查询效率和并行处理能力。
  2. 分桶:Doris数据库支持垂直分桶,即将同一行数据的不同列分别存储在不同的桶中。这种方式可以将经常一起查询的列存储在同一个桶中,提高查询效率。
  3. 节点负载均衡:Doris数据库可以将数据均匀地分布到不同的节点上,保证每个节点的负载均衡,提高系统的可扩展性和稳定性。
  4. 数据压缩:Doris数据库支持多种数据压缩算法,可以将数据在存储时进行压缩,减少存储空间,降低存储成本。
  5. 数据副本:Doris数据库支持数据副本,可以将数据复制到多个节点上,提高系统的容错能力和可用性。

​ Doris 支持两层的数据划分。第一层是 Partition,支持 Range 和 List 的划分方式。第二层是 Bucket(Tablet),仅支持 Hash 的划分方式。

​ Doris数据划分也可以仅使用一层分区。使用一层分区时,只支持 Bucket 划分

1. Partition - 分区

  • Partition 列可以指定一列或多列,分区列必须为 KEY 列。
  • 不论分区列是什么类型,在写分区值时,都需要加双引号。
  • 分区数量理论上没有上限。
  • 当不使用 Partition 建表时,系统会自动生成一个和表名同名的,全值范围的 Partition。该 Partition 对用户不可见,并且不可删改。
  • 创建分区时不可添加范围重叠的分区。

Partition,支持 Range 和 List

1.1 Range 分区使用介绍

Range分区是一种基于分区键对表进行分区的方式,分区键指的是表中的某一列,而Range则是指这个列的值的范围。使用Range分区,可以将数据按照列的值的范围进行划分,将数据分散到不同的节点上,以实现分布式存储和查询

  • 分区列通常为时间列,以方便的管理新旧数据

  • Partition 支持通过 VALUES LESS THAN (...) 仅指定上界,系统会将前一个分区的上界作为该分区的下界,生成一个左闭右开的区间。也支持通过 VALUES [...) 指定上下界,生成一个左闭右开的区间

  • 当不使用Partition建表时,系统会自动生成一个和表名同名的,全值范围的 Partition。该Partition对用户不可见,并且不可删改

官方示例介绍

1.1.1 建Range Partition测试表

-- Range Partition
use test_db;CREATE TABLE IF NOT EXISTS test_db.example_range_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 "用户最小停留时间"
)
ENGINE=OLAP
AGGREGATE KEY(`user_id`, `date`, `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" = "3","storage_medium" = "SSD","storage_cooldown_time" = "2023-05-12 12:00:00"
);

如上 example_range_tbl 示例,当建表完成后,会自动生成如下3个分区:

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

1.1.2 查看分区partition方式

MySQL [test_db]> show partitions from example_range_tbl;

1.1.3 向对应分区插入数据

insert into test_db.example_range_tbl values (10000,'2017-01-01','北京',20,0,'2017-01-01 06:00:00',20,10,10)

1.1.4 查询分区内数据

验证数据是否在对应分区内

MySQL [test_db]> SELECT user_id,date FROM example_range_tbl PARTITION `p201701`;
+---------+------------+
| user_id | date       |
+---------+------------+
| 10000   | 2017-01-01 |
+---------+------------+
1 row in set (0.01 sec)MySQL [test_db]> SELECT user_id,date FROM example_range_tbl PARTITION `p201702`;
Empty set (0.01 sec)MySQL [test_db]> SELECT user_id,date FROM example_range_tbl PARTITION `p201703`;
Empty set (0.00 sec)

1.1.5 测试插入无对应分区数据

MySQL [test_db]> insert into test_db.example_range_tbl values (20000,'2017-11-01','北京',20,0,'2017-11-01 06:00:00',20,10,10);
Query OK, 0 rows affected, 1 warning (0.04 sec)
{'label':'insert_1bb0bf5655344769-b3479bb93baa9a5a', 'status':'VISIBLE', 'txnId':'53'}MySQL [test_db]> select user_id,date from example_range_tbl;
+---------+------------+
| user_id | date       |
+---------+------------+
| 10000   | 2017-01-01 |
+---------+------------+
1 row in set (0.01 sec)

插入的这条数据,‘2017-11-01’ 没有对应的分区, 插入不成功, 但不会抛出异常或错误。( 查询结果里没有新插入的这条数据 )

1.1.6 增加一个partition分区

alter table example_range_tbl add partition p201705 values less than ('2017-06-01');

当alter执行完成后,新的分区信息如下:

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)

1.1.7 删除一个partition分区

alter table example_range_tbl drop partition p201703;

当alter执行完成后,新的分区信息如下:

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) 即如果导入的数据范围在这个空洞范围内,数据是无法导入的,虽然不会抛出异常或错误,但处于这个空洞区间的数据相当于抹去了。

1.1.8 Range多列分区

建表示例:

CREATE TABLE IF NOT EXISTS test_db.example_range_tbl_more
(`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 "用户最小停留时间"
)
ENGINE=OLAP
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
PARTITION BY RANGE(`date`, `user_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")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
PROPERTIES
("replication_num" = "3","storage_medium" = "SSD","storage_cooldown_time" = "2023-05-12 12:00:00"
);

在以上示例中,我们指定 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    --> 无法导入

1.2 List 分区使用介绍

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

  • Partition 支持通过 VALUES IN (…) 来指定每个分区包含的枚举值。

官方示例介绍

1.2.1 建List Partition测试表

-- List PartitionCREATE TABLE IF NOT EXISTS test_db.example_list_tbl
(`user_id` LARGEINT NOT NULL COMMENT "用户id",`date` DATE 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`, `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" = "3","storage_medium" = "SSD","storage_cooldown_time" = "2023-05-12 12:00:00"
);

如上 example_list_tbl 示例,当建表完成后,会自动生成如下3个分区:

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

1.2.2 向对应分区插入数据

MySQL [test_db]> insert into test_db.example_list_tbl values (10000,'2017-01-01','Beijing',20,0,'2017-01-01 06:00:00',20,10,10);
Query OK, 1 row affected (0.04 sec)
{'label':'insert_92a0193c3d65454a-a9d71623490c05eb', 'status':'VISIBLE', 'txnId':'66'}MySQL [test_db]> select user_id,date from example_list_tbl; 
+---------+------------+
| user_id | date       |
+---------+------------+
| 10000   | 2017-01-01 |
+---------+------------+
1 row in set (0.02 sec)

1.2.3 查询分区内数据

MySQL [test_db]> SELECT user_id,date FROM example_list_tbl PARTITION `p_cn`;
+---------+------------+
| user_id | date       |
+---------+------------+
| 10000   | 2017-01-01 |
+---------+------------+
1 row in set (0.01 sec)MySQL [test_db]> SELECT user_id,date FROM example_list_tbl PARTITION `p_usa`;
Empty set (0.00 sec)MySQL [test_db]> SELECT user_id,date FROM example_list_tbl PARTITION `p_jp`;
Empty set (0.00 sec)

这里和上方Range分区相同,不展开测试插入删除数据等操作

1.2.4 增加一个子分区

alter table example_list_tbl add partition p_uk values IN ("London");

当我们增加一个分区 p_uk VALUES IN (“London”),分区结果如下:

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

2. Bucket - 分桶

  • 分桶语法DISTRIBUTED BY HASH(user_id) BUCKETS 16

  • 如果使用了 Partition,则 DISTRIBUTED ... 语句描述的是数据在各个分区内的划分规则。如果不使用 Partition,则描述的是对整个表的数据的划分规则

  • 分桶列可以是多列,Aggregate 和 Unique 模型必须为 Key 列,Duplicate 模型可以是 key 列和 value 列。分桶列可以和 Partition 列相同或不同

  • 分桶列的选择,是在 查询吞吐查询并发 之间的一种权衡

    • 如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低。这个方式适合大吞吐低并发的查询场景
    • 如果仅选择一个或少数分桶列,则对应的点查询可以仅触发一个分桶扫描。此时,当多个点查询并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的IO影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景
  • Auto Bucket : 根据数据量,计算分桶数。 对于分区表,可以根据历史分区的数据量、机器数、盘数,确定一个分桶

  • 分桶的数量理论上没有上限

3. 官方关于 Partition 和 Bucket 的数量和数据量的建议

  • 一个表的 Tablet 总数量等于 (Partition num * Bucket num)。
  • 一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
  • 单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。
  • 当 Tablet 的数据量原则和数量原则冲突时,建议优先考虑数据量原则。
  • 在建表时,每个分区的 Bucket 数量统一指定。但是在动态增加分区时(ADD PARTITION),可以单独指定新分区的 Bucket 数量。可以利用这个功能方便的应对数据缩小或膨胀。
  • 一个 Partition 的 Bucket 数量一旦指定,不可更改。所以在确定 Bucket 数量时,需要预先考虑集群扩容的情况。比如当前只有 3 台 host,每台 host 有 1 块盘。如果 Bucket 的数量只设置为 3 或更小,那么后期即使再增加机器,也不能提高并发度。
  • 举一些例子:假设在有10台BE,每台BE一块磁盘的情况下。如果一个表总大小为 500 MB,则可以考虑4-8个分片。5 GB: 8-16个分片。50 GB: 32个分片。500 GB:建议分区,每个分区大小在 50 GB 左右,每个分区16-32个分片。5 TB:建议分区,每个分区大小在 50 GB 左右,每个分区16-32个分片。

4. 复合分区与单分区

复合分区

  • 第一级称为 Partition,即分区。用户可以指定某一维度列作为分区列(当前只支持整型和时间类型的列),并指定每个分区的取值范围。
  • 第二级称为 Distribution,即分桶。用户可以指定一个或多个维度列以及桶数对数据进行 HASH 分布 或者不指定分桶列设置成 Random Distribution 对数据进行随机分布。

以下场景推荐使用复合分区

  • 有时间维度或类似带有有序值的维度,可以以这类维度列作为分区列。分区粒度可以根据导入频次、分区数据量等进行评估。
  • 历史数据删除需求:如有删除历史数据的需求(比如仅保留最近N 天的数据)。使用复合分区,可以通过删除历史分区来达到目的。也可以通过在指定分区内发送 DELETE 语句进行数据删除。
  • 解决数据倾斜问题:每个分区可以单独指定分桶数量。如按天分区,当每天的数据量差异很大时,可以通过指定分区的分桶数,合理划分不同分区的数据,分桶列建议选择区分度大的列。

用户也可以不使用复合分区,即使用单分区。则数据只做 HASH 分布。


http://www.ppmy.cn/news/68019.html

相关文章

C语言小游戏的实现——三子棋

前言 Hello!友友们,前边我们已经学习了C语言的基础知识,但单纯的理论和简单的代码演示是无法真正做到巩固所学的知识的,那么今天我将会带领大家,根据之前所学的知识,来写一个三子棋小游戏。 目录 前言 总…

PMP之预测部分

引论 什么是项目 项目是为创造独特的产品、服务或成果而进行的临时性工作。 项目管理是把事办成的方法论,万物皆可项目。 项目的基本要素 项目(独特性、临时性)、驱动变更、启动背景、创造商业价值。 组织级项目管理(OPM&am…

泰克示波器MSO54B 5-BW-1000,4通道1G带宽

泰克MSO5B系列示波器,支持广泛的特定应用测量,满足您的各种需求。单独添加高级分析程序包或安装应用程序包,以处理更多不同的工作。 支持超过 25 种串行协议,覆盖常见的接口先进的单相和三相功率分析程序包确保信号完整性和电源完…

python列表平铺

嵌套的列表数据: lst = [1,2,[3,4],5,[6,[7,8]]] 大多数情况下不知道最深的嵌套是第几层,所以每次只能使用递归去解决平铺问题。代码如下: def flatten_list(nested_list):result = []for item in nested_list:if isinstance(item, list):result.extend(flatten_list(item…

关于使用pyinstaller来打包PySide2程序中的问题

打包 pyinstaller 02.py --noconsole --hidden-import PySide2.QtXml 报错0:The ‘pathlib‘ package is an obsolete backport of a standard library package 分析:这个是因为笔者使用的conda的集成环境,这里面自带了打包程序&#xff0c…

基于显扬科技3D视觉相机的医疗试管分拣系统

行业现状: 医疗试管分拣是医疗行业中的一个重要环节,指将医疗实验室或生物技术研究中的试管按照一定的规则进行分拣,并对试管的类型、位置、数量等信息进行识别和管理。 随着医疗技术的不断发展和诊断治疗的精细化,医疗试管分拣…

【C++】内存分区模型

目录 1、缘起 2、内存分区模型 2.1、程序运行前 2.2、程序运行后 3、总结 1、缘起 前几天学习完了 C 的 基础语法 知识点,现在终于要踏上学习 C 核心编程 的旅程了,期待沿途中所遇到的风景。 2、内存分区模型 C 程序在执行时,将内存大…

JUC并发编程18 | AQS分析

尚硅谷(140-155) 18 AQS 前置知识 公平锁和非公平锁可重入锁自旋思想LockSupport双向链表设计模式——模块设计 18.1 AQS入门级别理论知识 AQS一般指的是 AbstractQueuedSynchronized AQS 是用来实现锁或者其他同步器组件的公共基础部分的抽象实现…