MySQL分区表

1 分区表简介

 

    数据库分区是一种物理数据库设计技术。虽然分区技术可以实现很多效果,但其主要目的是为了在特定的SQL操作中减少数据读写的总量以缩减sql语句的响应时间,同时对于应用来说分区完全是透明的。

 

    分区表是一种粗粒度,简易的索引策略,适用于大数据的过滤场景.最适合的场景是,没有合适的索引时,对其中几个分区表进行全表扫描.或者只有一个分区表和 索引是热点,而且这个分区和索引能够全部存储在内存中.限制单表分区数不要超过150个,并且注意某些导致无法做分区过滤的细节,分区表对于单条记录的查 询没有优势,需要注意这类查询的性能。

 

2 分区表优缺点

 

    2.1 分区表优点

 

    可以把一些归类的数据放在一个分区中,可以减少服务器检查数据的数量加快查询。
    方便维护,通过删除分区来删除老的数据。
    分区数据可以被分布到不同的物理位置,可以做分布式有效利用多个硬盘驱动器。

 

    可以优化、检查、修复个别分区。

 

    根据查找条件,也就是where后面的条件,查找只查找相应的分区不用全部查找了。
    进行大数据搜索时可以进行并行处理。
    跨多个磁盘来分散数据查询,来获得更大的查询吞吐量。

 

    2.2 分区表缺点

 

    每张表最大分区数为1024。
    所有的主键或者唯一索引必须被包含在分区表达式中。
    不能使用任何外键约束。

 

3 分区类型

 

    MYSQL的分区主要有两种形式:水平分区和垂直分区。

 

    3.1 水平分区(根据列属性按行分)

 

    这种形式的分区是对根据表的行进行分区,通过这样的方式不同分组里面的物理列分割的数据集得以组合,从而进行个体分割(单分区)或集体分割(1个或多个分区)。
所有在表中定义的列在每个数据集中都能找到,所以表的特性依然得以保持。水平分区一定要通过某个属性列来分割。常见的比如年份,日期等。

 

    例如:一个包含十年发票记录的表可以被分区为十个不同的分区,每个分区包含的是其中一年的记录。

 

        3.1.1 水平分区几种模式:

 

        Range(范围) – 这种模式允许DBA将数据划分不同范围。例如DBA可以将一个表通过年份划分成三个分区,80年代(1980's)的数据,90年代(1990's)的数据以及任何在2000年(包括2000年)后的数据。

 

        List(预定义列表) – 这种模式允许系统通过DBA定义的列表的值所对应的行数据进行分割。例如:DBA建立了一个横跨三个分区的表,分别根据2004年2005年和2006年值所对应的数据。   
        Hash(哈希) – 这中模式允许DBA通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区,。例如DBA可以建立一个对表主键进行分区的表。  

 

        Key(键值) – Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。

 

        Composite(复合模式) - 其实是以上模式的组合使用而已。例如:在初始化已经进行了Range范围分区的表上,我们可以对其中一个分区再进行hash或KEY(Linear Hash)分区。

 

    3.2 垂直分区(按列分)

 

    这种分区方式一般来说是通过对表的垂直划分来减少目标表的宽度,使某些特定的列被划分到特定的分区,每个分区都包含了其中的列所对应所有行。

 

    例如:一个包含了大text和BLOB列的表,这些text和BLOB列又不经常被访问,这时候就要把这些不经常使用的text和BLOB了划分到另一个分区,在保证它们数据相关性的同时还能提高访问速度。

 

4 实例分析

 

    4.1 查看分区表

 

    查看是否支持分区

 

  1. mysql> show variables like '%partition%';  
  2. +-------------------+-------+  
  3. | Variable_name     | Value |  
  4. +-------------------+-------+  
  5. | have_partitioning | YES   |  
  6. +-------------------+-------+  
  7. mysql> show plugins;  
  8. +--------------------------+----------+--------------------+---------+-------------+  
  9. Name                     | Status   | Type               | Library | License     |  
  10. +--------------------------+----------+--------------------+---------+-------------+  
  11. | binlog                   | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  12. | mysql_native_password    | ACTIVE   | AUTHENTICATION     | NULL    | PROPRIETARY |  
  13. | mysql_old_password       | ACTIVE   | AUTHENTICATION     | NULL    | PROPRIETARY |  
  14. | MEMORY                   | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  15. | MyISAM                   | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  16. | CSV                      | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  17. | MRG_MYISAM               | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  18. | InnoDB                   | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  19. | INNODB_BUFFER_POOL_STATS | ACTIVE   | INFORMATION SCHEMA | NULL    | PROPRIETARY |  
  20. | PERFORMANCE_SCHEMA       | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  21. | FEDERATED                | DISABLED | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  22. | BLACKHOLE                | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  23. | ARCHIVE                  | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  24. | partition                | ACTIVE   | STORAGE ENGINE     | NULL    | PROPRIETARY |  
  25. +--------------------------+----------+--------------------+---------+-------------+  

    4.2 Range分区

    创建range分区表

 

  1. CREATE TABLE IF NOT EXISTS `user` (    
  2.     `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',    
  3.     `namevarchar(50) NOT NULL DEFAULT '' COMMENT '名称',    
  4.     `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女',    
  5.     PRIMARY KEY (`id`)    
  6. ) ENGINE=Innodb  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1    
  7. PARTITION BY RANGE (id) (    
  8.     PARTITION p0 VALUES LESS THAN (3)   
  9.         DATA DIRECTORY = '/data/data0'  
  10.         INDEX DIRECTORY = '/data/index0',    
  11.     PARTITION p1 VALUES LESS THAN (6)    
  12.         DATA DIRECTORY = '/data/data1'  
  13.         INDEX DIRECTORY = '/data/index1',  
  14.     PARTITION p2 VALUES LESS THAN (9)  
  15.         DATA DIRECTORY = '/data/data2'  
  16.         INDEX DIRECTORY = '/data/index2',   
  17.     PARTITION p3 VALUES LESS THAN (12)    
  18.         DATA DIRECTORY = '/data/data3'  
  19.         INDEX DIRECTORY = '/data/index3',  
  20.     PARTITION p4 VALUES LESS THAN MAXVALUE    
  21.         DATA DIRECTORY = '/data/data4'  
  22.         INDEX DIRECTORY = '/data/index4'  
  23.  );   

    在这里,将用户表分成5个分区,以每3条记录为界限(示例),每个分区都有自己独立的数据、索引文件的存放目录,与此同时,这些目录所在的物理磁盘分区可能也都是完全独立的,可以多大提高了磁盘IO吞吐量。

    插入一些数据

 

  1. INSERT INTO `user` (`name` ,`sex`)VALUES   
  2. ('name1''0'),('name2',1),('name3',1),('name4',1),('name5',0),('name6',1),('name7',1),('name8',1),  
  3. ('name9',1),('name10',1),('name11',1),('name12',1),('name13',1),('name14',1),('name15',1),('name16',1);  

 

    查询数据

 

  1. mysql> select count(id)  from user;  
  2. +-----------+  
  3. count(id) |  
  4. +-----------+  
  5. |        16 |  
  6. +-----------+  

    删除分区

  1. mysql> alter table user drop partition p3;  
  2. mysql> select count(id)  from user;  
  3. +-----------+  
  4. count(id) |  
  5. +-----------+  
  6. |        13 |  
  7. +-----------+  

    注意:可以对现有表进行分区,并且会按規则自动的将表中的数据分配相应的分区中,这样就比较好了,可以省去很多事情。

    例如:

 

  1. mysql> alter table aa partition by RANGE(id) (  
  2. PARTITION p1 VALUES less than (1),    
  3. PARTITION p2 VALUES less than (5),    
  4. PARTITION p3 VALUES less than MAXVALUE);   

 

    查看分区表信息

 

  1. SHOW CREATE TABLE user;  
  2. SHOW TABLE STATUS LIKE 'user';  
  3. mysql> select * from information_schema.`PARTITIONS` where TABLE_NAME = 'user';  

 

    查看各分区信息

 

  1. mysql> SELECT TABLE_NAME,PARTITION_NAME,PARTITION_EXPRESSION,PARTITION_DESCRIPTION,TABLE_ROWS,AVG_ROW_LENGTH/1024 as 'AVG_ROW_LENGTH (KB)',DATA_LENGTH/(1024*1024) as 'DATA_LENGTH (MB)',MAX_DATA_LENGTH/(1024*1024*1024*1024) as 'MAX_DATA_LENGTH (TB)',INDEX_LENGTH/(1024*1024) as 'INDEX_LENGTH (MB)',UPDATE_TIME,CHECK_TIME FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME='user' AND TABLE_SCHEMA='test1';  

 

    4.3 LIST分区

 

     LIST分区中每个分区的定义和选择是基于某列的值从属于一个值列表集中的一个值,而RANGE分 区是从属于一个连续区间值的集合。

 

    创建LIST分区表

 

  1. CREATE TABLE IF NOT EXISTS `list_part` (    
  2.     `id` int(11) NOT NULL COMMENT '用户ID',    
  3.     `namevarchar(50) NOT NULL DEFAULT '' COMMENT '名称',    
  4.     `sex` int(1) NOT NULL DEFAULT '0' COMMENT '0为男,1为女'   
  5. ) ENGINE=Innodb  DEFAULT CHARSET=utf8    
  6. PARTITION BY LIST (id) (    
  7.     PARTITION p0 VALUES in (1, 3, 5),    
  8.     PARTITION p1 VALUES in (2, 6, 4),  
  9.     PARTITION p2 VALUES in (7, 8, 9),   
  10.     PARTITION p3 VALUES in (13, 14, 15),  
  11.     PARTITION p4 VALUES in (10, 11, 12)   
  12.  );   

 

    插入一些数据

 

  1. INSERT INTO `list_part` (`id`,`name` ,`sex`)VALUES   
  2. (1,'name1''0'),(2,'name2',1),(3,'name3',1),(4,'name4',1),(5,'name5',0),(6,'name6',1),(7,'name7',1),(8,'name8',1),  
  3. (9,'name9',1),(10,'name10',1),(11,'name11',1),(12,'name12',1),(12,'name13',1),(14,'name14',1),(15,'name15',1);  

 

    注意:1.值列表中的数据不能重复,否则报错 ERROR 1495 (HY000): Multiple definition of same constant in list partitioning

 

                2.插入的数据必须包含在列值表中,比如插入id中包含16,则报错 ERROR 1526 (HY000): Table has no partition for value 16

 

                3.如果表包含主键,分区时主键一定要包含其中,否则报错 ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table's partitioning function,一般情况下,一个张表肯定会有一个主键,这算是一个分区的局限性吧。

 

    4.4 HASH分区

 

    HASH分区主要用来确保数据在预先确定数目的分区中平均分布,你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。

 

    创建HASH分区表

 

  1. CREATE TABLE IF NOT EXISTS `hash_part` (    
  2.     `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论ID',    
  3.     `comment` varchar(1000) NOT NULL DEFAULT '' COMMENT '评论',    
  4.     `ip` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',    
  5.     PRIMARY KEY (`id`)    
  6. ) ENGINE=INNODB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1    
  7.     PARTITION BY HASH(id)    
  8.     PARTITIONS 5;    

 

    4.5 KEY分区(Linear Hash)

 

    按照KEY进行分区类似于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的哈希函数是由MySQL服务器提供。

 

    创建KEY分区表

 

  1. CREATE TABLE IF NOT EXISTS `key_part` (    
  2.     `news_id` int(11) NOT NULL  COMMENT '新闻ID',    
  3.     `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '新闻内容',    
  4.     `u_id` varchar(25) NOT NULL DEFAULT '' COMMENT '来源IP',    
  5.     `create_time` DATE NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '时间'    
  6. ) ENGINE=INNODB  DEFAULT CHARSET=utf8    
  7.     PARTITION BY LINEAR HASH(YEAR(create_time))    
  8.     PARTITIONS 5;    

 

    4.6 子分区(也称复合分区)

 

    子分区是分区表中每个分区的再次分割,子分区既可以使用HASH希分区,也可以使用KEY分区。

 

    创建子分区表

 

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

 

    注:如果一个分区中创建了子分区,其他分区也要有子分区。
        如果创建了了分区,每个分区中的子分区数必有相同。
        同一分区内的子分区,名字不相同,不同分区内的子分区名子可以相同(5.1.50不适用)。

 

5 分区管理

 

    5.1 删除分区

 

    删除range分区

 

  1. mysql> alter table user drop partition p4;   

 

    删除list分区

 

  1. mysql> alter table list_part drop partition p4;  

 

    注:hash及key分区不能删除,否则报错

 

            ERROR 1512 (HY000): DROP PARTITION can only be used on RANGE/LIST partitions

 

    删除所有表的分区

 

  1. mysql> Alter table user remove partitioning;  
  2. mysql> Alter table list_part remove partitioning;  
  3. mysql> Alter table hash_part remove partitioning;  
  4. mysql> Alter table key_part remove partitioning;  

    注:对所有类型分区都合适,删除后变为普通表,不丢失数据。

    5.2 新增分区

 

    新增range分区

 

  1. mysql> alter table user add partition(partition p4 values less than MAXVALUE DATA DIRECTORY = '/data/data4' INDEX DIRECTORY = '/data/index4');  

 

    新增list分区

 

  1. mysql> alter table list_part add partition(partition p4 values IN (10,11,12));  

 

    新增hash分区

 

  1. mysql> alter table hash_part add partition(partition p5);  

 

    新增key分区

 

  1. mysql> alter table key_part add partition(partition p5);  

 

    5.3 合并分区

 

    合并range分区

 

  1. alter table user reorganize partition p0,p1 into(partition p0 values less than (6));  
  2. alter table user reorganize partition p0,p1 into(partition p0 values less than (6) DATA DIRECTORY = '/data/data1' INDEX DIRECTORY = '/data/index1');  

    注:不会丢失数据

    合并list分区

 

  1. alter table list_part reorganize partition p0,p1 into(partition p0 values in (1,2,3,4,5,6));  

    注:hash,key分区不能合并

    5.4 拆分分区

 

    拆分range分区

 

  1. alter table user reorganize partition p0 into(  
  2. partition p0 values less than (3) DATA DIRECTORY = '/data/data0' INDEX DIRECTORY = '/data/index0',  
  3. partition p1 values less than (6) DATA DIRECTORY = '/data/data1' INDEX DIRECTORY = '/data/index1'  
  4. );  

    拆分list分区

  1. alter table list_part reorganize partition p0 into(partition p0 values in (1,3,5), partition p1 values in (2,4,6));  

    注:hash,key分区不能拆分

    5.5 重新定义(修改)分区

 

    重定义range分区

 

  1. ALTER table `user` PARTITION BY RANGE (id) (    
  2.     PARTITION p0 VALUES LESS THAN (3)   
  3.         DATA DIRECTORY = '/data/data0'  
  4.         INDEX DIRECTORY = '/data/index0',    
  5.     PARTITION p1 VALUES LESS THAN (6)    
  6.         DATA DIRECTORY = '/data/data1'  
  7.         INDEX DIRECTORY = '/data/index1',  
  8.     PARTITION p2 VALUES LESS THAN (9)  
  9.         DATA DIRECTORY = '/data/data2'  
  10.         INDEX DIRECTORY = '/data/index2',   
  11.     PARTITION p3 VALUES LESS THAN MAXVALUE    
  12.         DATA DIRECTORY = '/data/data3'  
  13.         INDEX DIRECTORY = '/data/index3'  
  14.  );   

    重定义list分区

  1. ALTER table `list_part` PARTITION BY LIST (id) (    
  2.     PARTITION p0 VALUES in (1, 3, 5),    
  3.     PARTITION p1 VALUES in (2, 6, 4),  
  4.     PARTITION p2 VALUES in (7, 8, 9),   
  5.     PARTITION p3 VALUES in (10, 11, 12, 13, 14, 15)  
  6.  );   

 

    重定义hash分区

 

  1. ALTER TABLE `hash_part`   
  2.     PARTITION BY HASH(id)    
  3.     PARTITIONS 6;   

 

    重定义key分区

 

  1. ALTER TABLE `key_part`  
  2.     PARTITION BY LINEAR HASH(YEAR(create_time))    
  3.     PARTITIONS 6;    

    重定义子分区

  1. ALTER TABLE `sub_part` PARTITION BY RANGE(YEAR(create_time))    
  2.     SUBPARTITION BY HASH(TO_DAYS(create_time))(    
  3.         PARTITION p0 VALUES LESS THAN (1990)(SUBPARTITION s0,SUBPARTITION s1,SUBPARTITION s2),    
  4.         PARTITION p1 VALUES LESS THAN MAXVALUE(SUBPARTITION tank0,SUBPARTITION tank1,SUBPARTITION tank3)    
  5.     );    

 

    5.6 分析分区(analyze)

 

  1. mysql> ALTER TABLE user analyze PARTITION p0,p1;  
  2. +------------+---------+----------+----------+  
  3. Table      | Op      | Msg_type | Msg_text |  
  4. +------------+---------+----------+----------+  
  5. | test1.user | analyze | status   | OK       |  
  6. +------------+---------+----------+----------+  
  7. mysql> ALTER TABLE list_part analyze PARTITION p0;  
  8. +-----------------+---------+----------+----------+  
  9. Table           | Op      | Msg_type | Msg_text |  
  10. +-----------------+---------+----------+----------+  
  11. | test1.list_part | analyze | status   | OK       |  
  12. +-----------------+---------+----------+----------+  
  13. mysql> ALTER TABLE hash_part analyze PARTITION p0;  
  14. +-----------------+---------+----------+----------+  
  15. Table           | Op      | Msg_type | Msg_text |  
  16. +-----------------+---------+----------+----------+  
  17. | test1.hash_part | analyze | status   | OK       |  
  18. +-----------------+---------+----------+----------+  
  19. mysql> ALTER TABLE key_part analyze PARTITION p0;  
  20. +----------------+---------+----------+----------+  
  21. Table          | Op      | Msg_type | Msg_text |  
  22. +----------------+---------+----------+----------+  
  23. | test1.key_part | analyze | status   | OK       |  
  24. +----------------+---------+----------+----------+  

    读取并保存分区的键分布。

    5.7 检查分区(check)

 

  1. mysql> ALTER TABLE user CHECK PARTITION p0,p1;  
  2. +------------+-------+----------+----------+  
  3. Table      | Op    | Msg_type | Msg_text |  
  4. +------------+-------+----------+----------+  
  5. | test1.user | check | status   | OK       |  
  6. +------------+-------+----------+----------+  
  7. mysql> ALTER TABLE list_part CHECK PARTITION p0;  
  8. +-----------------+-------+----------+----------+  
  9. Table           | Op    | Msg_type | Msg_text |  
  10. +-----------------+-------+----------+----------+  
  11. | test1.list_part | check | status   | OK       |  
  12. +-----------------+-------+----------+----------+  
  13. mysql> ALTER TABLE hash_part CHECK PARTITION p0;  
  14. +-----------------+-------+----------+----------+  
  15. Table           | Op    | Msg_type | Msg_text |  
  16. +-----------------+-------+----------+----------+  
  17. | test1.hash_part | check | status   | OK       |  
  18. +-----------------+-------+----------+----------+  
  19. mysql> ALTER TABLE key_part CHECK PARTITION p0;  
  20. +----------------+-------+----------+----------+  
  21. Table          | Op    | Msg_type | Msg_text |  
  22. +----------------+-------+----------+----------+  
  23. | test1.key_part | check | status   | OK       |  
  24. +----------------+-------+----------+----------+  

    这个命令可以告诉你表的分区中的数据或索引是否已经被破坏。如果发生了这种情况,使用“ALTER TABLE ... REPAIR PARTITION”来修补该分区。

    5.8 修补分区(repair)

 

  1. mysql> ALTER TABLE user repair PARTITION p0,p1;  
  2. mysql> ALTER TABLE list_part repair PARTITION p0;  
  3. mysql> ALTER TABLE hash_part repair PARTITION p0;  
  4. mysql> ALTER TABLE key_part repair PARTITION p0;  

    修补被破坏的分区。

    注:innodb存储引擎表不支持修复分区,myisam表支持此选项。

 

    5.9 重建分区(rebuild)

 

    这和先删除保存在分区中的所有记录,然后重新插入它们,具有同样的效果。它可用于整理分区碎片。

 

  1. mysql> ALTER TABLE key_part rebuild partition p0;  
  2. mysql> ALTER TABLE hash_part rebuild partition p0;  
  3. mysql> ALTER TABLE list_part rebuild partition p0;  
  4. mysql> ALTER TABLE user rebuild partition p0;  

 

    5.10 优化分区(optimize)

 

    该参数由(rebuild+analyze取代)

 

    如果从分区中删除了大量的行,或者对一个带有可变长度的行(也就是说,有VARCHAR,BLOB,或TEXT类型的列)作了许多修改,可以使用 “ALTER TABLE ... OPTIMIZE PARTITION”来收回没有使用的空间,并整理分区数据文件的碎片。

 

  1. mysql> ALTER TABLE list_part optimize partition p0;  
  2. mysql> ALTER TABLE lhash_part optimize partition p0;  
  3. mysql> ALTER TABLE hash_part optimize partition p0;  
  4. mysql> ALTER TABLE key_part optimize partition p0;  
  5. +----------------+----------+----------+-------------------------------------------------------------------+  
  6. Table          | Op       | Msg_type | Msg_text                                                          |  
  7. +----------------+----------+----------+-------------------------------------------------------------------+  
  8. | test1.key_part | optimize | note     | Table does not support optimize, doing recreate + analyze instead |  
  9. | test1.key_part | optimize | status   | OK                                                                |  
  10. +----------------+----------+----------+-------------------------------------------------------------------+  

 

6 分区表和未分区表性能对比详细验证

 

    6.1 测试服务器配置

 

    CPU:64核

 

    内存:64GB

 

    6.2 准备数据

 

    6.2.1 创建未分区表及分区表

 

  1. CREATE TABLE no_part_tab (c1 int default NULL,c2 varchar(30) default NULL,c3 date default NULL) engine=innodb;  
  2.   
  3. CREATE TABLE part_tab ( c1 int default NULL, c2 varchar(30) default NULL, c3 date default NULL) engine=innodb  
  4. PARTITION BY RANGE (year(c3)) (  
  5. PARTITION p0 VALUES LESS THAN (1995) , PARTITION p1 VALUES LESS THAN (1996) ,  
  6. PARTITION p2 VALUES LESS THAN (1997) , PARTITION p3 VALUES LESS THAN (1998) ,  
  7. PARTITION p4 VALUES LESS THAN (1999) , PARTITION p5 VALUES LESS THAN (2000) ,  
  8. PARTITION p6 VALUES LESS THAN (2001) , PARTITION p7 VALUES LESS THAN (2002) ,  
  9. PARTITION p8 VALUES LESS THAN (2003) , PARTITION p9 VALUES LESS THAN (2004) ,  
  10. PARTITION p10 VALUES LESS THAN (2010), PARTITION p11 VALUES LESS THAN MAXVALUE);  

   6.2.2  通过存储过程写入800万条测试数据

  1. delimiter //   
  2. CREATE PROCEDURE load_part_tab()  
  3. begin  
  4.     declare v int default 0;  
  5.     while v < 8000000  
  6.     do  
  7.         insert into part_tab  
  8.         values (v,'testing partitions',adddate('1995-01-01',(rand(v)*36520) mod 3652));  
  9.          set v = v + 1;  
  10.     end while;  
  11. end  
  12. //  
  13. delimiter ;  

    6.2.3 调用存储过程

  1. mysql> call load_part_tab();  
  2. Query OK, 1 row affected (8 min 19.19 sec)  

    6.2.4 向非分区表写入数据(相当于loaddata一次性写入)

  1. mysql> insert into no_part_tab select * from part_tab;  
  2. Query OK, 8000000 rows affected (58.74 sec)  
  3. Records: 8000000  Duplicates: 0  Warnings: 0  

    6.3 查询及分析(未加索引)

    6.3.1 部分数据查询

 

  1. mysql> select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';   
  2. +----------+  
  3. count(*) |  
  4. +----------+  
  5. |   795181 |  
  6. +----------+  
  7. 1 row in set (0.68 sec)  
  8.   
  9. mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';   
  10. +----------+  
  11. count(*) |  
  12. +----------+  
  13. |   795181 |  
  14. +----------+  
  15. 1 row in set (3.75 sec)  

 

    6.3.2 查询语句分析

 

  1. mysql>  explain select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';  
  2. +----+-------------+----------+------+---------------+------+---------+------+--------+-------------+  
  3. | id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows   | Extra       |  
  4. +----+-------------+----------+------+---------------+------+---------+------+--------+-------------+  
  5. |  1 | SIMPLE      | part_tab | ALL  | NULL          | NULL | NULL    | NULL | 798628 | Using where |  
  6. +----+-------------+----------+------+---------------+------+---------+------+--------+-------------+  
  7. mysql> explain select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';  
  8. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  
  9. | id | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra       |  
  10. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  
  11. |  1 | SIMPLE      | no_part_tab | ALL  | NULL          | NULL | NULL    | NULL | 8000186 | Using where |  
  12. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  

    explain语句显示了SQL查询要处理的记录数目,分别为7.9万条和80万条,可见分区表可以选择性的进行查找而非全表扫描。
    6.3.3 增加日期范围

  1. mysql> select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  2. +----------+  
  3. count(*) |  
  4. +----------+  
  5. |  7998907 |  
  6. +----------+  
  7. 1 row in set (3.91 sec)  
  8.   
  9. mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  10. +----------+  
  11. count(*) |  
  12. +----------+  
  13. |  7998907 |  
  14. +----------+  
  15. 1 row in set (3.91 sec)  

    6.3.4 查询语句分析

  1. mysql> explain select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  2. +----+-------------+----------+------+---------------+------+---------+------+---------+-------------+  
  3. | id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows    | Extra       |  
  4. +----+-------------+----------+------+---------------+------+---------+------+---------+-------------+  
  5. |  1 | SIMPLE      | part_tab | ALL  | NULL          | NULL | NULL    | NULL | 8002786 | Using where |  
  6. +----+-------------+----------+------+---------------+------+---------+------+---------+-------------+  
  7. 1 row in set (0.00 sec)  
  8.   
  9. mysql> explain select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  10. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  
  11. | id | select_type | table       | type | possible_keys | key  | key_len | ref  | rows    | Extra       |  
  12. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  
  13. |  1 | SIMPLE      | no_part_tab | ALL  | NULL          | NULL | NULL    | NULL | 8000186 | Using where |  
  14. +----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+  

    可见:扫描全部数据的话,分区表和未分区表性能是一样的。

    6.4 查询及分析(加入索引)

 

    6.4.1 创建索引

 

  1. mysql> create index inx_c3 on no_part_tab(c3);  
  2. Query OK, 0 rows affected (29.61 sec)  
  3. Records: 0  Duplicates: 0  Warnings: 0  
  4.   
  5. mysql> create index inx_c3 on part_tab(c3);  
  6. Query OK, 0 rows affected (25.99 sec)  
  7. Records: 0  Duplicates: 0  Warnings: 0  

    6.4.2 部分数据查询

  1. mysql> select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';  
  2. +----------+  
  3. count(*) |  
  4. +----------+  
  5. |   795181 |  
  6. +----------+  
  7. 1 row in set (0.75 sec)  
  8.   
  9. mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '1995-12-31';  
  10. +----------+  
  11. count(*) |  
  12. +----------+  
  13. |   795181 |  
  14. +----------+  
  15. 1 row in set (0.78 sec)  

 

    6.4.3 全部数据查询

 

  1. mysql> select count(*) from part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  2. +----------+  
  3. count(*) |  
  4. +----------+  
  5. |  7998907 |  
  6. +----------+  
  7. 1 row in set (4.13 sec)  
  8.   
  9. mysql> select count(*) from no_part_tab where c3 > date '1995-01-01' and c3 < date '2010-12-31';  
  10. +----------+  
  11. count(*) |  
  12. +----------+  
  13. |  7998907 |  
  14. +----------+  
  15. 1 row in set (3.94 sec)  

    可见加入索引的话,分区表和未分区表额查询性能相当。

    6.5 总结
    分区和未分区占用文件空间大致相同 (数据和索引文件)
    如果查询语句中有未建立索引字段,在小范围内查询,分区时间远远优于未分区时间,随着使用分区的增加,二者的时间相差逐渐缩小。
    如果查询语句中字段建立了索引,分区和未分区的性能相当。

 

 

**********************************************************************************************
 转载请注明出处:http://blog.csdn.net/jesseyoung/article/details/37813331
**********************************************************************************************

你可能感兴趣的:(mysql 分区表)