写在前面:笔者以前也有一些MySQL方面的笔记,其中部份内容来自极客时间中丁奇老师的课程。后经园友提醒,这个作法确实不太好。以后我仍会继续更新一下MySQL方面的学习记录,在本身理解以后用本身的方式记录下来。学习与记录,也是我写博客的初衷。sql
概述:数据库
分区功能并非在存储引擎层完成的,所以不少存储引擎包括InnoDB, MyISAM, NDB等都支持分区功能。但也并非全部的存储引擎都支持分区。在使用分区前,首先要了解一下存储引擎对分区的支持状况。若是不做特殊说明,默认是在InnoDB下进行说明。 less
所谓分区,指的是将一个表或索引分解为更小的部分。从物理层面来讲,多是分红了N个物理分区,每一个分区都是独立的。从逻辑上来讲,这N个物理分区还是一个表或一个索引。函数
分区能够分为两大类:性能
MySQL在5.1版本中加入了对水平分区的支持,其最新版本是否支持垂直分区笔者暂未考证。网上有一些关于MySQL垂直分区的内容,大都也是在业务层面将表进行拆分,“手动的”垂直分区。学习
能够经过命令 SHOW PLUGINS; 来查看是否开启了分区功能。(partition的status值为ACTIVE)。优化
MySQL支持下面几种类型的分区:3d
不论建立哪一种类型的分区,若是表中存在主键或惟一索引,分区列必须是惟一索引的一个组成部分。(这里笔者初次接触时还闹了个笑话。上面提到了MySQL支持的是水平分区,也就是说把不一样的行记录分配到不一样的物理文件中,那为啥还有个分区列?分区列还必须是惟一索引的一个组成部分?实际上是这样的,在分区表中若是要插入一条记录,确定要先肯定应该插入到哪一个分区里去。而肯定它属于哪一个分区就是依靠了分区列的值,根据这个值再按照不一样分区自身的规则,就能够肯定这条记录应该被分配到哪一个分区了)。惟一索引能够是容许NULL值的,而且分区列只要是惟一索引的一个组成部分,不须要整个惟一索引分区列都是分区列。如惟一索引是 UNIQUE KEY(a,b),分区列能够只指定a列,PARTITION BY HASH(a);blog
若是建立表时没有指定主键,惟一索引,那么能够指定任何一个列为分区列。索引
分区类型:
RANGE分区:
这是最经常使用的一种分区,咱们来看一个简单的例子:
CREATE TABLE range_t( id INT )ENGINE = INNODB PARTITION BY RANGE (id)( PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN (20) );
由建表语句很容易看出咱们把range_t分红了p0,p1两部分。因为Range分区是给连续区间分区,所以p1的区间范围其实[10,20)。此处须要注意的是,若是仅仅按照上面咱们的分区来的话,是不能向表range_t中插入id大于20的记录的。
这种状况下咱们能够添加一个MAXVALUE值的分区,MAXVALUE能够理解为正无穷,所以区间能够改变为[20,MAXVALUE).
ALTER TABLE range_t ADD PARTITION( partition p2 values less than maxvalue);
range分区的一个典型的应用场景是记录与日期相关的记录。例如要记录某种交易记录,能够按年份时间进行分区。如
... PARTITION BY RANGE (YEAR(date))(
PARTITION p2017 VALUES LESS THAN (2018), PARTITION p2018 VALUES LESS THAN (2019), PARTITION p2019 VALUES LESS THAN (2020) );
根据date所属的年份进行分区,year函数取得的值若是小于2019就归档到p2018分区。这样作有这么一些好处。首先是方便管理,若是咱们要删除18年的数据,能够直接对分区p2018进行删除。 alter table t drop partition p2018; 另外一方面,这样分区也能够加快某些查询的速度。一样以p2018分区进行举例,若是你肯定要查询的范围只在2018这个区间内,能够直接对这个分区进行查询:select * from t partition(p2018);
有一点须要注意,优化器能够对range分区中的部分函数(如YEAR(),TO_DAYS()...)进行优化选择,而对形如YEAR(date)*100 + MONTH(date)这样的分区条件是无能为力的。
LIST分区:
list分区和range分区很类似,区别在于list分区的值是离散的。例如建表语句:
CREATE TABLE list_t( a INT, b INT )ENGINE = INNODB PARTITION BY LIST(b)( PARTITION p0 values IN(1,3,5,7), PARTITION p1 values IN(2,4,6,8) );
和range分区同样,若是你插入的记录的分区列的值不在list分区的范围内,MySQL数据库会抛出异常。另外,若是一次插入多个行的记录,而这些记录当中存在分区未定义的值时,MyISAM和InnoDB存储引擎的处理方式不一样。MyISAM会将以前的行数据库都插入,但以后的不会插入。而InnoDB会将其视为一个事务,所以没有任何事务插入。
MyISAM:
Innodb:
HASH分区:
Hash分区的目的是将数据均匀地分部到预先定义的各个分区中,尽可能保证各分区的数据量相等。在RANGE和LIST分区中,必须明确指定一个给定的列值活列值所在的集合范围,而HASH分区中,MySQL自动完成这些工做,用户所要作的只是基于将要进行哈希分区的列指定一个列值或表达式,以及指定被分区的表将要被分隔成几部分。一个Hash分区的建表语句例子以下:
CREATE TABLE hash_t( a INT, b INT )ENGINE = InnoDB PARTITION BY HASH(a+b) PARTITIONS 4;
如上所述,用户所要作的只是基于将要进行哈希分区的列指定一个列值或表达式。这里咱们使用a+b的值来做为进行hash的值,固然你也能够直接使用字段a或b,或是别的表达式。另外,后面的PARTITIONS 4;表明了要分隔成几个区,这里要求是一个非负整数,默认值是1.
KEY分区:
Key分区和Hash分区很相似,区别在于hash分区使用用户定义的函数进行分区,key分区使用MySQL数据库提供的函数进行分区。对于NDB Cluster引擎,MySQL使用MD5函数来进行分区,对于其余引擎,MySQL数据库使用其内部哈希函数,这些函数基于与Password()同样的运算规则。
COLUMNS分区:
前面介绍的这几种分区有一个共同条件,即数据必须是整型(interger),若是不是整形则须要经过函数将其转化为整型,如YEAR()等。从5.5版本开始,MySQL支持COLUMNS分区,能够理解成是Range分区和list分区的一种优化,它容许直接使用非整形的数据进行分区,分区根据类型直接比较而得,不须要额外的转型处理。此外,Range COLUMNS容许对多个列的值进行分区。COLUMNS分区所支持的类型:
Range Columns对多个列的值进行分区的例子以下:
CREATE TABLE range_column_t( a INT, b INT, c char(3) )engine = InnoDB PARTITION BY RANGE COLUMNS(a,b,c)( PARTITION p0 VALUES LESS THAN (5,10,'c'), PARTITION p1 VALUES LESS THAN (10,20,'m'), PARTITION p2 VALUES LESS THAN (30,50,'z') );
到这里,你应该和我同样有一个疑问,若是我三个值分别属于不一样的区间则会被插入到哪一个分区呢。好比插入这么一条记录:insert into range_column_t values(4,9,'n'); a,b字段的值都在p0分区范围内,c的值p2分区范围内,实际上也插入成功了。咱们来看看结果吧:
若是咱们再插入一条记录:insert into range_column_t values(25,15,'a');查看结果以下:
看来这种方式下,是按照分区列的顺序进行分区的,知足第一个条件后就会直接被分配到对应分区。
子分区:
子分区是在分区的基础上再进行分区,有时也称这种分区为复合分区。MySQL数据库容许在Range和List的分区上再进行Hash分区或Key的子分区。一个创建子分区的例子:
CREATE TABLE sub_t( 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 );
关于子分区,有几个地方须要注意一下:
NULL值:
MySQL数据库容许对NULL值作分区,但处理方式可能不一样于其余数据库。在MySQL的分区中,Null值被认为老是小于任何一个非NULL值。而且对于不一样的分区类型,处理方式也稍有不一样。
分区和性能:
其实有些相似于索引,并非说无脑地添加索引,或是使用分区,数据库的查询就会更快。咱们真正要作的是根据实际业务需求去具体的看待问题,如前面提到的按时间记录的交易记录的表,假设有这样的一张大表,而且能够明确的按照时间分区,且须要频繁访问。那么确实是可使用分区来提升效率,每次查询时尽可能只访问对应的分区便可。
但实际状况中也可能存在这么一种类型的表,它的数据量也很大,访问也很频繁。但每次可能只是会经过索引去访问几条记录,而不须要一次返回不少不少记录。这种状况下,分区可能会带来很差的影响。咱们知道,正常状况下B+树索引(MySQL索引采用B+树结构)只须要2~3次IO操做便可找到对应的记录。若是盲目地使用分区,反而可能会增长IO操做的次数。