分区表概念
将一个表或者索引分解为多个更小、更可管理的部分
目前只支持水平分区
只支持局部分区索引
每个分区保存自己的数据与索引
分区列必须是唯一索引的一个组成部分
CREATE TABLE T1(
col1 INT NOT NULL,
col2 DATE NOT NULL,
col3 INT NOT NULL,
col4 INT NOT NULL,
UNIQUE KEY(col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
分区类型支持
RANGE
LIST
HASH
KEY
COLUMNS
RANGE分区
use test; -- 5.7以上版本应该没有这个库,可以自行创建
CREATE TABLE t_range (
id INT PRIMARY KEY
) ENGINE=INNODB
PARTITION BY RANGE(id) ( -- 根据id划分
PARTITION p0 VALUES LESS THAN (10), -- 小于10的在p0分区
PARTITION P1 VALUES LESS THAN(20)); --大于10的在p1分区
RANGE后面必须是int类型的,如果是日期类型的需要转成int类型的,比如RANGE(year(from_date)),这样就是按年进行分区
LESS THAN (MAXVALUE)表示剩下的所有数据都插入到这个区间内
查看表结构
show create table t\G;
看到下图这样的有PARTITION选项的就表示是一张分区表
同时在数据库目录下面看到了有2个ibd文件,因为这里设置了2个分区
然后运行一些插入语句
insert into t values(1);
insert into t values(2);
insert into t values(16);
select * from t;
虽然这是一张有2个分区的分区表,但是查询的时候看到的内容还是一样的
有个小细节,如果这个时候插入一个10,是会被分配到p1分区的,可以这样查看
insert into t values(10);
explain select * from t where id = 10;
此时如果插入大于20的数据是会报错的,无法插入
insert into t values(30);
特殊数据类型NULL值
数据库不是一个二元的比较结果集(只会返回1或者0),数据库还可以返回NULL这个不确定性的值
select NULL = NULL; -- 依旧返回NULL值
select NULL is NULL; -- 查询NULL值需要is NULL来查询
空值会导致分区有一些小小的考虑的问题,如果from_date是NULl值,会被分配到p0或者最小的分区内,因为NULL值会被作为负无穷的值(最小值)。但是推荐创建表的时候分区键为NOT NULL,创建表的时候所有字段尽量要求也是非空的。NULL值不是一件好事情。
在mysql中NULL值和空字符串不是一个意思
LIST分区
CREATE TABLE t_list(
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));
LIST分区很简单,13579的数据会被插入到p0分区,02468的数据会被插入到p1分区,而且有a,b两个字段的时候,插入操作的时候必须对b列的数据进行插入操作。LIST简单点说也就是指定的值插入到对应的分区,而RANGE是一个范围的值插入到对应的分区。
HASH分区
CREATE TABLE t_hash(
a INT,
b DATETIME
)ENGINE=INNODB
PARTITION BY HASH(YEAR(b))
PARTITIONS 4; -- 4个分区
hash分区和range分区差不多,先做一次hash,然后平均的放到这4个分区里
面。hash分区相对range分区好处是数据的分布相对来说会比较平均一些。
KEY分区
CREATE TABLE t_key(
a INT,
b DATETIME
)ENGINE=INNODB
PARTITION BY KEY(b)
PARTITIONS 4; -- 4个分区
相对range和hash分区,key分区对分区键不需要进行整形转换,使用KEY(b)即可。
COLUMNS分区 5.5版本加持
支持所有的
整形类型(INT,SMALLINT,TINYINT,BIGINT),FLOAT和DECIMAL不支持
日期类型(DATE,DATETIME),其余不支持
字符串类型(CHAR,VARCHAR,BINARY,VARBINARY),BLOB和TEXT不支持
CREATE TABLE t_columns_range(
a INT,
b DATETIME
) ENGINE=INNODB
PARTITION BY RANGE|LIST COLUMNS(b)(
PARTITION p0 VALUES LESS THAN('2009-01-01'),
PARTITION p1 VALUES LESS THAN('2010-01-01')
);
COLUMNS函数的作用是不用在写其它的转换函数了
子分区
在分区的基础上再进行分区
也称为符合分区
-
允许在RANGE和LIST的分区上再进行HASH或者是KEY的子分区
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 );
上面一共创建了3个分区表以及每个分区表对应的2个子分区,所以在数据文件目录下你一共会看到3*2=6个相关的分区文件
实际中子分区用得并不多
更可管理
用分区表用得最多的时候不是hash,是range然后使用日期进行分区,因为很多数据都是有日期属性的,这样就实现了“更小、更可管理”
清理数据的时候可能会用到这样的方法
delete from ts where b >= '2016-06-01' and b <= '2016-06-30';
如果表的数据过大的时候,这样的逻辑删除操作是比较耗时的而且会产生大量的日志,做出从同步的时候会有比较大的延时,所以这样清理数据可能并不是一个很好的方法
但是如果做了分区表,可以直接直接drop partition
alert table ts drop partition p0;
实际上这个操作就是直接删除了该分区表对应的数据文件,还可以通过exchange进行数据表的交换,这样数据就显得非常灵活。
但是mysql中分区表某些场景下性能可能会不好
局部分区
mysql的分区是局部分区,所有的索引只在当前分区里面的
CREATE TABLE T1(
col1 INT NOT NULL,
col2 DATE NOT NULL,
col3 INT NOT NULL,
col4 INT NOT NULL,
UNIQUE KEY(col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;
报ERROR
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
当一张表有了唯一索引但是分区字段不是唯一索引,这里col1和col2有了唯一索引,但是想通过col3来进行分区就会报错,因为分区列必须是唯一索引的一个组成部分。
这样的例子就没问题
CREATE TABLE t1(
col1 INT NOT NULL,
col2 INT NOT NULL,
UNIQUE KEY (col1, col2)
) PARTITION BY HASH(col2) PARTITIONS 4;
原理图,mysql只能保证1在每个分区里保持唯一,不能保证在t1p1中也不存在1
性能
通常用分区表最常见的场景是根据日期来进行分区,然后对日期的这个子段进行查询、数据清理、归档。
mysql一张表多大进行分区没有这一说,一千万的表和一亿的表其实只有微小的性能差距,拆成小表的好处是管理起来比较方便,可以分别alter table。但是支持了在线的online DDL的,就不会阻塞应用,一张表多大数据就没有什么区别。当然分区还是管理比较方便的
查询每个分区的记录数量
use information_schema;
select * from partitions where TABLE_NAME='titles'\G;
使用explain来查看数据所在的分区
explain select * from t where id = 10;
一个分区的时候性能相对来说是不错的
explain select * from t where id >= 0;
用到多个分区的时候性能可能会非常糟糕
所以分区对性能有没有帮助,其实是要看你的查询方式,如果是在多个分区中进行查询,那每个分区表都要扫一下和只扫一个分区,性能会差非常多
explain查看分区直接显示只有在5.7里支持
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。