分区是指根据一定的规则,数据库把一个表分解成多个更小的、更容易管理的部分。分区有利于管理非常大的表。
MySQL分区的优点主要包括以下4个方面:
创建分区表:
mysql> create table partition_demo (id int(10),salary decimal(7,2),birthday date)
-> engine=innodb
-> partition by hash(Month(birthday))
-> partitions 4;
Query OK, 0 rows affected (0.09 sec)
注意:MySQL分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对一个表的部分数据进行分区。
Mysql5.5之后分区类型主要有五大类:
注意:无论使用何种分区类型,不能使用主键/唯一键之外的其他字段进行分区,例如:
创建employees 表主键为id,则通过store_id进行分区时会报错:
mysql> CREATE TABLE employees (
-> id INT NOT NULL,
-> fname VARCHAR(30),
-> lname VARCHAR(30),
-> hired DATE NOT NULL DEFAULT '1970-01-01',
-> separated DATE NOT NULL DEFAULT '9999-12-31',
-> job_code INT NOT NULL,
-> store_id INT NOT NULL,
-> PRIMARY KEY(id)
-> )
-> PARTITION BY RANGE (store_id) (
-> PARTITION p0 VALUES LESS THAN (6),
-> PARTITION p1 VALUES LESS THAN (11),
-> PARTITION p2 VALUES LESS THAN (16),
-> PARTITION p3 VALUES LESS THAN MAXVALUE
-> );
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
去掉主键约束后,创建成功:
mysql> CREATE TABLE employees (
-> id INT NOT NULL,
-> fname VARCHAR(30),
-> lname VARCHAR(30),
-> hired DATE NOT NULL DEFAULT '1970-01-01',
-> separated DATE NOT NULL DEFAULT '9999-12-31',
-> job_code INT NOT NULL,
-> store_id INT NOT NULL
-> )
-> PARTITION BY RANGE (store_id) (
-> PARTITION p0 VALUES LESS THAN (6),
-> PARTITION p1 VALUES LESS THAN (11),
-> PARTITION p2 VALUES LESS THAN (16),
-> PARTITION p3 VALUES LESS THAN MAXVALUE
-> );
Query OK, 0 rows affected (0.09 sec)
按照range分区的表是利用取值范围分成分区,区间要求连续且不能互相重叠。
range分区的分区键仅支持整数值,当操作其他类型时,需要用函数进行转换,例如将日期转换为整数值分区,可用year()、to_days()、to_seconds()、UNIX_TIMESTAMP()等函数。代码示例:
mysql> CREATE TABLE quarterly_report_status (
-> report_id INT NOT NULL,
-> report_status VARCHAR(20) NOT NULL,
-> report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
-> )
-> PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
-> PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),
-> PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),
-> PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),
-> PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),
-> PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),
-> PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),
-> PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),
-> PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),
-> PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),
-> PARTITION p9 VALUES LESS THAN (MAXVALUE)
-> );
Query OK, 0 rows affected (0.20 sec)
注意:在RANGE分区中,分区键值为null时,会被当做最小值来处理。
RANGE 分区功能非常适用于以下两种情况:
list分区是建立离散的值列表来告诉数据库特定的值属于哪个分区,LIST分区在很多方面类似于RANGE分区,区别在LIST分区是枚举列表的集合,RANGE分区是连续区间值的集合。
创建代码示例:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20),
PARTITION pWest VALUES IN (4,12,13,14,18),
PARTITION pCentral VALUES IN (7,8,15,16)
);
注意:如果试图插入的值不包含在分区值列表中时,insert操作会失败并报错。
columns分区是MySQL5.6 引入的分区类型,columns分区解决了range分区和list分区只支持整数型分区键的问题,columns分区可细分为range columns分区和list columns分区,都支持整数、日期时间、字符串三大数据类型。(不支持text、blob类型)
columns分区另一大两点是支持多列分区。
创建代码示例:
RANGE Columns:
mysql> CREATE TABLE rcx (
-> a INT,
-> b INT,
-> c CHAR(3),
-> d INT
-> )
-> 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,30,'sss'),
-> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
-> );
Query OK, 0 rows affected (0.15 sec)
RANGE Columns多列分区分区键的比较是基于元组的比较,从第一列到最后一列依次比较。如(5,10,‘ggg’)<(10,20,‘bbb’)<(10,20,‘mmm’),所以(10,20,‘bbb’)插入到p1分区。
LIST Columns:
CREATE TABLE customers_1 (
first_name VARCHAR(25),
last_name VARCHAR(25),
street_1 VARCHAR(30),
street_2 VARCHAR(30),
city VARCHAR(15),
renewal DATE
)
PARTITION BY LIST COLUMNS(city) (
PARTITION pRegion_1 VALUES IN('Oskarshamn', 'Högsby', 'Mönsterås'),
PARTITION pRegion_2 VALUES IN('Vimmerby', 'Hultsfred', 'Västervik'),
PARTITION pRegion_3 VALUES IN('Nässjö', 'Eksjö', 'Vetlanda'),
PARTITION pRegion_4 VALUES IN('Uppvidinge', 'Alvesta', 'Växjo')
);
HASH分区主要用于分散热点读,确保数据在预先确定个数的分区中尽可能平均分布。MySQL通过对分区键应用一个散列函数,来确定数据应当放在N个分区中的哪个分区中。
MySQL支持两种HASH分区,常规HASH分区和线性HASH分区(LINER HASH)。常规HASH分区使用的是驱魔算法,线性HASH分区使用的是一个线性的2的幂运算法则。
创建代码示例:
常规HASH分区:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY HASH(store_id)
PARTITIONS 4;
加入插入一条store_id为234的记录,mod(234,4)=2,所以该记录保存在第二个分区。
子句PARTITION BY HASH(expr) PARTITIONS num中,expr可以是用户自定义的函数或表达式,只要它返回一个既非常数也非随机数的整数即可。
常规HASH分区有一个明显的缺点,不利于分区管理。当我们需要增加或合并分区时,大部分数据都需要重新计算重新分区。线性HASH分区解决了此问题。
LINEAR HASH分区:
CREATE TABLE employees (
id INT NOT NULL,
fname VARCHAR(30),
lname VARCHAR(30),
hired DATE NOT NULL DEFAULT '1970-01-01',
separated DATE NOT NULL DEFAULT '9999-12-31',
job_code INT,
store_id INT
)
PARTITION BY LINEAR HASH( YEAR(hired) )
PARTITIONS 4;
线性HASH分区的优点:在分区维护(包括增加、删除、合并、拆分分区)时,MySQL能狗处理更加迅速。缺点:对比常规HASH分区,线性HASH分区各分区之间数据分布不太均衡。
key分区非常类似于HASH分区,区别是key分区不支持自定义表达式,HASH分区只支持整数类型作为分区键,而key分区支持除blob和text类型外的其他类型的列作为分区键。
创建代码示例:
CREATE TABLE tk (
col1 INT NOT NULL,
col2 CHAR(5),
col3 DATE
)
PARTITION BY LINEAR KEY (col1)
PARTITIONS 3;
注意:分区键不写时默认以主键作为分区键,没有主键时选择非空唯一键作为分区键。
没有主键也没有唯一键的情况下,必须用户指定分区键。
作为分区键的列必须为非空的,否则会报错。
子分区(subpartitioning)是对分区表中每个分区再次分割,又成为复合分区。子分区可以使用HASH分区和key分区。
创建代码示例:
CREATE TABLE ts (id INT, purchased DATE)
PARTITION BY RANGE( YEAR(purchased) )
SUBPARTITION BY HASH( TO_DAYS(purchased) )
SUBPARTITIONS 2 (
PARTITION p0 VALUES LESS THAN (1990),
PARTITION p1 VALUES LESS THAN (2000),
PARTITION p2 VALUES LESS THAN MAXVALUE
);
3个分区的每个分区又被分为两个分区,实际该表被分为6个分区。
分区管理包括添加、删除、重定义、合并、拆分分区的命令,这些操作都可以通过alter table 命令来实现。
(1)删除分区命令:
ALTER TABLE tr DROP PARTITION p3;
注意:删除分区删除表结构中的对应分区,同时删除该分区所有数据。
(2)新增分区命令:
RANGE分区:
ALTER TABLE members ADD PARTITION (PARTITION p3 VALUES LESS THAN (2010));
注意:对于range分区,只能添加新分区只能添加到列表最大一端。
LIST分区:
ALTER TABLE tt ADD PARTITION (PARTITION p2 VALUES IN (7, 14, 21));
注意:添加新分区的分区键值列表的任意值都不能是现有分区键列表已存在的。
(3)重定义分区命令(拆分、合并)
以range分区为例,拆分:
ALTER TABLE members REORGANIZE PARTITION n0 INTO (
PARTITION s0 VALUES LESS THAN (1960),
PARTITION s1 VALUES LESS THAN (1970)
);
合并(4个合成2个):
ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (
PARTITION m0 VALUES LESS THAN (1980),
PARTITION m1 VALUES LESS THAN (2000)
);
注意:
(1)增加分区命令
新增6个分区:
ALTER TABLE clients ADD PARTITION PARTITIONS 6;
(2)减少分区数量命令
减少4个分区:
ALTER TABLE clients COALESCE PARTITION 4;
注意:不能用COALESCE PARTITION关键字增加分区。也就是说COALESCE PARTITION关键字后的数不能大于现有分区数。
代码示例来自MySQL 5.6参考手册
https://dev.mysql.com/doc/refman/5.6/en/