Sql优化之Mysql表分区 MySQL的分区

MySQL的分区

一  分区表适用于如下场景html

1:表很是大以致于没法所有放在内存中,或者只在标的最后部分有热点数据,其余均是历史数据node

2:分区表的数据更容易维护。例如想批量删除大量数据可使用清除整个分区的方式。另外还能够对一个独立分区进行优化、检查、修复等操做。mysql

3:分区表的数据能够分布在不一样的物理设备上,从而高效的利用多个硬件设备sql

4:可使用分区表来避免某些特殊的瓶颈。例如InnoDB的单个索引的互斥访问、ext3文件系统的inode锁竞争等。数据库

5:若是须要,还能够备份和恢复独立的分区,这在很是大的数据集的场景下效果很是好。less

二  分区原理以及限制post

mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(能够经过my.cnf中的datadir来查看),一张表主要对应着三个文件,一个是frm存放表结构的,一个是myd存放表数据的,
一个是myi存表索引的。若是一张表的数据量太大的话,那么myd,myi就会变的很大,查找数据就会变的很慢,这个时候咱们能够利用mysql的分区功能,
在物理上将这一张表对应的三个文件,分割成许多个小块,这样呢,咱们查找一条数据时,就不用所有查找了,只要知道这条数据在哪一块,而后在那一块找就好了。
若是表的数据太大,可能一个磁盘放不下,这个时候,咱们能够把数据分配到不一样的磁盘里面去。优化

分区表的限制ui

\

       分区表的原理url

SELECT 查询:当查询一个分区表的时候,分区层先打开并锁住全部底层表,优化器先判断是否能够过滤部分分区,而后再调用对应的存储引擎接口访问各个分区的数据;

INSERT操做:当写入一条数据时,分区层先打开并锁住全部的底层表,而后肯定那个分区接收这条数据,而后再将记录写入对应的底层表;

DELETE操做:当删除一条记录时,分区层现代开并锁住全部的底层表,而后肯定数据对应的分区,最后对相应底层进行删除操做;

UPDATE操做:当更新一条记录时,分区层先打开并锁住全部的底层表,MYSQL先肯定须要更新的记录在哪一个分区,而后取出数据并更新,在判断更新后的数据应该放在哪一个分区,最后对底层表进行写入操做,并对原数据所在的底层表进行删除操做。

固然其中有些操做是支持过滤的。例如当删除一条记录时,MYSQL 须要先找到这条记录,若是where条件刚好和分区表达式匹配,就能够将全部不包含这条记录的分区都过滤掉。一样的操做对于update一样有效。若是是insert操做,其自己就是只命中一个分区 ,其余分区都会被过滤掉。MYSQL先肯定这条记录属于哪一个分区,再将记录写入对应的底层分区表,无须对任何其余分区进行操做。(虽然每一个操做都会“”“先打开并锁住全部的底层表”,可是并非说分区表在处理过程当中是锁住全表的,若是存储引擎可以本身实现行级锁,例如InnoDB,则会在分区层释放对应表锁)。

分区表的类型:MYSQL支持多种分区表

最多见的就是根据范围进行分区,每一个分区存储落在某个范围的记录,分区表达式能够是列,也能够是列的表达式。下面的例子是将每年的销售额存放在不一样的分区里。

三    建立分区操做
RANGE分区:
mysql> CREATE TABLE `operation_log` (
-> `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
-> `cid` mediumint(7) unsigned NOT NULL,
-> `accountid` mediumint(8) NOT NULL DEFAULT '0' ,
-> `flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
-> `addtime` int(11) unsigned NOT NULL,
-> `device` tinyint(1) unsigned NOT NULL DEFAULT '1' ,
-> PRIMARY KEY (`id`,`addtime`),
-> KEY `idx_accountid_addtime` (`accountid`,`addtime`),
-> KEY `idx_accountid_flag` (`accountid`,`flag`),
->) ENGINE=InnoDB AUTO_INCREMENT=50951039 DEFAULT CHARSET=utf8 COMMENT='操做记录'
->/*!50100 PARTITION BY RANGE (addtime)
->(PARTITION `2013-05` VALUES LESS THAN (1370016000) ENGINE = InnoDB,
-> PARTITION `2013-06` VALUES LESS THAN (1372608000) ENGINE = InnoDB,
-> PARTITION `2013-07` VALUES LESS THAN (1375286400) ENGINE = InnoDB,
-> PARTITION `2013-08` VALUES LESS THAN (1377964800) ENGINE = InnoDB,
-> PARTITION `2013-09` VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */;
1 row in set (0.00 sec)
( LESS THAN MAXVALUE考虑到可能的最大值)
 
list分区
//这种方式失败
mysql> CREATE TABLE IF NOT EXISTS `list_part` (
-> `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
-> `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',
-> `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
-> `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女',
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
-> PARTITION BY LIST (province_id) (
-> PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),
-> PARTITION p1 VALUES IN (9,10,11,12,16,21),
-> PARTITION p2 VALUES IN (13,14,15,19),
-> PARTITION p3 VALUES IN (17,18,20,22,23,24)
-> );
ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function
 
//这种方式成功
mysql> CREATE TABLE IF NOT EXISTS `list_part` (
-> `id` int(11) NOT NULL COMMENT '用户ID',
-> `province_id` int(2) NOT NULL DEFAULT 0 COMMENT '省',
-> `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名称',
-> `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY LIST (province_id) (
-> PARTITION p0 VALUES IN (1,2,3,4,5,6,7,8),
-> PARTITION p1 VALUES IN (9,10,11,12,16,21),
-> PARTITION p2 VALUES IN (13,14,15,19),
-> PARTITION p3 VALUES IN (17,18,20,22,23,24)
-> );
Query OK, 0 rows affected (0.33 sec)
上面的这个建立list分区时,若是有主銉的话,分区时主键必须在其中,否则就会报错。若是我不用主键,分区就建立成功了,通常状况下,一个张表确定会有一个主键,这算是一个分区的局限性
 
hash分区
mysql> CREATE TABLE IF NOT EXISTS `hash_part` (
-> `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论ID',
-> `comment` varchar(1000) NOT NULL DEFAULT '' COMMENT '评论',
-> `ip` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',
-> PRIMARY KEY (`id`)
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1
-> PARTITION BY HASH(id)
-> PARTITIONS 3;
Query OK, 0 rows affected (0.06 sec)
 
key分区
mysql> CREATE TABLE IF NOT EXISTS `key_part` (
-> `news_id` int(11) NOT NULL COMMENT '新闻ID',
-> `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新闻内容',
-> `u_id` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',
-> `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '时间'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY LINEAR HASH(YEAR(create_time))
-> PARTITIONS 3;
Query OK, 0 rows affected (0.07 sec)
 
 
增长子分区操做
子分区是分区表中每一个分区的再次分割,子分区既可使用HASH希分区,也可使用KEY分区。这 也被称为复合分区(composite partitioning)

1. 若是一个分区中建立了子分区,其余分区也要有子分区
2. 若是建立了了分区,每一个分区中的子分区数必有相同
3. 同一分区内的子分区,名字不相同,不一样分区内的子分区名子能够相同(5.1.50不适用)

1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> CREATE TABLE IF NOT EXISTS `sub_part` (
-> `news_id` int(11) NOT NULL COMMENT '新闻ID',
-> `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新闻内容',
-> `u_id` int(11) NOT NULL DEFAULT 0s COMMENT '来源IP',
-> `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '时间'
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8
-> PARTITION BY RANGE(YEAR(create_time))
-> SUBPARTITION BY HASH(TO_DAYS(create_time))(
-> PARTITION p0 VALUES LESS THAN (1990)(SUBPARTITION s0,SUBPARTITION s1,SUBPARTITION s2),
-> PARTITION p1 VALUES LESS THAN (2000)(SUBPARTITION s3,SUBPARTITION s4,SUBPARTITION good),
-> PARTITION p2 VALUES LESS THAN MAXVALUE(SUBPARTITION tank0,SUBPARTITION tank1,SUBPARTITION tank3)
-> );
Query OK, 0 rows affected (0.07 sec)
分区管理

增长分区操做(针对设置MAXVALUE)
range添加分区

1
2
3
mysql>alter table operation_log add partition(partition `2013-10` values less than (1383235200)); --->适用于没有设置MAXVALUE的分区添加
ERROR 1481 (HY000):MAXVALUE can only be used in last partition definition
mysql>alter table operation_log REORGANIZE partition `2013-09` into (partition `2013-09` values less than (1380556800),partition `2013-10` values less than (1383235200),partition `2013-11` values less than maxvalue);

 

list添加分区

1
2
3
mysql> alter table list_part add partition(partition p4 values in (25,26,28));
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

 

hash从新分区

1
2
3
mysql> alter table list_part add partition(partition p4 values in (25,26,28));
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

 

key从新分区

1
2
3
mysql> alter table key_part add partition partitions 4;
Query OK, 1 row affected (0.06 sec)//有数据也会被从新分配
Records: 1 Duplicates: 0 Warnings: 0

 

子分区添加新分区,虽然我没有指定子分区,可是系统会给子分区命名的

1
2
3
mysql> alter table sub1_part add partition(partition p3 values less than MAXVALUE);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

 

删除分区操做

alter table user drop partition2013-05;

分区表其余操做

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
重建分区(官方:与先drop全部记录而后reinsert是同样的效果;用于整理表碎片)
alter table operation_log rebuild partition `2014-01`;
重建多个分区
alter table operation_log rebuild partition `2014-01`,`2014-02`;
过程以下:
 
pro
优化分区(若是删除了一个分区的大量记录或者对一个分区的varchar blob text数据类型的字段作了许多更新,此时能够对分区进行优化以回收未使用的空间和整理分区数据文件)
alter table operation_log optimize partition `2014-01`;
 
优化的操做至关于check partition,analyze partition 和repair patition
 
分析分区
alter table operation_log analyze partition `2014-01`;
 
修复分区
alter table operation_log repair partition `2014-01`;
 
检查分区
alter table operation_log check partition `2014-01`;
相关文章
相关标签/搜索