1. Feature 背景
新增Range 分区用于解决业务中大量删除带来的性能问题,支持快速删除分区
2. range 分区处理流程
TBD
3. 功能测试
3.1 feature 开关
range 分区功能主要受两个因素影响:1. tidb_enable_table_partition 参数;2.range 分区配置在table info中是否存在.
根据此两个参数划分为一下几个子场景:
1. tidb_enable_table_partition=on, create table with range partition config, feature on, table created as range partition table
2. tidb_enable_table_partition=on, create table without range partition config, table created as normal table
3. tidb_enable_table_partition=off, create table with range partition config, table created as normal table
4. tidb_enable_table_partition=off, create table without range partition config, table created as normal table
5. tidb_enable_table_partition=on, create table with range partition config, then set tidb_enable_table_partition=off, insert some records, records don't saved as partition
6. tidb_enable_table_partition=off, create table with range partition config, then set tidb_enable_table_partition=on, insert some records, records saved as partition
3.2 range 分区管理
range 分区管理主要有以下几个操作: 创建分区表,新增一个分区,删除分区表,删除一个分区,合并,分解,更新,显示
3.2.1 创建range分区表
影响创建range分区表的因素包括:表名,PK/UK,分区名,分区key的个数,分区key的类型,分区数,分区值,分区值顺序
1.表名对分区的影响: 如果表名不存在,通过create table 方式创建partition 表;如果表名已经存在,创建分区表失败;
2. 表中是否会有PK/UK: 如果创建分区表sql中有PK/UK,那partition key只能用同时出现在PK/UK中的字段;如果创建分区表sql中没有PK/UK,那partition可以选址有效类型的字段;
3. 分区名: name type:string, 长度1 to max, 有效字符集:字母,数字,下划线(文档中没找到说明); 如果partition name 长度非法,创建失败;如果partition name字符非法,创建失败;如果partition name大小写转换后相同,report error
4. 分区key的个数:创建partition table时,没有设置key column, 创建失败;创建partition table时,number of partition key = 1, 创建成功;创建partition table时,number of partition key > 1, 创建失败
5. 分区key的类型:分区key可以是单个column, 也可以是一个函数,或者一个表达式。
如果分区key是单个column, 字段类型必须是INT, Date,timestamp; 如果key 在表中不存在,返回失败
如果分区key是函数,函数名必须在ABS() CEILING() DATEDIFF() DAY() DAYOFMONTH() DAYOFWEEK() DAYOFYEAR() EXTRACT() (see EXTRACT() function with WEEK specifier) FLOOR() HOUR() MICROSECOND() MINUTE() MOD() MONTH() QUARTER() SECOND() TIME_TO_SEC() TO_DAYS() TO_SECONDS() UNIX_TIMESTAMP() (with TIMESTAMP columns) WEEKDAY() YEAR() YEARWEEK() 中
如果分区key是一个表达式,但不能是FuncCastExpr, CaseExpr, SubqueryExpr, WindowFuncExpr, RowExpr, DefaultExpr, ValuesExpr:Or,And,Xor,LeftShift, RightShift, BitNeg, Div:
6. 分区数:最大支持1024个分区
7. 分区值:如果多个partition的value相同,创建分区表失败;如果partition名字和值都不相同,增加成功。
8. 分区值位置:创建分区表, partition value 乱序,创建分区表失败;
9. parition划分函数: VALUES LESS THAN , 其他
test cases list:
1. 创建range分区表,表名不存在,表中没有pk/uk,partion名称长度为1,字符合法,分区个数为1,partition key 唯一有效,类型为Date,创建分区表成功,查询分区信息显示正确
2. 创建range分区表,表名不存在,表中有pk,partion名称长度为n-1(不确定系统限定长度,假设为n),字符包含字母数字下划线,1023个分区,分区名无重复,partition key 唯一有效,类型为INT,创建分区表成功,查询分区信息显示正确
3. 创建range分区表,表名不存在,表中有uk,partion名称长度为n-1(不确定系统限定长度,假设为n),字符包含字母数字下划线,1023个分区,分区名无重复,partition key 唯一有效,类型为INT,创建分区表成功,查询分区信息显示正确
4. 创建range分区表,表名不存在,表中有pk+uk,分区类型为TIMESTAMP,创建分区表成功,查询分区信息显示正确
5.创建22个range分区表,分区类型分别为支持列表中的函数,创建分区表成功,查询分区信息显示正确
6.创建range分区表,分区类型为表达式包含+-* div,创建成功,查询分区信息显示正确
7. 创建range分区表,表名已经存在,创建失败
8. 创建range分区表,表名不存在,表中PK/UK无交集,创建range分区表失败
9. 创建range分区表,表名不存在,表中PK/UK有交集但不包含partition key,创建range分区表失败
10. 创建range分区表,partition函数用 values more than, 创建失败
11. 创建range分区表,分区名是空格,创建失败
12. 创建range分区表,分区名超长,创建失败
13. 创建range分区表,分区名含特殊符号,创建失败
14. 创建range分区表,分区名重复,创建失败
15. 创建range分区表,分区名大小写处理后有重复,创建失败
16. 创建range分区表,未指定partition key,创建失败
17. 创建range分区表,partition key有2个,创建失败
18. 创建range分区表,partition key column字段在表中不存在,创建失败
19. 创建range分区表,partition key为varchar类型,创建失败
20. 创建range分区表,partition key为smallint,创建失败(not sure)
21. 创建range分区表,partition key类型为json,创建失败
22. 创建range分区表,partition key是函数类型,函数不在指定列表中,创建失败
23. 创建range分区表,partition key是TIMESTAMP类型,函数不是UNIX_TIMESTAMP(),创建失败
24. 创建range分区表,partition key是表达式,表达式中含逻辑运算符,创建失败
25. 创建range分区表,partition key是表达式,表达式中含位运算符,创建失败
26. 创建range分区表,PARTITION BY RANGE (key_1) ()结构体内为空,0个partition,创建失败
27. 创建range分区表,1025个partition,创建失败
28. 创建range分区表,parition p0,p1对应的value相同,创建失败
29. 创建range分区表,parition p0,p1对应的value不是递增,创建失败
3.2.2 新增一个分区
影响创建新range分区的因素包括:原分区类型,分区名,分区数,分区值,分区值顺序
1. 原分区类型:原来表的partition配置可能会包括: 没有partition, range partition和hash partition 3种。 原表没有partition的,增加partition失败; 原表是range partition表, 可以添加新的partition;如果原表是hash partition的表,那么添加range partition失败。
2. 要创建的分区名称是否存在: table 已经存在,alter table add partition,如果partition名已经存在,report error; 如果partition 名仅大小写不同,处理同same, report error; 如果partition不存在,创建成功
3. 分区数:最大支持1024个分区
4. 分区值:如果table 已经存在,增加partition, 如果新的partition名字不存在,但是值和已经存在的partition相同,增加失败;如果partition名字和值都不相同并且新的partition值大于old的值,增加成功。如果原来的partition中没有MaxValue,新值小于最大的old值,添加失败;如果原partition中有maxvalue, 新的值大于除maxvalue外的最大old值,添加成功
5. 分区值位置:table 存在,新增一个partition, value 小于当前最小值;table存在,新增一个partition, value < 当前最大值;table 存在,新增一个partition, value 大于当前最大值
test cases list:
原table是range partition表只有一个partition,value 为maxvalue,新增分区分区名长度为1,分区名不重复,创建分区成功,查询分区信息显示正确
原table是range partition表有一个partition,value 为10,新增分区分区名长度为n,分区名不重复,分区值为11,创建分区成功,查询分区信息显示正确
原table是range partition表,有1023个分区,新增一个partition成功,查询分区信息显示正确
原table是range partition表,原partition value 分别为10, 15, maxvalue, 新增一个partition, value 为17,创建分区成功,查询分区信息显示正确
原table不是partition表,增加分区失败,查询分区信息显示正确
原table是hash partition表,增加分区失败,查询分区信息显示正确
新增分区的分区名大于最大长度n,增加分区失败,查询分区信息显示正确
新增分区的分区名是空格,增加分区失败,查询分区信息显示正确
新增分区的分区名有特殊符号,增加分区失败,查询分区信息显示正确
新增分区的分区名与原有分区相同,增加分区失败,查询分区信息显示正确
新增分区的分区名大小写处理后与原分区中的相同,增加分区失败,查询分区信息显示正确
原表中已经有1024个分区,新增分区失败,查询分区信息显示正确
新增分区的value与已有分区的value相同,增加分区失败,查询分区信息显示正确
新增分区的值小于最后一个非maxvalue的值,增加分区失败,查询分区信息显示正确
新增分区后,插入属于新分区的记录,记录成功保存到新分区。老分区中无重复记录,查询分区信息显示正确
3.2.3 delete range partition table
delete partition table, all partition file is deleted.
range分区表,drop table 删除最后一个partition
3.2.4 delete range partition
通过分区名删除: table 不是分区表,删除partition失败;分区名存在,删除分区成功; 分区名不存在,删除失败; 分区名大小写转换后相同,删除成功; 原表只有一个partition, 删除partition失败; 原表2个partition, 删除一个,此分区的数据也被删除;删除partition后,insert 新数据,数据可以放入正确的partition中;同时删除多个partition
是否有其他删除方式,没找到相关说明
test cases list:
1. table 是range partition table,有2个partition,要删除的分区名存在,删除成功
2.table 是range partition table,有2个partition,要删除的分区名大小写处理后存在,删除成功
3. 删除分区后,插入属于已删除分区的记录,该记录被保存到其他分区,插入记录成功
4. 批量删除2个分区,删除成功
5. 批量删除n个分区,删除成功(不确认允许同时删多少个分区,假定是n)
6. 原table只有一个分区,删除该分区失败
7. 原table不是分区表,删除分区失败
8. 要删除的分区名不存在,删除分区失败
9.同时删除大于n个分区
3.2.3 merge range partition
not found detail description
按个人理解: 1到多个partition合并为1个partition
1个partition合并到一个partition
3个partition合并到一个partition
1024个partition合并到一个partition
3.2.4 split range partition
not found detail description
按照个人的理解:1个partition 分解为多个partition
1个partition分解为一个partition
1个partition分解为3个partition
1个partition分解为1024个partition
有上下限partition的分解
无下限的partition的分解
无上限的partition的分解
3.2.5 update range partition
通过分区名更新: name exist, name don't exist, 大小写
更新后的value已经存在;更新后的value不存在;value >next key,;value < former key
更新key column name, 更新 key column number
更新主键和唯一键
test cases list:
1. 更新partition value, table是range 分区表,待更新分区是该表第一个分区,分区名存在,新value小于下一个分区的value,更新成功。
2.更新partition value, table 是range分区表,待更新的分区名存在,新value =旧 value, 更新成功
3. 更新partition value, table 是range分区表,待更新的分区名大小写处理后存在,上一个分区的value<新value <下一个分区的 value, 更新成功
4. 更新partition value, table 是range分区表,待更新的分区是该表最后一个分区,分区名存在,新value > old value, 更新成功
5. 更新partition key为另一个col, 该key有效,更新成功
6. 更新partition key为函数,该函数在支持的列表内,更新成功
7. 更新partition key 为表达式,该表达式满足要求,更新成功
8. 更新partition key 在pk/uk 交集中,更新成功
9. 更新partition name, 新分区名字符合法,长度为最大支持长度,更新成功
10. table不是分区表,更新分区失败。
11. 更新的分区名不存在,更新失败
12. 更新的函数名不在支持的列表中,更新失败
13. 更新的表达式含有不支持的操作符, 更新失败
14. 更新的partiton key 在表中不存在,更新失败。
15, 更新partition key,从一个变为多个,更新失败
16. 更新partition key 为空,更新失败
17, 更新分区值,该分区值已经存在,更新失败
18. 更新分区值,第一个partiton. new 分区值> 第2个分区的分区值
19. 更新分区值,最后一个分区, new分区值 < 倒数第二个分区值
20. 更新分区值,更新maxvalue分区为一个固定数字(不确定是否可以更新)
21. 更新partition key, 新key不在pk/uk交集中
3.2.6 display range partition
1. tidb_enable_table_partition = on
创建成功,查询显示partition
创建失败,查询显示partition
创建1024个partition. 查询显示partition
删除唯一的partition, 查询显示partition 信息
多个partition, 删除其中一个,查询显示partition
在3.2.1-3.2.5的场景中覆盖
2. tidb_enable_table_partition = off
查询显示分区信息失败
3.3 分区中的数据处理
3.3.1 插入语句分区选择
假设当前表的range分区的配置为:
tidb_enable_table_partition = on
1. 插入一条新的record r_1, key_1=k1-1, r_1被保存到p0分区
2. 插入一条新的record r_2, key_1=null, r_2被保存到p0分区
3. 插入一条新的record r_3, key_1=k1, r_3被保存到p1分区
4. 如果k4 != MAXVALUE, 插入一条新的record r_4, key_1=k4-1, r_4被保存到p3分区
5. 如果k4 != MAXVALUE, 插入一条新的record r_5, key_1=k4, r_5插入失败
6. 如果k4 != MAXVALUE, 插入一条新的record r_6, key_1=k4+1, r_6插入失败
7. 如果k4 = MAXVALUE, 插入一条新的record r_7, key_1=k3-1, r_7被保存到p2分区
5. 如果k4 = MAXVALUE, 插入一条新的record r_8, key_1=k3, r_8被保存到p3分区
6. 如果k4 = MAXVALUE, 插入一条新的record r_9, key_1=k3+1, r_9被保存到p3分区
tidb_enable_table_partition = off
1. 插入一条新的record r_1, key_1=k1-1, r_1被保存,按分区查询失败,显示分区信息失败
2. 插入一条新的record r_2, key_1=null, r_2被保存,按分区查询失败,显示分区信息失败
3. 插入一条新的record r_3, key_1=k1, r_3被保存,按分区查询失败,显示分区信息失败
3.3.2 分区选择
在4.1的 “test_partition_table as t1” 中进行查询:
1. SELECT * FROM t1 PARTITION (p1, p2) ,查看查询规模,只查询并返回分区 p1 和 p2 的所有行。
2.SELECT * FROM t1 PARTITION (p1, p2) 语句中联合 having, group by, order by, limit 选项
3. 在自查询中使用partiion(p1) 关键字
3.3.3 分区裁剪
如果查询条件不能下推到分区表,则相应的查询语句无法执行分区裁剪;无法在查询计划生成阶段获取到过滤条件的场景,无法利用分区裁剪的优化;对于fn(col)查询条件无法下推到 TiKV 的表达式,不支持分区裁剪。
在4.1的 “test_partition_table as t1” 中进行查询:
tidb_enable_table_partition = on
2. explain SELECT * FROM t1 WHERE key_1 > k1 AND key_1 < k2; 查看查询规模 ,只查询并返回了分区p1的结果。
3. explain SELECT * FROM t1 WHERE key_1 > k1 AND key_1 <= k2; 查看查询规模 ,只查询并返回了分区p1,p2的结果。
4. explain SELECT * FROM t1 WHERE key_1 > k1-1 AND key_1 <= k2+1; 查看查询规模 ,只查询并返回了分区p0, p1,p2的结果。
tidb_enable_table_partition = off
重新执行上诉4个cases,查看查询规模,查询并返回了所有数据的结果
3.3.4 数据移动
增加一个partition "partition p4 values less than (k4)," , k4>k3, 原p3中>=k3 && <k4的数据移动到p4
删除一个partition p1, 查询partition信息,p1被删除,查询数据,p1中的数据也被删除
清空一个partition p0, 查询partition信息,p0存在,查询数据,p0的数据被删除
update 一条记录,key_1 的值 由 <k2, 变为> k2, 查询p1,p2中的数据,该条记录被移动到p2
4. performance test
对于如下场景,执行时间时间应该由于非partition表和hash partition表
1. 频繁删除,批量删除应优于非partition和hash partition
2. 使用partition key字段做查询时的读速率优于非partition和hash partition
3. 写入速率应优于非partition
对于tidb_enable_table_partition=off,各性能指标不应低于原no partition表
对于频繁插入的场景,性能低于hash partition, 但应满足一定的可用行范围
对数据倾斜问题的处理,大量数据集中在少数partition进行读写
对于少量数据分散在大量partition的处理
大量频繁删除下的GC处理
5. compatibility test
与range partition 有交互的功能如下,需要进行回归测试
1. hash partition null值处理,分区选择和裁剪
2. PK/UK 增删改操作的影响, 需要包含range partition key
3. tidb_enable_table_partition 开关
4. hash/ range 切换?(not sure)