Mysql分区大全及讲解

2022-04-27
2204

MySQL

一、查看Mysql是否支持分区

show variables like ‘%partition%’;
show plugins;

二、基于InnerDB创建分区表

create table tab_name(
->id int not null
->name varchar(30)
->)engine=InnoDB
->partition by hash(id)
->partitions 5;



三、分区的优势
存储更多的数据
优化查询
并行处理
快速删除数据
更大的数据吞吐量
四、分区的类型分为range分区,list分区,hash分区,key分区,再此基础上又派生出了columns分区和子分区
1、range分区:根据一个连续的区间范围,将数据分散存储与不同的分区,支持对字段名或表达式进行分区
2、list分区:根据给定的值列表,将数据分散存储到不同的分区,支持对字段名或者表达式进行分区
3、hash:根据给定的分区个数,结合一定的hash算法,将数据分散存储到不同的分区,可以使用用户自定义的函数。
4、key分区:与hash分区类似,但是只能使用mysql自带的hash函数
5、columns分区:为解决MySQL5.5版本之前RANGE分区和LIST分区支持整数分区而在MySQL5.5版本新引入的分区类型
6、子分区:对数据表中的每个分区再次进行分区
注意:RANGE分区与LIST分区有一定的相似性,RANGE分区是基于一个连续的区间范围分区,而LIST分区是基于一个给定的值列表进行分区,HASH分区与KEY分区类似,HASH分区既可以使用MySQL本身提供的HASH函数进行分区,也可以使用用户自定义的表达式分区,而KEY分区只能使用MySQL本身提供的函数进行分区
注意:在MySQL所有的分区类型中,进行分区的数据表可以不存在主键或者唯一键,如果存在主键或者是唯一键,则不能使用主键或者是唯一键之外的其他字段进行分区操作。
实例:

create table tab_partition_test(
->id int not null primary key,
->year int
->)ENGINE=InnoDB
->PARTITION BY RANGE(year)(
PARTITION part0 VALUES LESS THAN (100),
PARTITION part1 VALUES LESS THAN (200),
PARTITION part2 VALUES LESS THAN (300),
PARTITION part3 VALUES LESS THAN (400),
PARTITION part4 VALUES LESS THAN (500),
->);



以上的代码以主键之外的字段进行分区,MySQL会报错,此时去除主键约束。

create table tab_partition_test(
->id int not null,
->year int
->)ENGINE=InnoDB
->PARTITION BY RANGE(year)(
PARTITION part0 VALUES LESS THAN (100),
PARTITION part1 VALUES LESS THAN (200),
PARTITION part2 VALUES LESS THAN (300),
PARTITION part3 VALUES LESS THAN (400),
PARTITION part4 VALUES LESS THAN (500),
->);



或者

create table tab_partition_test(
->id int not null primary key,
->year int
->)ENGINE=InnoDB
->PARTITION BY RANGE(id)(
->PARTITION part0 VALUES LESS THAN (100),
->PARTITION part1 VALUES LESS THAN (200),
->PARTITION part2 VALUES LESS THAN (300),
->PARTITION part3 VALUES LESS THAN (400),
->PARTITION part4 VALUES LESS THAN (500),
->);



五、RANGE分区
1、创建分区表tab_01

create table tab_01(
->id int not null,
->name varchar(500)
->gruop_id int not null
->)
->PARTITION BY RANGE(id)(
->PARTITION part0 VALUES LESS THAN (100),
->PARTITION part1 VALUES LESS THAN (200),
->PARTITION part2 VALUES LESS THAN (300),
->PARTITION part3 VALUES LESS THAN (400),
->PARTITION part4 VALUES LESS THAN (500),
->);



2、查看分区表的数据分布

select 
->partition_name part,
->partition_expression expr,
->partition_description part_desc,
->table_rows
->from
->information_schema.partitions
->where
->table_schema=schema()
->AND table_name='tab_01';



3、我们定义的tab_01的最大的分区是500,如果插入id大于500的数据时就会报错
我们可以为tab_01天机一个分区,让大于的数据都存在这个分区中

alter table tab_01
->add PARTITION (
->PARTITION part5 values less than maxvalue
->);



4、使用show create table tab_01 \G查看表信息
5、删除分区

alter table tab_01 drop PARTITION part2;



注意:删除分区后,此分区里面的数据也会被删除

6、查看表结构

show create table tab_01;



7、重新定义分区
将分区part4分为part4和part5


alter table tab_01
->REORGANIZE PARTITION part4 into(
->PARTITION part4 VALUES LESS THAN (450),
->PARTITION part5 VALUES LESS THAN (500),
->);



8、合并分区
将分区part4和part5合并成part4

alter table tab_01
->REORGANIZE PARTITION part4,part5 into(
->PARTITION part4 VALUES LESS THAN (500),
->);



注意:重定义分区不会删除数据
注意:RANGE只能对整数类型的字段分区,如果不是整数类型,需要将其他类型转化为整数类型

六、LIST分区
简介:list是一个逗号分隔的整数列表,不必按照某种顺序进行排列。在MySQL5.5版本之后,支持对非整数类型进行LIST分区

1、创建分区表

create table tab_01(
->id int not null,
->group_id int not null
->)PARTITION  BY LIST(group_id)(
->PARTITION part0 VALUES LESS in(1,2,5),
->PARTITION part1 VALUES LESS in(15,30,34),
->);



MySQL5.5之后可以使用下面的语句创建

create table tab_01(
->id int not null,
->group_id varchar(20) not null
->)PARTITION  BY LIST(group_id)(
->PARTITION part0 VALUES LESS in(’a‘,'g','h'),
->PARTITION part1 VALUES LESS in('p','o'),
->);



注意:如果添加的数据不存在定义的分区中会报错

2、添加分区
向数据表tab_01中添加分区part2,内容为22,33

alter table tab_01
->add PARTITION (
->PARTITION part5 values in(22,33)
->);



注意:为LIST添加分区的时候,需要注意的是对于分区列表中的特定的值,必须存在并且只能存在于一个分区中。

3、删除分区

alter table tab_01 drop PARTITION part5 ;



注意:删除分区后,对应的分区中的数据也会被一同删除。

4、重定义分区
重新定义part1,part2,part3,part4分区,合并part1和part4为新的part1分区

create table tab_01(
->id int not null,
->group_id int not null
->)PARTITION  BY LIST(group_id)(
->PARTITION part0 VALUES LESS in(1,2,5),
->PARTITION part1 VALUES LESS in(15,30,34),
->PARTITION part2 VALUES LESS in(200),
->PARTITION part3 VALUES LESS in(333,356),
->PARTITION part4 VALUES LESS in(400,450),
->);



alter table tab_01
->REORGANIZE PARTITION part1,part2,part3,part4 into(
->PARTITION part1 VALUES in (1,2,5,400,450),
->PARTITION part2 VALUES in (200),
->PARTITION part3 VALUES in in(333,356),
->);



注意:重定义LIST分区时,只支持重定义相邻的分区,重定义后的分区区间必须与原分区区间的范围相同,不支持使用重定义分区来修改分区的类型。

七、COLUMNS分区
COLUMNS分区MySQL 5.5 版本引入的新的分区类型,能够解决MySQL之前的版本中RANGE分区和LIST分区只支持整数分区的问题。COLUMNS分区可以分为RANGE COLUMNS分区和LIST COLUMNS分区。

RANGE COLUMNS分区和LIST COLUMNS分区都支持整数类型,日期时间类型和字符串类型。

1、RANGE COLUMNS分区
RANGE COLUMNS不仅增加了支持的数据类型,而且还能够对数据表中的多个字段进行分区。
实例:

create table tab_01(
->id int not null,
->t_name varchar(30) not null default '',
->group_id int not null,
->group_code int not null
->)PARTITION  BY RANGE COLUMNS(group_id,group_code)(
->PARTITION  part0 VALUES LESS THAN (1,10)
->PARTITION  part1 VALUES LESS THAN (10,20)
->PARTITION  part2 VALUES LESS THAN (10,30)
->PARTITION  part3 VALUES LESS THAN (10,MAXVALUE)
->PARTITION  part4 VALUES LESS THAN (MAXVALUE,MAXVALUE)
->);



注意:向RANGE COLUMNS分区表中插入数据时,会按照字段组进行比较,如果插入的数据与字段中的第一个字段值相同,则按照第二个字段值进行比较,直到确定数据插入到哪个分区为止。

实例:向数据表tab_01中插入group_id为10,group_code为15的数据。这条数据会被插入到part1分区中。

八、LIST COLUMNS分区
LIST COLUMNS分区不仅仅具有LIST分区的特性,同样也可以支持对多个列进行分区。例如,创建一个根据group_id和group_code字段进行LIST COLUMNS分区的tab_01数据表。

create table tab_01(
->id int not null,
->t_name varchar(20) not null default '',
->group_id int not null,
->group_code int not null,
->)PARTITION  BY LIST COLUMNS(group_id,group_code)(
->PARTITION  part0 VALUES LESS THAN ((1,1),(1,2),(1,3))
->PARTITION  part1 VALUES LESS THAN ((1,4),(2,1))
->PARTITION  part2 VALUES LESS THAN  ((3,3),(3,5))
->);



实例:向数据表tab_01中添加两条数据,一个数据的group_id为1,group_code为3,另外一条数据的group_id为1,group_code为4。第一条数据会被插入到part1中,另外一条插入到part2中。

九、HASH分区
HASH能够分散数据库中的热点数据,能够在一定的程度上保证分区中的数据尽可能平均分布。HASH分区可以分为常规HASH分区和线性HASH分区。

1、创建分区表
可以使用PARTITION BY HASH 语句创建HASH分区表,例如,创建一个以group_id字段进行HASH分区的tab_01表。

create table tab_01(
->id int not null,
->t_name varchar(40) not null default '',
->group_id int not null
->)PARTITION  BY HASH(group_id)
->PARTITIONS 4;



SQL语句执行成功,对group_id 字段进行HASH ,将tab_01数据表分为4个分区。
常规HASH分区,在插入数据时会对分区列的值进行求模运算,从而得出数据被插入哪个分区中。基本的算法如下:

P=value%num



或者

P=MOD(value,num)



其中,P为数据所在的分区;value为插入数据时,分区列插入的数据值;num为分区个数。

例如:group_id 为3的数据会被分配到p3分区中。

注意:在使用HASH分区时,当数据表中的数据发生变更时,每次都需要使用HASH算法计算一次,所以不推荐使用复杂的HASH算法,也不推荐对数据表中的多个字段进行HASH分区,否则会引起性能问题。

2、创建线性HASH分区
常见线性HASH分区比创建常规分区多一个关键字LINEAR,例如,创建tab_01数据表,并按照group_code字段进行线性HASH分区

create table tab_01(
->id int not null,
->t_name varchar(40) not null default '',
->group_id int not null
->)PARTITION  BY LINEAR HASH(group_id)
->PARTITIONS 4;



3、添加分区
MySQL使用alter table add partition 语句为数据表增加HASH分区。例如,为tab_01数据表添加HASH分区。

alter table tab_01 
->add PARTITION  PARTITIONS 11;



查看数据表的表结构信息:

show create table tab_01 \G;



注意:为数据表增加HASH分区的时候,实际上是对原来的数据表新增N个分区,而不是增加分区后,分区的总个数为N。

3、合并分区
MySQL中不支持使用alter table drop partition 语句删除HASH分区,但是可以通过alter table coalesce partition 语句对hash分区进行合并。

例如:将数据表tab_01中的15个分区合并成分区数量为6个分区

alter table tab_01 
->coalesce partition 9;



注意:使用alter table drop partition语句合并hash分区时,partition关键词后面的数字是要减少的分区数。另外,不能使用alter table coalesce partition 语句来增加hash分区的个数,否则MySQL会报错。

十、key分区
MySQL中的key分区在某种程度上与hash分区类似,只不过hash分区可以使用用户自定义的函数和表达式,而key分区不能。另外,hash分区只能对整数类型的列进行分区,而key分区能够支持对除了blob和text数据类型以外的其他数据类型的列进行分区。

1、创建分区
创建数据表的时候可以使用partition by key 语句指定的key分区。与hash分区不同的是,当数据表中存在主键的时候,可以不指定分区键,MySQL默认使用主键作为key分区的分区键。

create table tab_01(
->id int not null primary key,
->t_name varchar(40),
->group_id int not null
->)PARTITION BY PARTITIONS 8;



如果数据表中没有主键,则MySQL会自动选择非空并且唯一的列进行key分区。

create table tab_01(
->id int not null,
->t_name varchar(30)
->group_id int not null,
->UNIQUE KEY (group_id)
->)PARTITION BY KEY() PARTITIONS 6;



注意:如果唯一键没有指定为非空,则MySQL会报错

在既没有指定主键,又没有指定非空唯一键的时候,则必须为key分区指定分区键。

create table tab_01(
->id int not null,
->t_name varchar(30)
->group_id int not null
->)PARTITION BY KEY(group_id) PARTITIONS 6;



2、添加与合并分区的操作和HASH分区相同
十一、子分区表示可以对数据表中的RANGE分区和LIST分区再次进行子分区,形成复合分区。其中,子分区可以使用HASH分区,也可以使用KEY分区。
例如,创建tab_01数据表的sql语句如下:

create table tab_01(
->id int not null,
->t_name varchar(30),
->group_id int 
->)PARTITION BY RANGE (group_id)
->SUBPARTITION BY HASH(group_id)
->SUBPARTITIONS 4
->(
->PARTITION part0 VALUES LESS THAN(10),
->PARTITION part1 VALUES LESS THAN(MAXVALUE)
->);



数据表的tab_01中存在两个RANGE分区,分别为part0和part1分区。每个RANGE分区又被进一步分成4个HASH子分区。所以。tab_01数据表中总共存在8个分区。

在向tab_01数据表中插入数据时,group_id列小于10的数据将会被插入part0分区的HASH子分区中,group_id大于或者等于10的数据将会被插入到part1分区的HASH子分区中。

十二、分区中的NULL值处理
MySQL中支持在分区中使用NULL值,每种分区中对NULL值的处理方式不尽相同。

1、RANGE分区中的NULL值
在RANGE分区中,NULL值会被当做最小值进行处理,例如,创建数据表tab_01,按照group_id进行RANGE分区。

create table tab_01(
->id int not null,
->name varchar(500)
->gruop_id int not null
->)
->PARTITION BY RANGE(gruop_id)(
->PARTITION part0 VALUES LESS THAN (5),
->PARTITION part1 VALUES LESS THAN (10),
->PARTITION part2 VALUES LESS THAN (15),
->);



向数据表中插入数据

insert into tab_01
->(id name group_id)
->values
->(1,'beijing','NULL');



查看数据分布我们发现数据会被插入到part0中,因为null值在RANGE分区中会被当做最小值来处理。

2、LIST分区中的值为NULL的时候,LIST分区的值列表中必须包含NULL值才能被成功写入,否则MySQL会报错。

create table tab_01(
->id int not null,
->t_name varchar(30),
->group_id int not null
->)PARTITION  BY LIST(group_id)(
->PARTITION part0 VALUES LESS in(1,2),
->PARTITION part1 VALUES LESS in(3,4),
->);



以上的结构无法向数据表插入group_id为null的数据,如果想插入null值,结构为

create table tab_01(
->id int not null,
->t_name varchar(30),
->group_id int not null
->)PARTITION  BY LIST(group_id)(
->PARTITION part0 VALUES LESS in(1,2),
->PARTITION part1 VALUES LESS in(3,4),
->PARTITION part2 VALUES LESS in(null),
->);



3、HASH分区和KEY分区中的NULL值
HASH分区和KEY分区中处理NULL值的方式相同,就是将NULL值当做0进行处理。例如,创建tab_01数据表,按照id字段进行HASH分区。

create table tab_01(
->id int ,
->t_name varchar(30),
->group_code int
->)PARTITION BY HASH(id)
->PARTITIONS 4;



向数据库存入数据

insert into tab_01
->(id,t_name,group_code)
->values
->(NULL,'mysql',1001);



查看数据分布

select 
->partition_name part,
->partition_expression expr,
->partition_description part_desc,
->table_rows
->from
->information_schema.partitions
->where
->table_schema=schema()
->AND table_name='tab_01';