业务场景:当前有个发料表,随着业务数据量增多,达到了几千万级别水平,查询的效率就越来越低了,针对当前的架构情况,我们进行了分区的设置,通过对时间字段,按年月,一个月作为一个分区,这样查询效率会有一定程度的提升
业务上,会查询表中多个字段,比如条码,批次,时间。其中,作为时间字段来说,可能是种类比较少的,分区的个数不建议过多,那么我们最后就选择了按时间分区
alter table `表名称` PARTITION by LIST COLUMNS (YEAR_MON)
(
PARTITION p1 VALUES IN ('2023-01'),
PARTITION p2 VALUES IN ('2023-02'),
PARTITION p3 VALUES IN ('2023-03'),
……
PARTITION pN VALUES IN ('2023-07')
)
range,list,hash,key这四种分区中,分区的条件是:数据必须是整型,如果不是整型,那应该通过函数将其转化为整型(如:YEAR(),TO_DAYS(),MONTH()等函数)。
create table sales (
money int unsigned not null,
date datetime
) engine=innodb
partition by range (TO_DAYS(date)) (
partition p201001 values less than (TO_DAYS('2010-02-01')),
partition p201002 values less than (TO_DAYS('2010-03-01')),
partition p201003 values less than (TO_DAYS('2010-04-01'))
);
create table t1(
a int,
b int
) engine=innodb
partition by list(b) (
partition p0 values in (1,3,5,7,9),
partition p1 values in (0,2,4,6,8)
);
create table t2(
a int,
b datetime
) engine=innodb
partition by hash (year(b))
partitions 4;
create table t3(
a int,
b datetime
)engine=innodb
partition by linear hash (year(b))
partitions 4;
create table t4(
a int,
b datetime
)engine=innodb
partition by key (b)
partitions 4;
MySQL5.5版本开始支持columns分区,可以看做range分区和list分区的一种进化。
columns分区可以直接使用非整型的数据进行分区,分区根据类型直接比较而得,不需要转化为整型。
columns分区支持以下的数据类型:
1、所有的整数类型,如int,smallint,tinyint,mediumint,bigint。float和decimal则不予支持
2、日期类型,如date和datetime。其余的日期类型不予支持。
3、字符串类型,如char,varchar,binary和varbinary。blob和text类型不予支持。
create table t5(
a int,
b datetime
)engine=innodb
partition by range columns (b) (
partition p0 values less than ('2009-01-01'),
partition p1 values less than ('2010-01-01')
);
range columns 分区可以对多个列的值进行分区。
create table t5_rcx(
a int,
b int,
c char(3),
d int
)engine=innodb
partition by range columns (a,d,c) (
partition p0 values less than (5, 10, 'ggg'),
partition p1 values less than (10, 20, 'mmm'),
partition p2 values less than (15, 20, 'sss'),
partition p3 values less than (maxvalue, maxvalue, maxvalue)
);
list columns 中文也可以
create table t6(
first_name varchar(25),
last_name varchar(25),
street_1 varchar(30),
street_2 varchar(30),
city varchar(15),
renewal date
)engine=innodb
partition by list columns (city) (
partition pRegion_1 values in ('北京','上海','广州'),
partition pRegion_2 values in ('武汉','郑州','成都')
);
子分区是指在分区的基础上再进行分区,也称该分区为复合分区。MySQL允许在range和list的分区上再进行hash和key的子分区。
子分区建立需要注意以下几个问题:
1、每个子分区的数量必须相同
2、要在一个分区表的任何分区上使用subpartition明确定义任何子分区,就必须定义所有的子分区。
3、每个subpartition子句必须包括子分区的一个名字
4、子分区的名字必须是唯一的。
b列进行range分区,又进行了一次hash分区,分区的数量是(3 x 2 = 6)
create table ts(
a int,
b date
)engine=innodb
partition by range (YEAR(b))
subpartition by hash(TO_DAYS(b))
subpartitions 2
(
partition p0 values less than (1990),
partition p1 values less than (2000),
partition p2 values less than maxvalue
);
我们也可以使用subpartition语法显示的指出各个子分区的名字:
create table dspy_sub_name(
a int,
b date
)engine=innodb
partition by range (YEAR(b))
subpartition by hash (TO_DAYS(b))
(
partition p0 values less than (2009) (
subpartition s0,
subpartition s1
),
partition p1 values less than (2010) (
subpartition s2,
subpartition s3
),
partition p2 values less than maxvalue (
subpartition s4,
subpartition s5
)
);
子分区可以用于特别大的表,在多个磁盘间分别分配数据和索引。假设有6个磁盘,分别为/disk0,/disk1,/disk2等,现在考虑下面的例子:
create table ts1(
a int,
b date
)engine=innodb
partition by range (YEAR(b))
subpartition by hash (TO_DAYS(b))
(
partition p0 values less than (2009) (
subpartition s0
DATA DIRECTORY = '/disk0/data'
INDEX DIRECTORY = '/disk0/idx',
subpartition s1
DATA DIRECTORY = '/disk1/data'
INDEX DIRECTORY = '/disk1/idx'
),
partition p1 values less than (2010) (
subpartition s2
DATA DIRECTORY = '/disk2/data'
INDEX DIRECTORY = '/disk2/idx',
subpartition s3
DATA DIRECTORY = '/disk3/data'
INDEX DIRECTORY = '/disk3/idx'
),
partition p2 values less than (2011) (
subpartition s4
DATA DIRECTORY = '/disk4/data'
INDEX DIRECTORY = '/disk4/idx',
subpartition s5
DATA DIRECTORY = '/disk5/data'
INDEX DIRECTORY = '/disk5/idx'
)
);
由于innodb存储引擎使用表空间自动的进行数据和索引的管理,因此会忽略DATA DIRECTORY 和INDEX DIRECTORY语法,因此上述分区表的数据和索引文件的分开放置对其是无效的。
mysql数据库允许对null值做分区。MySQL数据库的分区总是把null值看做是小于任何一个非null值,这和MySQL数据库中处理null值得order by操作是一样的。因此对于不同的分区类型,MySQL数据库对于null值的处理也是不相同的。
1、对于range分区,如果向分区中插入null值,则MySQL数据库会将该值放入最左边的分区,另外注意的是如果删除最左边的分区,则会删除该分区的记录包括null值的记录
2、list分区下要使用null值,则必须显示地指出哪个分区中放入null值,否则会报错。3、hash和key分区对于null的处理方式和range分区、list分区不一样。任何分区的函数都会将含有null值得记录返回为0
create table t(
int a,
int b
)engine=innodb
partition by list (b) (
partition p0 values in (1,3,5,7,9,null),
partition p1 values in (0,2,4,6,8)
);
MySQL5.6开始支持alter table
table_name
exchange partitionpartition_name
with tabletable_name
的语法。该语法允许分区或子分区中的数据与另一个非分区的表中的数据进行交换。如果表中的数据为空,那么相当于将分区中的数据移动到非分区表中。若分区表中的数据为空,则相当于将外部表中的数据导入到分区中。
要使用这个语法要满足以下条件:
1、要交换的表必须和分区表有相同的表结构,但是要交换的表不能含有分区
2、在非分区表中的数据必须在交换的分区定义内
3、被交换的表中不能含有外键或者其他的表含有对该表的外键引用
4、用户除了需要alter,insert,create权限外,还需要有drop权限
另外,有两个小细节需要注意:
1、使用该语句时不会触发交换表和被交换表上的触发器
2、auto_increment列将被重置
create table e(
id int not null,
fname varchar(30),
lname varchar(30)
)engine=innodb
partition by range (id) (
partition p0 values less than (50),
partition p1 values less than (100),
partition p2 values less than (150),
partition p3 values less than maxvalue
);
--- 插入数据
insert into e values
(1669, 'Jim', 'Smith'),
(337, 'Mary', 'Jones'),
(16, 'Frank', 'White'),
(2005, 'Jerry', 'Tom');
--- 创建e2表,复制e表结构,并清除partition
create table e2 like e;
alter table e2 remove partitioning;
--- 交换数据
alter table e exchange partition p0 with table e2;
备注:一个分区只能交换到一个外部物理表中,不能将多个分区交换到同一个表。