分区表是关系数据库非常重要的功能,相比传统单机关系数据库的分区表的所有分区仍存放在同一物理机器上,OceanBase能够将所有分区打散到不同物理机器,从而能够真正体现出分布式带来的福利,可以彻底解决扩展性问题。本文给大家介绍OceanBase1.0支持的分区功能及分区表查询的相关优化
1 分区功能
1.1 一级分区
OceanBase1.0现在支持的一级分区类型有:HASH, KEY, RANGE, RANGE COLOMNS (LIST, LIST COLUMNS, INTERVAL分区正在开发中)
1.1.1 HASH分区
HASH分区是使用用户定义的表达式所返回的值,按指定的分区个数取模,进行分区。 用户指定的分区表达式返回结果需要为整数。
举例1:对于info_t表,按照分区键id进行HASH分区,分区数为3时,数据分布如下:
CREATE TABLE `info_t`(id INT, gmt_create DATETIME, info VARCHAR(20), PRIMARY KEY(id))
PARTITION BY HASH(id)PARTITIONS 3; 举例2: 对于info_t表,按gmt_create字段生成年份进行HASH分区,分区数为3时,数据分布如下:
CREATE TABLE `info_t`(id INT, gmt_create DATETIME, info VARCHAR(20), PRIMARY KEY(gmt_create))
PARTITION BY HASH(YEAR(gmt_create))PARTITIONS 3;1.1.2 KEY分区
KEY分区与HASH分区类似,不同在于:
a. 使用系统提供的HASH函数(murmurhash)对涉及的分区键进行计算后再分区,不允许使用用户自定义的表达式
b. 仅支持指定0到多个分区键向量进行分区, 对每个分区键没有类型限制
举例:以下建表语句表示将id,gmt_create键作为分区键,按KEY分区划分为3个分区。
CREATE TABLE `info_t`(id INT, gmt_create DATETIME, info VARCHAR(20))PARTITION BY KEY(id, gmt_create)PARTITIONS 3;1.1.3 RANGE分区
RANGE分区是按用户指定的表达式范围将每一条记录划分到不同分区。
举例:range分区常用场景是按时间字段进行分区, 比如将生成时间字段gmt_create作为分区键, 并按以下指定分区规则分区:
(-∞, 2015-01-01)的数据在P0分区,
[2015-01-01, 2016-01-01)在p1分区,
[2016-01-01, 2017-01-01)在p2分区,
[2017-01-01, +∞)在P3分区 从而可以将数据按gmt_create字段划分到4个不同范围分区
对应建表语句如下:
CREATE TABLE `info_t`(id INT, gmt_create TIMESTAMP, info VARCHAR(20), PRIMARY KEY (gmt_create)) PARTITION BY RANGE(UNIX_TIMESTAMP(gmt_create))
(PARTITION p0 VALUES LESS THAN (UNIX_TIMESTAMP('2015-01-01 00:00:00')), PARTITION p1 VALUES LESS THAN (UNIX_TIMESTAMP('2016-01-01 00:00:00')), PARTITION p2 VALUES LESS THAN (UNIX_TIMESTAMP('2017-01-01 00:00:00')), PARTITION p3 VALUES LESS THAN (MAXVALUE)) 1.1.4 RANGE COLUMNS分区:
RANGE COLUMNS分区与RANGE分区类似,但不同点在于RANGE COLUMNS分区可以按一个或多个分区键向量进行行分区,并且每个分区键的类型除了INT类型还可以支持其他类型,比如VARCHAR、DATETIME等。
举例1:直接以DATETIME类型的gmt_create字段进行RANGE COLUMNS分区
CREATE TABLE `info_t`(id INT, gmt_create DATETIME, info VARCHAR(20), PRIMARY KEY (gmt_create))PARTITION BY RANGE COLUMNS(gmt_create)
(PARTITION p0 VALUES LESS THAN ('2015-01-01 00:00:00'), PARTITION p1 VALUES LESS THAN ('2016-01-01 00:00:00'), PARTITION p2 VALUES LESS THAN ('2017-01-01 00:00:00'), PARTITION p3 VALUES LESS THAN (MAXVALUE));举例2: 使用多分区键向量进行RANGE COLUMNS分区
CREATE TABLE `info_t`(id INT, child_id INT, info VARCHAR(20), PRIMARY KEY (id, child_id))PARTITION BY RANGE COLUMNS(id, child_id)
(PARTITION p0 VALUES LESS THAN (1, 1), PARTITION p1 VALUES LESS THAN (5, 5), PARTITION p2 VALUES LESS THAN (6, 6), PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE));1.2 二级分区
上面介绍了基本的一级分区功能, 一级分区只能按照一个维度进行数据的划分,而很多互联网流水业务都需要按两个维度划分数据:一个维度是时间,数据是按照时间顺序生成的;另外一个维度是用户。这时候就能使用OceanBase的二级分区的方式来划分数据,时间维度用RANGE分区,用户ID用HASH分区。现在OceanBase支持的二级分区功能有:HASH-RANGE, HASH-RANGE COLUMNS(RANGE-HASH, RANGE-RANGE,LIST-RANGE组合正在开发中,更多二级分区的组合会根据业务需求支持,最终我们将实现Oracle所有分区功能)
二级分区的每一级分区方式与一级分区方式相同,只是第二级的分区是在第一级分区的基础之上再进行一次分区划分。如下history_t表按一级HASH划分3个分区,每个一级分区按时间RANGE COLUMNS划分为4个分区,总共有12个分区。
CREATE TABLE history_t(user_id INT, gmt_create DATETIME, info VARCHAR(20), PRIMARY KEY(user_id, gmt_create))PARTITION BY HASH(user_id)SUBPARTITION BY RANGE COLUMNS(gmt_create)
SUBPARTITION TEMPLATE (SUBPARTITION p0 VALUES LESS THAN ('2014-11-11'),
SUBPARTITION p1 VALUES LESS THAN ('2015-11-11'),
SUBPARTITION p2 VALUES LESS THAN ('2016-11-11'),
SUBPARTITION p3 VALUES LESS THAN (MAXVALUE))PARTITIONS 3;
这12个分区对于业务方是透明的,业务方看到的是一张表,但数据是按照用户和时间序列两个维度划分到不同分区,这些分区可以分布到不同服务器,当某一用户数据量增大时,可以增加二级分区的个数,并将数据均衡到更多的服务器,从而可以很好的解决大用户扩展性的问题。并且要删除过期数据的话,可以通过drop分区实现。
1.3 生成列(Generated Columns)分区
生成列是指这一列是由其他列计算而得,生成列分区是指将生成列作为分区键进行分区,该功能能够更好的满足期望将某些字段进行一定处理后作为分区键的需求。
比如我们可能会碰到这种情况:某个表gc_part_t含有t_key字段(varchar),该字段的前四个字符表示user_id, 其他字符描述其他信息,我们期望用user_id对该表进行数据划分,但user_id并不是单独的字段。 此时就可以使用生成列分区解决该问题,将t_key字段的前4个字符作为生成列,然后将该生成列作为分区键进行分区,即可满足需求。
具体建表语句如下,可将该表数据根据user_id按KEY分区划分到10个不同分区:
CREATE TABLE gc_part_t(t_key varchar(10) PRIMARY KEY, gc_user_id VARCHAR(4) GENERATED ALWAYS AS (SUBSTRING(t_key, 1, 4)) VIRTUAL, c3 INT)PARTITION BY KEY(gc_user_id)
PARTITIONS 10;
2 分区表查询优化
2.1 分区裁剪
分区裁剪是对分区表查询的一种优化,对于过滤条件中包含分区键或分区表达式的查询,SQL引擎会计算出涉及的分区,从而在执行时,仅访问该查询涉及的分区。目的是在查询中消除不必要访问的分区,减少访问的数据量和运行时间,从而提高了查询性能和资源利用率。
对于上面的二级分区表history_t,如果业务需要查看0号用户2016以后所有数据,查询sql及查询计划如下:
SELECT * FROM history_t WHERE user_id = 0 AND gmt_create >= '2016-01-01' and gmt_create < '2017-01-01' ;
从执行计划可以看出,该查询只需要访问p0sp2和p0sp3两个分区,而不用将12个分区全都访问,从而可以大大减少访问的数据量和运行时间。
OceanBase除了能够支持按单column分区的剪裁,同时也支持表达式分区的分区裁剪,当分区表达式为func(col)的时候,对于过滤条件含func(col) = value, func(col) = const_x OR col = const_y,func(col) = const_x AND col = const_y等分区表达式和分区键混合的查询也能够进行分区剪裁。
举例:expr_range_t表按mod(c1, 100)进行range分区,划分为3个分区,建表语句如下
CREATE TABLE expr_range_t (c1 INT primary key, c2 INT) PARTITION BY RANGE (mod(c1, 100))
(PARTITION p0 VALUES LESS THAN (30),
PARTITION p1 VALUES LESS THAN (60),
PARTITION p2 VALUES LESS THAN (MAXVALUE));对于下面的查询,查看其执行计划可知,分区裁剪后仅涉及p2分区
SELECT * FROM expr_range_t WHERE mod(c1, 100) = 60 OR c1 = 70;2.2 分区间并行
分区间并行是指执行计划在各个分区间进行并行执行,从而提升执行效率。
当sql查询在分区剪裁后,仍然涉及多个分区时,会生成一个分布式执行计划,该分布式计划会被调度到分区所在不同机器上进行执行。OceanBase可以通过Hint或Session变量指定并行度来控制分区间并发执行任务的数量。
对于以下查询,history_t表为上面的二级分区表,分区裁剪后涉及3个分区,指定并发度为3进行查询。
SELECT/*+ parallel(3)*/ info FROM history_t where user_id = 0 order by info;
执行计划如下:
分布式计划中的EXCHANGE操作符标识着这是一个分布式计划。EXCHANGE OUT DIST及其下面的运算符组成了一个“小”的执行计划(SubPlan),这个计划会被调度器拆分为3个计算任务(task)分发到分区所在的机器上并发执行,从而可以将SubPlan中算子的处理速度提升3倍的性能,当涉及的数据量很大时,可以大大提高整体的执行效率。
2.3 分区间排序消除
分区间排序消除是指对于RANGE分区场景,其分区规则已确保每一个分区间是有序的,因此对range分区键进行排序时,是可以消除的。
举例:range_t表按c1进行RANGE分区,分成3个分区;
CREATE TABLE range_t(c1 INT PRIMARY KEY, c2 INT) PARTITION BY RANGE(c1)
(PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN (20), PARTITION p2 VALUES LESS THAN (MAXVALUE));对于以下sql,从执行计划可以看出,该sql涉及3个分区,并且在EXCHANGE IN节点接收到3个分区的结果后直接利用的partition order,而没有在EXCHANGE IN上面做SORT操作。
SELECT * FROM range_t ORDER BY c1;2.4 Partition Wise Join
Partition Wise Join是指分区模式相同的分区表在按分区键进行JOIN时,如果两表对应的Partition分布是在相同的server上,可以将JOIN操作下压到各个分区所在的server处理,而不需要将数据拉取到主控节点后再进行JOIN。配合分区间并行,可以很好的提升查询性能。
举例:t1, t2表按c1进行HASH分区,均分成3个分区,且每个对应分区分布在相同server。
CREATE TABLE t1(c1 INT PRIMARY KEY, c2 INT) PARTITION BY HASH(c1) PARTITIONS 3;CREATE TABLE t2(c1 INT PRIMARY KEY, c2 INT) PARTITION BY HASH(c1) PARTITIONS 3;对于以下sql,从执行计划可以看出,该sql涉及每个表的3个分区,并且各个对应分区间的JOIN操作已经下压到了EXCHANGE IN节点下的SubPlan中, 直接在各个分区所在的server处理JOIN操作。
SELECT * FROM t1, t2 WHERE t1.c1 = t2.c1;3 总结
OceanBase1.0现在支持多种分区类型KEY, HASH, RANGE, RANGE COLUMNS, 同时我们也支持将生成列作为分区键进行分区;分区裁剪方面,对于查询中含分区表达式和分区键混合的过滤条件的复杂情况,能准确的计算出该查询对应的分区;对于多分区的查询,当数据量较大时,使用分区间并行功能能够大大提升查询性能,分区间排序消除,Partition Wise Join在某些场景也能起到非常好的性能提升作用。
还有很多工作是我们努力在做的,比如LIST,INTERVAL等更多的分区类型的支持,分区管理功能的完善,分区间并行的进一步优化,分区内并行的处理等等,相信我们的这些努力会给客户带来更好的体验,更多的价值。




