作者:肖亮亮
Table Partition 是指根据一定规则,将数据库中的一张表分解成多个更小的容易管理的部分。从逻辑上看只有一张表,但是底层却是由多个物理分区组成。相信对有关系型数据库使用背景的用户来说可能并不陌生。
TiDB 正在支持分区表这一特性。在 TiDB 中分区表是一个独立的逻辑表,但是底层由多个物理子表组成。物理子表其实就是普通的表,数据按照一定的规则划分到不同的物理子表类内。程序读写的时候操作的还是逻辑表名字,TiDB 服务器自动去操作分区的数据。
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
separated DATE NOT NULL
)
PARTITION BY RANGE ( YEAR(separated) ) (
PARTITION p0 VALUES LESS THAN (1991),
PARTITION p1 VALUES LESS THAN (1996),
PARTITION p2 VALUES LESS THAN (2001),
PARTITION p3 VALUES LESS THAN MAXVALUE
);
本文接下来按照 TiDB 源码的 release-2.1 分支讲解,部分讲解会在 source-code 分支代码,目前只支持 Range 分区所以这里只介绍 Range 类型分区 Table Partition 的源码实现,包括 create table、select 、add partition、insert 、drop partition 这五种语句。
create table 会重点讲构建 Partition 的这部分,更详细的可以看 TiDB 源码阅读系列文章(十七)DDL 源码解析,当用户执行创建分区表的SQL语句,语法解析(Parser)阶段会把 SQL 语句中 Partition 相关信息转换成 ast.PartitionOptions,下文会介绍。接下来会做一系列 Check,分区名在当前的分区表中是否唯一、是否分区 Range 的值保持递增、如果分区键构成为表达式检查表达式里面是否是允许的函数、检查分区键必须是 INT 类型,或者通过表达式返回 INT 类型、检查分区键是否符合一些约束。
解释下分区键,在分区表中用于计算这一行数据属于哪一个分区的列的集合叫做分区键。分区键构成可能是一个字段或多个字段也可以是表达式。
// PartitionOptions specifies the partition options.
type PartitionOptions struct {
Tp model.PartitionType
Expr ExprNode
ColumnNames []*ColumnName
Definitions []*PartitionDefinition
}
// PartitionDefinition defines a single partition.
type PartitionDefinition struct {
Name model.CIStr
LessThan []ExprNode
MaxValue bool
Comment string
}
PartitionOptions
结构中 Tp 字段表示分区类型,Expr
字段表示分区键,ColumnNames
字段表示 Columns 分区,这种类型分区有分为 Range columns 分区和 List columns 分区,这种分区目前先不展开介绍。PartitionDefinition
其中 Name 字段表示分区名,LessThan
表示分区 Range 值,MaxValue
字段表示 Range 值是否为最大值,Comment
字段表示分区的描述。
CreateTable Partition 部分主要流程如下:
ast.PartitionOptions
, 然后 buildTablePartitionInfo 负责把 PartitionOptions
结构转换 PartitionInfo
, 即 Partition 的元信息。PartitionInfo
的一系列 check 主要流程就讲完了,需要注意的是我们没有对 PartitionInfo
的元数据持久化单独存储而是附加在 TableInfo Partition 中。add partition 首先需要从 SQL 中解析出来 Partition 的元信息,然后对当前添加的分区会有一些 Check 和限制,主要检查是否是分区表、分区名是已存在、最大分区数限制、是否 Range 值保持递增,最后把 Partition 的元信息 PartitionInfo 追加到 Table 的元信息 TableInfo中,具体如下:
drop partition 和 drop table 类似,只不过需要先找到对应的 Partition ID,然后删除对应的数据,以及修改对应 Table 的 Partition 元信息,两者区别是如果是 drop table 则删除整个表数据和表的 TableInfo 元信息,如果是 drop partition 则需删除对应分区数据和 TableInfo 中的 Partition 元信息,删除分区之前会有一些 Check 具体如下:
tablePrefix_rowPrefix_partitionID_rowID
startKey: tablePrefix_rowPrefix_partitionID
endKey: tablePrefix_rowPrefix_partitionID + 1
Select 语句重点讲 Select Partition 如何查询的和分区裁剪(Partition Pruning),更详细的可以看 TiDB 源码阅读系列文章(六)Select 语句概览 。
一条 SQL 语句的处理流程,从 Client 接收数据,MySQL 协议解析和转换,SQL 语法解析,逻辑查询计划和物理查询计划执行,到最后返回结果。那么对于分区表是如何查询的表里的数据的,其实最主要的修改是 逻辑查询计划 阶段,举个例子:如果用上文中 employees 表作查询, 在 SQL 语句的处理流程前几个阶段没什么不同,但是在逻辑查询计划阶段,rewriteDataSource 将 DataSource 重写了变成 Union All 。每个 Partition id 对应一个 Table Reader。
select * from employees
等价于:
select * from (union all
select * from p0 where id < 1991
select * from p1 where id < 1996
select * from p2 where id < 2001
select * from p3 where id < MAXVALUE)
通过观察 EXPLAIN
的结果可以证实上面的例子,如图 1,最终物理执行计划中有四个 Table Reader 因为 employees 表中有四个分区,Table Reader
表示在 TiDB 端从 TiKV 端读取,cop task
是指被下推到 TiKV 端分布式执行的计算任务。
<center>图 1:EXPLAIN 输出</center>
用户在使用分区表时,往往只需要访问其中部分的分区, 就像程序局部性原理一样,优化器分析 FROM
和 WHERE
子句来消除不必要的分区,具体还要优化器根据实际的 SQL 语句中所带的条件,避免访问无关分区的优化过程我们称之为分区裁剪(Partition Pruning),具体实现在 这里,分区裁剪是分区表提供的重要优化手段,通过分区的裁剪,避免访问无关数据,可以加速查询速度。当然用户可以刻意利用分区裁剪的特性在 SQL 加入定位分区的条件,优化查询性能。
Insert 语句 是怎么样写入 Table Partition ?
其实解释这些问题就可以了:
普通 Table 和 Table Partition 也是实现了 Table 的接口,load schema 在初始化 Table 数据结构的时候,如果发现 tableInfo
里面没有 Partition 信息,则生成一个普通的 tables.Table
,普通的 Table 跟以前处理逻辑保持不变,如果 tableInfo
里面有 Partition 信息,则会生成一个 tables.PartitionedTable
,它们的区别是 RowKey 的编码方式:
tablePrefix_rowPrefix_partitionID_rowID
undefined
Value: [col1, col2, col3, col4]
tablePrefix_rowPrefix_tableID_rowID
undefined Value: [col1, col2, col3, col4]
INSERT INTO employees VALUES (1, 'PingCAP TiDB', '2003-10-15'),
NULL
,对于不同的 Partition 类型有不同的处理方式:NULL
,该行数据被插入那个 Partition,否则插入失败NULL
值视为 0,计算 Partition ID 将数据插入到对应的 PartitionTiDB 目前支持 Range 分区类型,具体以及更细节的可以看 这里。剩余其它类型的分区类型正在开发中,后面陆续会和大家见面,敬请期待。它们的源码实现读者届时可以自行阅读,流程和文中上述描述类似。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。