另有一篇简单易懂的好文章帮助学习
Mysql分区表的原理和优缺点以及注意点
一、分区概述
分区是指根据一定的规则,数据库把一个表分解成多个更小的、更容易管理的部分。分区有利于管理非常大的表。
MySQL分区的优点主要包括以下4个方面:
1、和单个磁盘或者文件系统相比,可以存储更多的数据
2、优化查询。where子句包含分区条件时,可以只扫描对应分区,缩小了查询范围。同时在涉及count()和sum()等聚合函数时,可以在多个分区上并行处理。
3、对于已经过期或不需要的数据,可以通过删除分区快速删除。
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之后分区类型主要有五大类:
RANGE分区:基于一个给定连续区间范围,把数据分配到不同分区。
LIST分区:基于一个枚举出值的列表进行分区。
COLUMNS分区:又分为Range Columns分区和List Columns分区。
HASH分区:基于给定的分区个数进行分区。
KEY分区:类似于HASH分区。
注意:无论使用何种分区类型,不能使用主键/唯一键之外的其他字段进行分区,例如:
创建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)
1、range分区
按照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 分区功能非常适用于以下两种情况:
- 当需要删除过期数据时,只需要alter table quarterly_report_status drop partition p0来删除p0分区中的数据。
- 经常运行包含分区键的查询
2、LIST分区
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操作会失败并报错。
3、COLUMNS分区
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')
);
4、HASH分区
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分区各分区之间数据分布不太均衡。
5、Key分区
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;
注意:分区键不写时默认以主键作为分区键,没有主键时选择非空唯一键作为分区键。
没有主键也没有唯一键的情况下,必须用户指定分区键。
作为分区键的列必须为非空的,否则会报错。
6、子分区
子分区(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、RANGE、LIST 分区管理
(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)
);
注意:
只能够重定义相邻的分区,不能跳过某个分区进行重定义。
重定义后的分区区间必须和原分区区间覆盖相同的区间或集合。
不能通过重定义分区来改变分区的类型。
2、HASH、KEY 分区管理
(1)增加分区命令 新增6个分区:ALTER TABLE clients ADD PARTITION PARTITIONS 6;
(2)减少分区数量命令
减少4个分区:
ALTER TABLE clients COALESCE PARTITION 4;
注意:不能用COALESCE PARTITION关键字增加分区。也就是说COALESCE PARTITION关键字后的数不能大于现有分区数。