MySQL中的分区表

Posted 程序猿集锦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL中的分区表相关的知识,希望对你有一定的参考价值。

第一时间了解更多干货分享,还有各类视频教程资源。扫描它,带走我


  • 什么是分区表

  • 为什么要用分区表

  • 如何查看分区信息

  • 分区的类型

    • range范围分区

    • list列表分区

    • columns多列分区

      • range columns范围多列分区

      • list columns列表多列分区

    • hash哈希分区

      • linear hash线性哈希分区

    • key按键分区

      • linear key线性按键分区

    • 复合分区

      • range-hash复合分区

      • range-key复合分区

      • list-hash复合分区

      • list-key复合分区

      • 复合分区综合示例

  • 分区表的管理

    • range和list分区

      • 添加分区

      • 删除分区

      • 修改分区

      • 合并或拆分分区

    • hash和key分区

      • 添加分区

      • 删除分区

      • 修改分区键

      • 合并或拆分分区

  • 分区的维护

    • 重建分区

    • 优化分区

    • 分析分区

    • 检查分区

    • 修复分区

  • 分区表的注意事项

  • 总结


什么是分区表

分区表示把一个表中的数据在物理上进行分开存储,对应到磁盘上是多个数据文件。但是这些数据文件在逻辑上又是于同一个表,它们共用一个数据表结构的元数据,但是在存放的时候,他们分别独立使用自己的数据文件。在某些情况下可以并行地读取各个分区中的数据,这样有利于提高数据存储和查询的效率。

为什么要用分区表

分区表,在一定的程度上可以提高mysql数据库的性能。因为每一个分区对应着磁盘上面的一个单独的数据文件,这样可以提高磁盘的I/O,有助于提高查询和存储的时候的效率。但是这个是有一个前提:每一个分区都位于不同的存储设备上。如果是所有的分区都在一个磁盘上存放,那么对性能的提示是有限的,效率不会提高很多。如下图所示:

MySQL中的分区表

涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。通过跨多个磁盘来分散数据查询,来获得更大的查询吞吐量,这个是分区表的最大优点。


如何查看分区信息

对于一个分区表,如何查看这个表的分区信息?我们可以使用show create table xxx;查看某一个表的分区信息。

那么一个分区表,如何查看每一个分区中,都存储了多少数据呢?在MySQL的information_schema下面的PARTITIONS表中存储着所有的分区表的详细元数据信息,我们可以使用下面的这个SQL语句来查看具体的分区信息和每一个分区中存储的数据量是多少。

SELECT
PARTITION_NAME,
PARTITION_METHOD,
PARTITION_EXPRESSION,
PARTITION_DESCRIPTION,
TABLE_ROWS,
SUBPARTITION_NAME,
SUBPARTITION_METHOD,
SUBPARTITION_EXPRESSION
FROM
information_schema.PARTITIONS
WHERE
TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME = 'xxxx';


分区的类型

根据分区类型的不同,分区表可以分为如下几种分区类型。

  • range分区:根据某一个列所存储值的范围对数据进行分区存储,这种分区经常使用在数据随着时间的增加在不断的增加,为了提高数据存储和处理的效率,按照时间段对数据进行分区存储,例如按照每一个月一个分区来存放数据,或者每一年一个分区来存放数据,这样就保证了同一个月内的数据,或同一年内的数据是处于同一个range范围分区中的。范围分区分区字段:integer、数值表达式、日期列,日期函数表达式(如year(),to_days(),to_seconds(),unix_timestamp())。

  • list分区:根据某一个列去重后的值的列表对数据进行分区存储,这样的分区能够保证在分区列上的值相同的数据行会被分到同一个分区中,便于一次性的把在分区列上有相同值的数据全部取出。常见的分区字段有类别、区号、性别、国家等字段。

  • columns分区:多列分区是指,在分区的时候,不是根据表中的某一个列进行分区,而是使用2个或2个以上的列作为分区键。前面的range范围分区、list列表分区在选择分区键的时候,只能选择表中的某一个列作为分区键,而columns多列分区可以选择表中的多个列作为分区键。不过这种分区的方式使用的比较少。

  • hash分区:根据指定的分区数,根据分区字段的哈希值来将数据分散到不同的分区中。它会拿每行数据中分区字段的值,与分区的数目取模,得到的余数是多少这行数据就落在对应的分区编号中。这种分区通常是为了把数据均匀的分布在各个分区中,避免在各个分区中所存储的数据量不同,这样就没有数据倾斜的现象发生。常见的分区字段有年龄、编号等。它只能基于interger类型的列作为分区键。

  • linear hash分区:线性哈希分区,这个分区方式和普通的hash分区差不多,只不过是在计算每一行数据到底该划分到哪个分区的时候使用的算法和普通的hash算法有些区别。和普通的hash分区相比,它的优点是可以更快的进行添加、删除、合并、拆分分区,在数据量特别大的时候这种效果尤其明显,它的缺点是数据在各个分区中的分布可能不均匀的,有数据倾斜的可能。

  • key分区:按键分区和hash分区类似。但是它和hash分区不同的是,它支持使用除了blob、text类型之外的所有类型的字段作为分区键,不像hash分区那样,仅仅支持使用integer类型的字段作为分区键。与此同时,key分区不允许使用自定义的表达式进行分区,它只能使用MySQL系统提供的hash函数作为表达式。在使用key分区的时候,如果没有指定分区键,则会使用表的主键作为分区键,如果没有主键,则使用唯一索引列作为分区键,这个唯一索引列不包含null类型的值。

  • linear key分区:和key按键分区类似,只不过在底层实现觉得数据应该存储到哪个分区的时候使用的算法不一样。它和key按键分区的区别与linear hash分区 vs hash分区的区别一样。在对分区的添加、修改、删除、合并等操作上,linear key分区的效率要高于普通的key按键分区。

  • 复合分区:这种分区是基于前面的几种(range、list、hash、key)分区 进行组合而产生的一种分区。就是在某一个分区中的数据,再根据另外一个分区规则进行再次分区,而再次分区所产生的分区称之为子分区。

下面我们针对每一种类型的分区,进行详细的示例说明。


range范围分区

下面我们创建一个range范围分区表,来真实地感受一下MySQL中的range分区表。

创建range范围分区表的示例语句如下,其中create_date创建日期字段是我们进行范围分区的字段。其中注意less than关键字后面跟着的边界值,它是一个右侧开区间,当前分区不包含less than后面的边界值。

drop table if exists range_partition_table;
create table range_partition_table(
id int auto_increment,
code varchar(16),
create_date date,
primary key(id, create_date)
) partition by range columns(create_date) (
partition p1 values less than('2020-02-01'), /*p1分区不包含2020-02-01这一天的数据*/
partition p2 values less than('2020-03-01'),
partition p3 values less than('2020-04-01'),
partition p4 values less than('2020-05-01'),
partition px values less than maxvalue
);

注意:范围分区的时候,不支持使用datedatetime以外的日期或时间类型的列作为分区列。

插入测试数据:

insert into range_partition_table(id, code, create_date) values (null, 'A', '2020-01-04');
insert into range_partition_table(id, code, create_date) values (null, 'B', '2020-01-31');
insert into range_partition_table(id, code, create_date) values (null, 'C', '2020-02-01');
insert into range_partition_table(id, code, create_date) values (null, 'D', '2020-02-29');
insert into range_partition_table(id, code, create_date) values (null, 'E', '2020-03-01');
insert into range_partition_table(id, code, create_date) values (null, 'F', '2020-03-31');
insert into range_partition_table(id, code, create_date) values (null, 'G', '2020-04-01');
insert into range_partition_table(id, code, create_date) values (null, 'H', '2020-04-30');
insert into range_partition_table(id, code, create_date) values (null, 'I', '2020-05-01');
insert into range_partition_table(id, code, create_date) values (null, 'J', '2020-05-31');
insert into range_partition_table(id, code, create_date) values (null, 'K', '2020-06-01');
insert into range_partition_table(id, code, create_date) values (null, 'L', '2020-06-30');
insert into range_partition_table(id, code, create_date) values (null, 'M', '2020-07-01');

查看插入测试数据后的分区表的详细信息:

MySQL中的分区表

分区表在MySQL数据库服务器上,每一个分区对应着一个数据文件,他们共用一个.frm表结构文件,然后每一个分区是一个单独的数据文件。如下图所示:

MySQL中的分区表


list列表分区

列表分区比较适合哪种已经确定某个字段存储值的取值范围的情况下,根据这个字段来分区存储数据。下面我们举例说明一下这种分区的使用方式。创建list列表分区表的结构如下:

drop table if exists list_partition_table;
create table list_partition_table(
id int auto_increment,
type int,
create_date date,
primary key(id, type)
) partition by list columns(type) (
partition p1 values in (1, 2),
partition p2 values in (3, 4),
partition p3 values in (5, 6)
);

创建好表之后,插入下面的测试数据:

insert into list_partition_table(id, type, create_date) values(null,1,now());
insert into list_partition_table(id, type, create_date) values(null,2,now());
insert into list_partition_table(id, type, create_date) values(null,3,now());
insert into list_partition_table(id, type, create_date) values(null,4,now());
insert into list_partition_table(id, type, create_date) values(null,5,now());
insert into list_partition_table(id, type, create_date) values(null,6,now());

使用前面我们提到的查看分区表详细分区信息的SQL语句,查询该分区表的详细信息如下:

MySQL中的分区表


columns多列分区

多列分区在某种意义上属于范围分区和列表分区的一种变形,它解决了range范围分区和list列表分区中只能指定一个列作为分区键的限制,这种分区方式可以一次性的选择多个列放在一起作为一个分区键来对数据进行分区。它是基于range和list两种分区之上来创建的,所以可以分为range columns范围多列分区和list columns列表多列分区两种。

在使用多列分区的时候,设置的分区列和设置的分区列的值要顺序和数目都对应上才可以。比如你选择了3个列作为分区键,那么你设置各个分区的边界值的时候,也要指定3个列的值,并且字段类型的顺序也需要对应起来。

针对这两种分区分别演示一下。


range columns范围多列分区

创建范围多列分区的示例如下:

drop table if exists range_columns_partiton_tab;
create table range_columns_partiton_tab(
id int auto_increment,
a int,
b int,
c varchar(16),
d int,
primary key(id,a,d,c)
) partition by range columns(a,d,c) (
partition p1 values less than (10,15,'d'),
partition p2 values less than (20,20,'g'),
partition p3 values less than (35,45,'k'),
partition p4 values less than (40,60,'p'),
partition px values less than (maxvalue,maxvalue,maxvalue)
);

创建好分区之后,插入如下测试数据

insert into range_columns_partiton_tab(id,a,b,c,d) values(null,9,1,'b',14);/*p1的数据*/
insert into range_columns_partiton_tab(id,a,b,c,d) values(null,10,1,'d',15);/*p2的数据*/
insert into range_columns_partiton_tab(id,a,b,c,d) values(null,22,1,'h',22);/*p3的数据*/
insert into range_columns_partiton_tab(id,a,b,c,d) values(null,37,1,'m',50);/*p4的数据*/
insert into range_columns_partiton_tab(id,a,b,c,d) values(null,40,1,'p',60);/*px的数据*/

插入数据后,使用前面我们提到的查询分区表详细信息的SQL语句,可以得到如下的结果:

MySQL中的分区表


从上面的例子可以看出,分区键排列的顺序和表中字段定义的顺序可以不一致,上面的例子中表字段的顺序是a,c,d,但是我们在定义分区字段的时候使用的顺序是a,d,c。但是我们在给分区字段进行定义边界值的时候,需要按照a,d,c的顺序来定义边界值。


list columns列表多列分区

列表多列分区和list分区差不多,只是此时选择分区键的时候,不仅仅可以支持int类型的列作为分区键,还可以选择varchardatetimestamp类型的列作为分区字段。所以,列表多列中的多列并不是像范围多列分区中的多列那样指多个列一起作为分区字段,而是指支持多种字段类型来作为分区字段。

下面给出一个使用字符串类型的字段作为分区字段的分区表:

drop table if exists list_columns_partition_tab;
create table list_columns_partition_tab(
id int auto_increment,
area_code varchar(16),
create_date date,
primary key(id, area_code)
) partition by list columns(area_code) (
partition p1 values in ('010','020'),
partition p2 values in ('021','022'),
partition p3 values in ('023','024')
);

创建好表之后,插入下面的测试数据:

insert into list_columns_partition_tab(id, area_code, create_date) values(null,'010',now());
insert into list_columns_partition_tab(id, area_code, create_date) values(null,'020',now());
insert into list_columns_partition_tab(id, area_code, create_date) values(null,'021',now());
insert into list_columns_partition_tab(id, area_code, create_date) values(null,'022',now());
insert into list_columns_partition_tab(id, area_code, create_date) values(null,'023',now());
insert into list_columns_partition_tab(id, area_code, create_date) values(null,'024',now());

使用前面我们提到的inoformation.PARTITIONS表去查看list列表分区表的详细分区信息如下所示:

MySQL中的分区表


当我们向列表分区表中插入一个不存在的列表值的时候,会出现如下的错误,所以,我们要保证我们的分区已经全部以后分区键可能出现的值,否则插入数据的时候会失败,如下所示:

mysql> insert into list_columns_partition_tab(id, area_code, create_date) values(null,'999',now());
ERROR 1526 (HY000): Table has no partition for value from column_list
mysql>


hash哈希分区

哈希分区是根据分区字段的值,进行hash然后与分区的数目取余,得到的余数就是这个数据所在分区的编号。下面举例说明。

drop table if exists hash_partition_table;
create table hash_partition_table(
id int auto_increment,
store_code int,
create_date date,
primary key(id, store_code)
) partition by hash(store_code)
partitions 4;

对hash分区表,插入测试数据如下所示:

insert into hash_partition_table(id, store_code, create_date) values(null, 10, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 11, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 12, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 13, now());

查询hash分区后的详细信息如下:

MySQL中的分区表


注意:hash分区在选择分区键的时候,只能选择integer类型的列才可以作为分区键,如果我们使用一个非integer类型的列作为hash分区键的时候,如有如下错误:

mysql> create table hash_partition_table(
-> id int auto_increment,
-> store_code varchar(16),
-> create_date date,
-> primary key(id, store_code)
-> ) partition by hash(store_code)
-> partitions 4;
ERROR 1659 (HY000): Field 'store_code' is of a not allowed type for this type of partitioning
mysql>

当然我们也可以选择一个非integer类型的字段作为hash分区的分区键,但是需要对这个字段进行一个函数的转换,将这个非integer类型的字段转换为integer类型的字段后再作为hash分区的分区键,如下所示,获取create_date的年作为一个integer类型值,然后基于这个值做hash分区。这就是在hash分区中使用了自定义表达式的方式来将一个可以转回为integer类型的字段转换为interger类型之后再做为hash分区的分区键。

drop table if exists hash_partition_table;
create table hash_partition_table(
id int auto_increment,
store_code int,
create_date date,
primary key(id, create_date)
) partition by hash(year(create_date))/*这里使用了year(xxx)自定义表达式,来满足hash分区的分区键必须为interger类型的条件。*/
partitions 4;


linear hash线性哈希分区

线性哈希分区在创建是使用的时候,和普通的哈希分区相比,没有什么区别,唯一的区别就是它在创建的时候比普通的哈希多了一个关键字linear。它的创建如下所示:

drop table if exists linear_hash_partition_table;
create table linear_hash_partition_table(
id int auto_increment,
store_code int,
create_date date,
primary key(id, store_code)
) partition by linear hash(store_code)
partitions 4;

插入如下测试数据:

insert into linear_hash_partition_table(id, store_code, create_date) values(null, 10, now());
insert into linear_hash_partition_table(id, store_code, create_date) values(null, 11, now());
insert into linear_hash_partition_table(id, store_code, create_date) values(null, 12, now());
insert into linear_hash_partition_table(id, store_code, create_date) values(null, 13, now());

查询详细的分区信息如下,从中可以看出里面的分区方式是LINEAR HASH,而不是普通的HASH

MySQL中的分区表


key按键分区

key分区和hash分区类似,但是它不支持自定义分区的表达式,只能使用MySQL自带的hash函数,它不仅仅支持interger类型的列作为分区键,支持除了blob、text之外的所有类型的字段作为分区键。在创建key按键分区表的时候,可以显示的指定分区键,也可以显示的指定分区键。下面看一个示例:

drop table if exists key_partition_table;
create table key_partition_table(
id int auto_increment,
code varchar(16),
create_date date,
primary key(id)
) partition by key()/*这里没有指定使用哪个列作为分区键,则会使用上面表中的主键id作为分区键*/
partitions 4;

/*下面的SQL和上面的SQL是等价的,都是使用id作为key分区的分区键,只不过下面的SQL显示的指定的分区键的列*/
drop table if exists key_partition_table;
create table key_partition_table(
id int auto_increment,
code varchar(16),
create_date date,
primary key(id)
) partition by key(id)/*这里指定使用表中的主键id作为分区键*/
partitions 4;

上述两种方式在创建完成分区表之后,查看分区表详细信息的时候,有一点区别如下,如果不指定分区键,则在相信分区信息中是看不到分区的表表达式的,但是这并不影响分区的功能,所以说不要以为这样的key分区表示有问题的。

MySQL中的分区表



如下的建表语句就会出现错误,疑问它没有主键,也没有唯一索引列,所以在按键分区的时候,它不知道该使用哪个列作为分区键。

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date
) partition by key()/*没有主键、也没有非空的唯一索引,所以创建按键分区失败*/
partitions 4;

/*错误如下*/
ERROR 1488 (HY000): Field in list of fields for partition function not found in table

如下的SQL是可以成功了,因为里面虽然没有主键,但是有一个唯一索引列,并且唯一索引指定了not null非空约束。

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16) not null,/*这个唯一索引增加了not null非空的约束,所以才可以使用这个唯一索引列作为分区键*/
create_date date,
unique key(code)
) partition by key()/*这里没有指定使用哪个列作为分区键,则会使用上面表中的唯一索引列code作为分区键*/
partitions 4;

如果我们使用下面的SQL语句创建表就会失败,因为虽然有唯一索引,但是这个唯一索引,没有增加not null非空的约束,按键分区可以使用唯一约束作为分区键,但是这个唯一索引不能包含null值。注意这里有一个隐藏的知识点:主键和唯一键(唯一索引)的区别是什么?主键中不能有null空值,而唯一键(唯一索引)中是可以包含null空值的。而此时在使用key按键分区的时候,如果不指定主键,那么使用表中非空的唯一索引列,而这个列不就是表中的一个主键列吗?只是没有显示的使用primary key关键字指定主键列是谁。

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),/*唯一约索引,没有增加not null约束,所以创建按键分区失败*/
create_date date,
unique key(code)
) partition by key()
partitions 4;

/*错误如下*/
ERROR 1488 (HY000): Field in list of fields for partition function not found in table

当然,我们也是可以在创建key按键分区的时候,指定分区键的列是哪一个,如下所示。但是要求这个列必须是主键中的某一个列,或者是一个非空的唯一索引列。如果满足这两个条件,则有如下错误:

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date,
unique key(code)/*指定code为唯一索引列*/
) partition by key(create_date)/*指定了使用create_date作为按键分区的分区键,但是这个列不是主键,也不是非空的唯一索引列*/
partitions 4;

/*错误信息如下:*/
ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table's partitioning function


drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date,
primary key(id)/*指定id为主键列*/
) partition by key(create_date)/*指定了使用create_date作为按键分区的分区键,但是这个列不是主键,也不是非空的唯一索引列*/
partitions 4;

/*错误信息如下:*/
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function

如下才是指定key按键分区键的正确方式:

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date,
primary key(id)/*指定id为主键列*/
) partition by key(id)/*使用主键id作为按键分区的分区键*/
partitions 4;


drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date,
unique key(code)/*指定code为唯一索引列*/
) partition by key(code)/*使用唯一索引列code作为按键分区的分区键*/
partitions 4;

向key按键分区表中,插入测试数据如下:

insert into key_partition_table(id, code, create_date) values(1,'A',now());
insert into key_partition_table(id, code, create_date) values(2,'B',now());
insert into key_partition_table(id, code, create_date) values(3,'C',now());
insert into key_partition_table(id, code, create_date) values(4,'D',now());

查看详细的分区信息和数据如下:

MySQL中的分区表



key分区的时候,不仅仅只支持一个列作为分区键,还可以选择多个列作为分区键,如下所示,也是可以的。

drop table if exists key_partition_table;
create table key_partition_table(
id int,
code varchar(16),
create_date date,
unique key(id,code)/*指定id, code为唯一索引列*/
) partition by key(id,code)/*使用唯一索引列id, code作为按键分区的分区键*/
partitions 4;


linear key线性按键分区

linear key分区和linear hash分区类似。它是key按键分区的一种变形。在创建语法上只不过增加了linear关键字而已。它的优点是合并、删除、修改、拆分分区的时候比普通的key按键分区效率要高。就像linear hash分区和普通的hash分区的区别一样。

具体创建linear key分区的示例如下:

drop table if exists linear_key_partition_table;
create table linear_key_partition_table(
id int,
code varchar(16),
create_date date,
unique key(code)/*指定code为唯一索引列*/
) partition by linear key(code)/*使用唯一索引列code作为按键分区的分区键*/
partitions 4;


复合分区

复合分区又称为子分区,虽然是前面的几种分区组合之后产生的,但是它们的组合并不是任意组合都可以的,而是由规律个限制的。目前的复合分区仅仅支持在range范围分区和list列表分区下面创建子分区,并且这个子分区的类型也只能是hash分区或者key分区。详细的组合方式下面分别展开说明。

  • range范围分区下面支持的子分区有如下两种:

    • hash哈希分区

    • key按键分区

  • list列表分区下面支持的子分区有如下两种:

    • hash哈希分区

    • Key按键分区

range-hash复合分区

range-hash复合分区是指先根据某个列进行范围分区,然后再基于每一个分区进行hash分区。示例如下:

drop table if exists range_hash_partition_tab;
create table range_hash_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by range columns(create_date) /*根据create_date拆分为4个范围分区*/
subpartition by hash(type)
subpartitions 2 /*每个分区再根据type拆分为2个子分区*/
(
partition p1 values less than ('2020-02-01'),
partition p2 values less than ('2020-03-01'),
partition p3 values less than ('2020-04-01'),
partition px values less than maxvalue
);

创建完成分区表之后,在MySQL服务器上面的数据库目录下面,存储的文件如下所示,可以从下面的图中看出,这个分区表有4个分区,分别为p1、p2、p3、px四个分区,然后每一个分区下面又有两个子分区,分别为sp0、sp1。所以这个表一共有4*2=8个分区。

MySQL中的分区表

插入测试数据

insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 4, '2020-01-11','p1分区中sp0子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 7, '2020-01-14','p1分区中sp1子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 8, '2020-02-22','p2分区中sp0子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 5, '2020-02-24','p2分区中sp1子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 2, '2020-03-07','p3分区中sp0子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 3, '2020-03-13','p3分区中sp1子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 6, '2021-01-22','px分区中sp0子分区的数据',1);
insert into range_hash_partition_tab(id, type, create_date, col1, col2) values(null, 1, '2021-01-11','px分区中sp1子分区的数据',1);

分区的详细信息如下:

MySQL中的分区表


range-key复合分区

范围按键复合分区的示例如下,需要注意的时候,在key按键分区作为子分区的时候,需要指定key分区的分区键,不能向单独创建一个按键分区表那样,可以不指定分区键,在key分区作为子分区的时候,需要显示的声明key分区的分区键。

drop table if exists range_key_partition_tab;
create table range_key_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by range columns(create_date) /*根据create_date拆分为4个范围分区*/
subpartition by key(type)
subpartitions 2 /*每个分区再根据type拆分为2个子分区*/
(
partition p1 values less than ('2020-02-01'),
partition p2 values less than ('2020-03-01'),
partition p3 values less than ('2020-04-01'),
partition px values less than maxvalue
);

插入初始化数据如下:

insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 4, '2020-01-11','p1分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 7, '2020-01-14','p1分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 8, '2020-02-22','p2分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 5, '2020-02-24','p2分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 2, '2020-03-07','p3分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 3, '2020-03-13','p3分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 6, '2021-01-22','px分区',1);
insert into range_key_partition_tab(id, type, create_date, col1, col2) values(null, 1, '2021-01-11','px分区',1);

分区表的详细信息如下:

MySQL中的分区表


list-hash复合分区

list-hash复合分区是先根据list分区,然后在每一个list分区中,再按照hash来分区。示例如下:

drop table if exists list_hash_partition_tab;
create table list_hash_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by list columns(type) /*根据type的值拆分为2个列表分区*/
subpartition by hash(year(create_date)) /*根据create_date的year()表达式,进行hash分区*/
subpartitions 2 /*每个分区再根据year(create_date)拆分为2个子分区*/
(
partition p1 values in (1, 3, 5, 7, 9),
partition p2 values in (2, 4, 6, 8, 10)
);

创建好复合分区后的表,在物理磁盘上存储如下所示。从中可以看出共有4个分区。先安装type进行列表分区,分成了两个分区,分别为p1、p2两个分区。然后再每一个分区上,又根据create_date进行了hash分区,分成了sp0、sp1两个子分区。

MySQL中的分区表

插入如下初始化数据,在区分是插入到p1还是p2的时候,是根据type的值来区分的,奇数值全部插入到p1分区中,偶数值全部插入到p2分区中。然后在p1p2的分区中,再根据year(create_date)的值与我们的子分区的数目2进行取余,再决定到底该分区到sp0还是sp1中。

insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 1, '2020-01-11','p1分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 3, '2021-01-14','p1分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 5, '2020-02-22','p1分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 7, '2021-02-24','p1分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 9, '2020-03-07','p1分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 2, '2021-03-13','p2分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 4, '2020-01-22','p2分区',1);
insert into list_hash_partition_tab(id, type, create_date, col1, col2) values(null, 6, '2021-01-11','p2分区',1);

详细的分区信息如下图所示:

MySQL中的分区表




list-key复合分区

list-key列表按键复合分区是先进行list列表分区,然后再每一个分区的基础上,再按照key进行分区。示例如下:

drop table if exists list_key_partition_tab;
create table list_key_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by list columns(type) /*根据type的值拆分为2个列表分区*/
subpartition by key(create_date) /*根据create_date进行按键分区*/
subpartitions 2 /*每个分区再根据create_date拆分为2个按键子分区*/
(
partition p1 values in (1, 3, 5, 7, 9),
partition p2 values in (2, 4, 6, 8, 10)
);

创建好表之后,可以在磁盘上看到如下数据文件:

MySQL中的分区表


插入如下测试数据:

insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 1, '2020-01-11','p1分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 3, '2021-01-14','p1分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 5, '2020-02-22','p1分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 7, '2021-02-24','p1分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 9, '2020-03-07','p1分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 2, '2021-03-13','p2分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 4, '2020-01-22','p2分区',1);
insert into list_key_partition_tab(id, type, create_date, col1, col2) values(null, 6, '2021-01-11','p2分区',1);

详细的分区信息如下所示:

MySQL中的分区表




复合分区综合示例

复合分区在设置子分区的时候,同时可以设置子分区的名称,并可以指定每一个分区的存储目录。看下面的这个示例:

drop table if exists list_key_partition_tab;
create table list_key_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by list columns(type) /*根据type的值拆分为2个列表分区*/
subpartition by key(create_date) /*根据create_date进行按键分区*/
(
partition p1 values in (1, 3, 5, 7, 9) (
subpartition s1,/*指定子分区的名称*/
subpartition s2 /*指定子分区的名称*/
),
partition p2 values in (2, 4, 6, 8, 10) (
subpartition s3,/*指定子分区的名称*/
subpartition s4 /*指定子分区的名称*/
)
);

上面的复合分区表创建完成之后,在磁盘上看下分区的效果,指定了子分区的名称,从下图中可以看出,子分区的名称确实如我们设置的规则生成的,分别为s1、s2、s3、s4四个子分区。当我们不设置子分区的名称的时候,子分区的名称是sp0、sp1这样的规则。

MySQL中的分区表

注意:上面的例子中,我们指定子分区的名称,需要为每一个分区都指定子分区的名称,假如我们有4个分区,不能只为其中的某一个或多个分区指定其分区内的子分区名称,需要为每一个分区都指定子分区的名称。要么全部指定,要么全都不指定。

下面我们再看另外一个例子,指定每一个分区的磁盘目录,这样对于分区表提高磁盘的I/O尤为重要。

drop table if exists list_key_partition_tab;
create table list_key_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by list columns(type) /*根据type的值拆分为2个列表分区*/
subpartition by key(create_date) /*根据create_date进行按键分区*/
(
partition p1 values in (1, 3, 5, 7, 9) (
subpartition s1
data directory = '/disk1/data',
subpartition s2
data directory = '/disk2/data'
),
partition p2 values in (2, 4, 6, 8, 10) (
subpartition s3
data directory = '/disk3/data',
subpartition s4
data directory = '/disk4/data'
)
);

上面的语句执行成功的前提是,指定的挂载的磁盘目录需要对mysql这个操作系统用户有对应的权限。如下,磁盘的目录是有owner的权限的,即执行了:chwon -R mysql:mysql /disk*/命令,否则在创建分区表的时候,可能会有ERROR 1030 (HY000): Got error 168 from storage engine的错误。

MySQL中的分区表

指定每一个分区存储目录的分区表,在磁盘上分布如下,只有数据文件在指定的磁盘目录上,表结构元数据信息文件还是在数据库feng的数据目录下面,但是在数据库目录下面会对应的.isl结尾的数据文件,.isl文件中只是存储的数据文件的真实路径是什么。

MySQL中的分区表



不仅仅是复合分区支持指定磁盘目录,非复合分区也支持指定磁盘目录,如下所示:

drop table if exists list_partition_tab;
create table list_partition_tab(
id int auto_increment,
type int,
create_date date,
col1 varchar(16),
col2 int,
primary key(id,create_date,type)
)
partition by list columns(type) /*根据type的值拆分为2个列表分区*/
(
partition p1 values in (1, 3, 5, 7, 9)
  data directory = '/disk1/data',
partition p2 values in (2, 4, 6, 8, 10)
data directory = '/disk2/data'
);

创建完成之后,分区表在磁盘上的目录结果如下所示:

MySQL中的分区表




分区表的管理

range和list分区

对于range和list两种类型的分区表的管理,接下来的各种演示示例:添加分区、删除分区、修改分区、合并拆分分区的各种操作,都是基于下面的这个range范围分区表来做演示,list类型的分区操作类似。

这个range范围分区表的建表语句和初始化数据如下所示:


/*创建range范围分区表*/
drop table if exists range_partition_table;
create table range_partition_table(
id int auto_increment,
code varchar(16),
create_date date,
primary key(id, create_date)
) partition by range columns(create_date) (
partition p1 values less than('2020-02-01'), /*p1分区不包含2020-02-01这一天的数据*/
partition p2 values less than('2020-03-01'),
partition p3 values less than('2020-04-01'),
partition p4 values less than('2020-05-01'),
partition px values less than maxvalue
);

/*插入测试数据*/
insert into range_partition_table(id, code, create_date) values (null, 'A', '2020-01-04');
insert into range_partition_table(id, code, create_date) values (null, 'B', '2020-01-31');
insert into range_partition_table(id, code, create_date) values (null, 'C', '2020-02-01');
insert into range_partition_table(id, code, create_date) values (null, 'D', '2020-02-29');
insert into range_partition_table(id, code, create_date) values (null, 'E', '2020-03-01');
insert into range_partition_table(id, code, create_date) values (null, 'F', '2020-03-31');
insert into range_partition_table(id, code, create_date) values (null, 'G', '2020-04-01');
insert into range_partition_table(id, code, create_date) values (null, 'H', '2020-04-30');
insert into range_partition_table(id, code, create_date) values (null, 'I', '2020-05-01');
insert into range_partition_table(id, code, create_date) values (null, 'J', '2020-05-31');
insert into range_partition_table(id, code, create_date) values (null, 'K', '2020-06-01');
insert into range_partition_table(id, code, create_date) values (null, 'L', '2020-06-30');
insert into range_partition_table(id, code, create_date) values (null, 'M', '2020-07-01');

原始range分区表的详细信息如下图所示:

MySQL中的分区表




添加分区

这里管理分区部分,我们都使用range范围分区来做演示,

如果要添加一个分区,则只需如下的SQL语句

alter table range_partition_table add partition (partition p5 values less than ('2020-06-01'));

在添加分区的时候,需要注意如果你的分区表类型是range范围分区,并且制定了最后一个范围分区的范围边界为maxvalue,那么你不能直接在这样的一个range范围分区表上增加分区,否则会出现如下的错误:

mysql> alter table range_partition_table add partition (partition p5 values less than ('2020-06-01'));
ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

对于这样的情况,你只能针对倒数第二个分区拆分为一个新的分区。或者是删除掉最后一个分区。然后执行add partition的操作,但是这样将会丢失最后一个分区的数据,所以不建议这么做。最好的办法是在创建range范围分区的时候,不要使用maxvalue作为最后一个分区的边界值,当我们发现分区不够的时候,就可以直接使用add partition语句增加分区了。如下所示:

mysql> /*删除掉最后一个包含maxvalue的范围分区后,再添加分区是可以成功的。*/
mysql> alter table range_partition_table drop partition px;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> alter table range_partition_table add partition (partition p5 values less than ('2020-06-01'));
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> /*建议不使用maxvalue设置最后一个分区的value边界值。或者使用如下的方式来拆分租后一个分区。*/
mysql> alter table range_partition_table reorganize partition px into (
-> partition p6 values less than ('2020-06-01'),
-> partition px values less than maxvalue
-> );
Query OK, 0 rows affected (0.08 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql>

对于列表分区来说,可以就没有range范围分区遇到的这个问题了,可以直接使用add partition语句进行添加分区的操作。只要添加的分区中的value,不和已经存在的分区有任何交集就可以。


删除分区

如果想要删除一个或多个分区,可以使用如下的语句:

alter table range_partition_table drop partition p1; /*删除一个分区,数据和分区都会被干掉*/
alter table range_partition_table drop partition p1, p2; /*删除多个分区,数据和分区都会被干掉*/

当你删除一个分区的时候,这个分区内所存储的数据也会被一并删除,这对一个普通的非分区表执行drop table xx的效果是一样的。所以,在删除分区之前一定要想清楚是否确定要删除这个分区。同时当你想删除某个分区的时候,你还需要对这个分区表有drop的权限才可以。

删除分区的语句仅适用于range分区和list分区,不适用于hash分区和key分区。如果尝试对一个hash分区使用drop partition语句的操作,会出现如下错误信息:

mysql> alter table hash_partition_table drop partition p1;
ERROR 1512 (HY000): DROP PARTITION can only be used on RANGE/LIST partitions
mysql>

如果你只是想删除数据,而不想删除分区,则只需如下的truncate partition语句:

alter table range_partition_table truncate partition p1; /*删除一个分区内的数据,分区会被保留*/

如果你要删除分区表中所有的数据,同时保留分区信息不被删除,使用如下的truncate table语句。

truncate table range_partition_table; /*把表中所有数据都干掉,分区信息会被保留*/


修改分区键

如果原先的涉及到分区表不够好,想重新选择一个新的分区键,或者是我们原先使用的分区类型不够好,先换一个分区类型。我们该如何操作?最简单方式并不是我们首先想到的重新创建一个新的分区表,然后后把数据导入到新的分区表中,再把旧表删除掉,在把新的分区表的名称改为旧的分区表的名称。这种方式当然可以,但是除了这种方式之外,我们还可以使用下面的语句来完成分区表的修改。

可以使用如下SQL语句把分区键进行更改,这样数据会根据新的分区键还有分区规则重新分布:

alter table range_partition_table
partition by range columns(id)(
partition p1 values less than (5),
partition p2 values less than (10),
partition px values less than maxvalue
);

上述SQL执行完成后,表的分区结构如下所示:

MySQL中的分区表



基于修改后的SQL语句,我们还可以使用如下的SQL对分区表的类型进行修改,如下的SQL语句是把一个range范围分区表改为一个hash分区表。

alter table range_partition_table
partition by hash(id)
partitions 4;

上述的SQL执行完成后,表的分区信息如下所示:

MySQL中的分区表

在上面的实验中,我们把一个range分区的表,显示修改了它的分区键,接着又把这个range分区表改为了hash分区表。可以发现数据是不会丢失的,并且可以根据我们修改的时候指定的分区键和分区规则自动的对原先的range分区表中的数据进行重新分区存储。


合并或拆分分区

我们可能会有这样的需求:想把已经存在的两个分区合并为一个分区,或者是想把一个已经存在的分区拆分为两个分区。此时我们就不能直接使用add paritiondrop partition两种命令的组合来实现,因为在我们执行drop partition的时候,会把数据也删除掉了。此时我们使用alter table xx reorganize partition语句来实现这样的需求。

我们下面的拆分分区和合并分区的操作都将基于前面我们提到的range范围分区表来演示。

把两个分区合并为一个分区的SQL语句如下,此时分区合并,并且数据不会丢失。两个分区内的数据会根据分区的规则,自动的合并在一个分区中。在合并分区的时候,不能把间隔的两个分区合并为一个分区。比如把p1p3合并为一个分区,因为中间还有一个分区p2。所以不能间隔着分区操作。同时,使用reorganize命令不能把分区类型修改掉,比如把range分区修改为list分区。

alter table range_partition_table reorganize partition p1,p2 into (
partition p1_and_p2 values less than('2020-03-01')
);

把一个分区拆分为两个分区的语句如下:

alter table range_partition_table reorganize partition p4 into (
partition p4_1 values less than('2020-04-15'),
partition p4_2 values less than('2020-05-01')
);

经过上面两步合并和拆分分区的操作之后,结果如下。我们可以看到分区p1p2已经合并为一个分区p1_and_p2。分区p4也被拆分成了两个分区:p4_1p4_2。但是每一个分区的数据量重下面的截图中可以看到还有些不对,但是我们把分区表执行一下分析命令之后就可以看到正确的统计结果了。

MySQL中的分区表


执行如下命令,再次查看修改后的分区详细信息如下:

analyze table range_partition_table;

MySQL中的分区表



hash和key分区

我们基于下面的这个hash分区表来做演示

drop table if exists hash_partition_table;
create table hash_partition_table(
id int auto_increment,
store_code int,
create_date date,
primary key(id, store_code)
) partition by hash(store_code)
partitions 4;

对hash分区表,插入测试数据如下所示:

insert into hash_partition_table(id, store_code, create_date) values(null, 10, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 11, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 12, now());
insert into hash_partition_table(id, store_code, create_date) values(null, 13, now());

查询hash分区后的详细信息如下:

MySQL中的分区表




添加分区

把原先4个hash分区的分区表,增加2个hash分区,使用如下的SQL语句:

alter table hash_partition_table add partition partitions 2;

添加2个hash分区后的结果如下所示,我们可以发现原的4个分区下载变成了6个分区

MySQL中的分区表




删除分区

基于上面添加后的分区表,把现在6个hash分区的分区表,减少4个hash分区,调整为2个分区,使用如下的SQL:

alter table hash_partition_table coalesce partition 4;

最后结果如下所示,可以看到分区已经调整为2个分区,并且数据也没有丢失,这就是hash、key分区和range、list分区一个比较明显的区别:删除分区后,hash、key分区中的数据不会丢失,而range、list分区中的数据会随着分区的删除而一起别删除。

MySQL中的分区表

hash分区和key分区的删除分区的操作,其实就是收缩分区的一个操作。原先的数据均匀的分布在n个分区中,如果要删除几个分区,就会触发数据重新分布的操作,把数据重新根据分区的规则再次均匀的分布到新的分区中。


修改分区

hash分区和key分区的修改分区,前面我们说的增加分区和删除分区的操作也是一种修改的行为,除此之外呢,我们可以使用如下的命令把hash分区修改为key分区,或者把key分区修改为hash分区,或者是把hash分区的分区键进行修改也是可以的。下面分别演示一下:

/*查看原先的hash分区表的结构*/
mysql> show create table hash_partition_tableG
*************************** 1. row ***************************
Table: hash_partition_table
Create Table: CREATE TABLE `hash_partition_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_code` int(11) NOT NULL,
`create_date` date DEFAULT NULL,
PRIMARY KEY (`id`,`store_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY HASH (store_code)
PARTITIONS 4 */

1 row in set (0.01 sec)

/*把原先的hash分区修改key分区,同时把分区键从store_code改为id,还修改的了分区的数目由4变成2。*/
mysql> alter table hash_partition_table partition by key(id) partitions 2;
Query OK, 4 rows affected (0.09 sec)
Records: 4 Duplicates: 0 Warnings: 0

/*查看修改后的结果,从下面的输出可以看到,已经从hash(store_code)的哈希分区改为key(id)的按键分区,
分区数目也变成了2个。*/

mysql> show create table hash_partition_tableG
*************************** 1. row ***************************
Table: hash_partition_table
Create Table: CREATE TABLE `hash_partition_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_code` int(11) NOT NULL,
`create_date` date DEFAULT NULL,
PRIMARY KEY (`id`,`store_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY KEY (id)
PARTITIONS 2 */

1 row in set (0.01 sec)

/*把分区类型从key分区再改回hash分区,同时修改了分区的数目有2个改为3个,但是没有修改分区键的列。*/
mysql> alter table hash_partition_table partition by hash(id) partitions 3;
Query OK, 4 rows affected (0.05 sec)
Records: 4 Duplicates: 0 Warnings: 0

/*查看修改后的结果,从下面的输出可以看到,已经从key(id)按键分区改为hash(id)哈希分区。
分区的数目也从2改为了3。*/

mysql> show create table hash_partition_tableG
*************************** 1. row ***************************
Table: hash_partition_table
Create Table: CREATE TABLE `hash_partition_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_code` int(11) NOT NULL,
`create_date` date DEFAULT NULL,
PRIMARY KEY (`id`,`store_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY HASH (id)
PARTITIONS 3 */

1 row in set (0.01 sec)

/*修改分区键,把分区键从id改为store_code,其他不变。*/
mysql> alter table hash_partition_table partition by hash(store_code) partitions 3;
Query OK, 4 rows affected (0.16 sec)
Records: 4 Duplicates: 0 Warnings: 0

/*查看修改后的结果,从下面的输出可以看到,分区键已经从hash(id)改为hash(store_code)。*/
mysql> show create table hash_partition_tableG
*************************** 1. row ***************************
Table: hash_partition_table
Create Table: CREATE TABLE `hash_partition_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`store_code` int(11) NOT NULL,
`create_date` date DEFAULT NULL,
PRIMARY KEY (`id`,`store_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4
/*!50100 PARTITION BY HASH (store_code)
PARTITIONS 3 */

1 row in set (0.01 sec)

mysql>


合并或拆分分区

hash分区和key分区,不涉及到拆分和合并分区的操作。它们的分区规则和特点决定了它们没有办法拆分或合并。只有增加和收缩的分区的操作。

收缩、减少分区的操作就相当于是合并分区的操作,比如原先有10个hash分区,现在减少到5个分区,此时的收缩分区的操作就相当于是合并分区的操作。

增加分区的操作就相当于是拆分分区的操作。比如原先有5个hash分区,现在增加到10个分区,此时的增加分区的操作就相当于是拆分分区的操作。


分区的维护

分析分区、检查分区、修复分区等操作。

分区的维护涉及到加检查分区、分析分区、修复分区等操作。在分区中的数不断地进行增删改查的时候,可能会造成数据空洞、磁盘碎片。定期的进行分区的维护工作是有必要的。对应提高分区表的性能也是很重要的。


重建分区

使用下面的语句重建分区。重建分区等同于先删除分区,然后重新创建分区,然后在插入分区中的数据。重建分区对于整理磁盘碎片很有效果。

alter table hash_partition_table rebuild partition p0, p1;


优化分区

使用下面的语句来优化分区。如果分区中的数据有大量的删除、修改的操作,执行这个命令可以有效的回收没有被使用的空间,并整理分区中的数据。

alter table hash_partition_table optimize partition p0, p1;

使用optimize partition的命令等同于以此执行了check partitionanalyze partitionrepair partition三个操作。


分析分区

使用下面的语句来分析分区,分析分区用于统计分区中信息,便于在选择执行计划的时候可以选择正确有效的执行计划。

alter table hash_partition_table analyze partition p0, p1;


检查分区

使用下面的语句来检查分区,当分区有异常问题的时候,使用这个命令可以检查出分区是否有问题。

alter table hash_partition_table check partition p0, p1;


修复分区

使用下面的语句来修复分区,这适用于分区损坏的情况。当检查出分区中有异常问题的时候,可以使用repair partition的命令来尝试修复分区。

alter table hash_partition_table repair partition p0, p1;

分区表的注意事项

分区表在使用的时候有很多注意事项,下面是我们需要注意的几点:

  • 子分区只有range范围分区和list列表分区才支持有子分区,hash分区和key分区不支持子分区。

  • range分区的每个分区包含那些分区表达式的值位于一个给定的连续区间内的行。这些区间要连续且不能相互重叠。

  • list分区只支持整形字段或返回整形数的表达式,每个分区列表里的值列表必须整数。

  • hash分区类型只支持整形字段或返回整形数的表达式。

  • key类型只支持列名形式(可一个或多个列名),不支持表达式。

  • 若表有primary key或unique key,则分区表的分区列必须包含在primary key或unique key列表里,这是为了确保主键的效率,否则同一主键区的东西一个在A分区,一个在B分区,显然会比较麻烦。

  • 一个分区表的所有分区必须使用相同的存储引擎,要和表的存储引擎要一致。如果有子分区,子分区的存储引擎也要和表的存储引擎一致。

  • hash哈希分区和key按键分区,删除一个分区数据不会丢失,原先存储在被删除的分区中的数据会自动根据新的分区数重新均匀的分布到剩余保留的分区中。
    range范围分区和list列表分区,删除分区之后,相应分区数据会一并被删除。

  • 一个分区表最多支持1024个分区,包括子分区的数目在内,总数不能超过1024。这个分区的数目应该可以支持大多数业务需求。

  • innodb存储引擎的分区表,不能引用其他表的字段作为自己的外键,也不能别其他表引用自己表中的字段。即为:分区和外键的支持二选一。

  • InnoDB不支持使用多个磁盘作为子分区,目前只有MyISAM支持。

  • 慎重选择分区键,避免跨分区扫描,要结合实际的业务来觉得分区键。任何脱落的业务来谈分区键都是无意义的。

innodb存储的分区表,不支持使用optimize partition优化分区的命令,需要使用rebuild partitonanalyze partition来代替。如下所示:

总结

如果遇到性能上的瓶颈,我们可以考虑使用分区表的方式来尝试优化,因为这样在底层存储的时候是分库存储,但是在整体的逻辑上它还是属于同一个表,这样就避免了真正的物理上分库分表的方式来解决性能的问题。但是分区表的方式如果不是分磁盘存储各个分区,在性能上提升是有限的。

如果项目中已经引入的数据库中间件,建议还是直接使用物理层面上的分库分表。如果还没有引入数据库中间件,可以尝试使用分区表的方式来解决性能的瓶颈。

注意:在选择使用哪种分区、选择分区键的时候要根据实际的业务结合各种分区方式和分区的条件限制来仔细衡量,如果选择不当,性能不仅没有提升反而会有下降的现象。


第一时间了解更多干货分享,还有各类视频教程资源。扫描它,带走我

以上是关于MySQL中的分区表的主要内容,如果未能解决你的问题,请参考以下文章

MySQL中的分区HASH分区

mysql分区交换exchange partition

部分代码片段

MySQL 分区修剪始终包括不等式查询中的第一个分区

linux中怎么查看mysql数据库版本

mysql水平分表和分区有啥区别