表分区学习

1. 概述

1.1. 优点:

l 将表分区比一个表在单个磁盘或者文件系统存储能够存储更多数据

l 可以通过drop分区删除无用数据,也可以通过增加分区添加数据

l 查询可以通过分区裁剪进行优化,设置可以显示指定查找分区

l 对于聚合函数的全表查询,可以并行对分区查询,汇总得到结果

l 可以对分区指定不同存储磁盘实现更高的查询吞吐量

1.2. 分区类型:

l 范围分区

l 列表分区

l 哈希分区

l 键值分区

1.3. 时间范围分区应用

同时支持将date,datetime字段作为范围分区、列表分区的分区键:

CREATE TABLE members (

firstname VARCHAR(25) NOT NULL,

lastname VARCHAR(25) NOT NULL,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATE NOT NULL

)

PARTITION BY RANGE( YEAR(joined) ) (

PARTITION p0 VALUES LESS THAN (1960),

PARTITION p1 VALUES LESS THAN (1970),

PARTITION p2 VALUES LESS THAN (1980),

PARTITION p3 VALUES LESS THAN (1990),

PARTITION p4 VALUES LESS THAN MAXVALUE

);

对于mysql的分区,有一点是非常重要:无论什么类型的分区,分区在创建时都会逐一数字化,当一个新行插入分区表,通过匹配不同的值插入不同分区

分区表的分区名称区分大小写:

mysql> CREATE TABLE t2 (val INT)

-> PARTITION BY LIST(val)(

-> PARTITION mypart VALUES IN (1,3,5),

-> PARTITION MyPart VALUES IN (2,4,6)

-> );

ERROR 1488 (HY000): Duplicate partition name mypart

当指定分区数字时,分区数字只能是正整数,不能是零,也不能以0靠头,不能是类似的表达式,0.8E+01 or 6-2,decimal类型不能带有小数部分。

2. 分区类型详解

2.1. 范围分区:

2.1.1. 定义:分区包含数据在一个给定的范围内,范围应该是连续的且不重叠

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 (21)

PARTITION p3 VALUES LESS THAN MAXVALUE

);

VALUES LESS THAN clause in the CREATE TABLE statement hat provides for all values greater than the highest value explicitly named,将超过最大值的记录都存到p3分区内。可以通过alter table语句增加新分区将p3分区内的数据做进一步分区。

2.1.2. 重点应用基于时间间隔的分区方案

l 在date、time、dateitme字段上利用函数做分区键

CREATE TABLE members (

firstname VARCHAR(25) NOT NULL,

lastname VARCHAR(25) NOT NULL,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL

)

PARTITION BY RANGE( YEAR(joined) ) (

PARTITION p0 VALUES LESS THAN (1960),

PARTITION p1 VALUES LESS THAN (1970),

PARTITION p2 VALUES LESS THAN (1980),

PARTITION p3 VALUES LESS THAN (1990),

PARTITION p4 VALUES LESS THAN MAXVALUE

);

l 基于时间戳的分区方案,其余对时间戳类型值的表达式不允许做分区key

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)

);

l 范围Columns分区

CREATE TABLE members (

firstname VARCHAR(25) NOT NULL,

lastname VARCHAR(25) NOT NULL,

username VARCHAR(16) NOT NULL,

email VARCHAR(35),

joined DATETIME NOT NULL

)

PARTITION BY RANGE COLUMNS(joined) (

PARTITION p1 VALUES LESS THAN ('2015-01-01'),

PARTITION max VALUES LESS THAN MAXVALUE,

);

2.2. 列表分区

类似于范围分区,每个分区必须显示定义,每一个分区的定义和选择基于在列表中定义的值,而不是连续范围的值。分区表达式或者字段,最终返回的也是一个整数

注意:在支持事务的InnoDB中,如果使用insert语句同时插入多条记录,如果有一条失败,那么整个事务回滚,所有插入都失败。如果使用IGNORE关键字插入,那么能够插入的会成功,失败的不会插入,并且不报错;在不支持事务的MyISAM中,在错误数据之前插入的全都可以插入,错误数据之后插入的全都报错

mysql> CREATE TABLE h2 (

-> c1 INT,

-> c2 INT

-> )

-> PARTITION BY LIST(c1) (

-> PARTITION p0 VALUES IN (1, 4, 7),

-> PARTITION p1 VALUES IN (2, 5, 8)

-> );

Query OK, 0 rows affected (0.11 sec)

mysql> INSERT INTO h2 VALUES (3, 5);

ERROR 1525 (HY000): Table has no partition for value 3

mysql> TRUNCATE h2;

Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM h2;

Empty set (0.00 sec)

mysql> INSERT IGNORE INTO h2 VALUES (2, 5), (6, 10), (7, 5), (3, 1), (1, 9);

Query OK, 3 rows affected (0.00 sec)

Records: 5 Duplicates: 2 Warnings: 0

mysql> SELECT * FROM h2;

+------+------+

| c1 | c2 |

+------+------+

| 7 | 5 |

| 1 | 9 |

| 2 | 5 |

+------+------+

3 rows in set (0.00 sec)

2.3. COLUMNS分区

该类型分区是范围分区和列表分区的变形,支持多个列作为分区键,这些列共同决定数据插入时匹配分区和检索时的分区裁剪。此外范围COLUMNS和列表COLUMNS分区支持非数字类型作为分区键。

支持类型如下:

l All integer types: TINYINT, SMALLINT, MEDIUMINT, INT (INTEGER), and BIGINT. (This is the

l same as with partitioning by RANGE and LIST.)

l Other numeric data types (such as DECIMAL or FLOAT) are not supported as partitioning columns.

l DATE and DATETIME.

l Columns using other data types relating to dates or times are not supported as partitioning columns.

l The following string types: CHAR, VARCHAR, BINARY, and VARBINARY.

l TEXT and BLOB columns are not supported as partitioning columns.

2.3.1. 范围COLUMNS分区:类似于范围分区,但分区键支持非数字类型,并且分区键可以由多个列组成。

分区键不支持表达式,只能是列名。指定多个列作为分区键,那么比较方式是将多个字段作为元组进行比较的,不是单独比较。

CREATE TABLE rc1 (

a INT,

b INT

)

PARTITION BY RANGE COLUMNS(a, b) (

PARTITION p0 VALUES LESS THAN (5, 12),

PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)

);

mysql> INSERT INTO rc1 VALUES (5,10), (5,11), (5,12);

Query OK, 3 rows affected (0.00 sec)

Records: 3 Duplicates: 0 Warnings: 0

mysql> SELECT PARTITION_NAME,TABLE_ROWS

-> FROM INFORMATION_SCHEMA.PARTITIONS

-> WHERE TABLE_NAME = 'rc1';

+--------------+----------------+------------+

| TABLE_SCHEMA | PARTITION_NAME | TABLE_ROWS |

+--------------+----------------+------------+

| p | p0 | 2 |

| p | p1 | 1 |

+--------------+----------------+------------+

2 rows in set (0.00 sec)

mysql> SELECT (5,10) < (5,12), (5,11) < (5,12), (5,12) < (5,12);

+-----------------+-----------------+-----------------+

| (5,10) < (5,12) | (5,11) < (5,12) | (5,12) < (5,12) |

+-----------------+-----------------+-----------------+

| 1 | 1 | 0 |

+-----------------+-----------------+-----------------+

1 row in set (0.00 sec)

作为分区键的所有列,必须严格递增,对于超出元组最大值的行,可以指定MAXVALUE作为新分区。

严格递增是按照元组的元素顺序比较,可以通过查询语句进行测试:

mysql> SELECT (5,10) < (5,12), (5,11) < (5,12), (5,12) < (5,12);

+-----------------+-----------------+-----------------+

| (5,10) < (5,12) | (5,11) < (5,12) | (5,12) < (5,12) |

+-----------------+-----------------+-----------------+

| 1 | 1 | 0 |

+-----------------+-----------------+-----------------+

1 row in set (0.00 sec)

所以如下定义也是合法的

CREATE TABLE rc4 (

a INT,

b INT,

c INT

)

PARTITION BY RANGE COLUMNS(a,b,c) (

PARTITION p0 VALUES LESS THAN (0,25,50),

PARTITION p1 VALUES LESS THAN (10,20,100),

PARTITION p2 VALUES LESS THAN (10,30,50)

PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)

);

如下定义是不合法的

mysql> CREATE TABLE rcf (

-> a INT,

-> b INT,

-> c INT

-> )

-> PARTITION BY RANGE COLUMNS(a,b,c) (

-> PARTITION p0 VALUES LESS THAN (0,25,50),

-> PARTITION p1 VALUES LESS THAN (20,20,100),

-> PARTITION p2 VALUES LESS THAN (10,30,50),

-> PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)

-> );

ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

2.3.2. 列表COLUMNS分区:类似于列表分区,但分区键支持非数字类型,并且分区键可以由多个列组成

根据以上需求,可以创建以字符串作为分区键的列表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')

);

2.4. 分区表对于NULL值处理

MySQL允许NULL作为分区值,对于不同的分区类型对NULL的处理不一样

l 范围分区分区键的值为NULL的记录会被插入到最低的分区内

l 列表分区分区键的值为NULL的记录时,如果显示定了NULL为一个列表分区,则插入该分区,否则无法插入

l 哈希和键值分区会将NULL当做0来处理

3. 分区管理

5.6支持多种方式修改分区表,包括添加、删除、重定义、合并、拆分等操作

对于分区的添加删除比较简单,不给出具体说明和示例,参考语法图即可。

语法图:

ALTER [IGNORE] TABLE tbl_name

| ADD PARTITION (partition_definition)
  | DROP PARTITION partition_names
  | TRUNCATE PARTITION {partition_names | ALL}
  | COALESCE PARTITION number
  | REORGANIZE PARTITION partition_names INTO (partition_definitions)
  | EXCHANGE PARTITION partition_name WITH TABLE tbl_name
  | ANALYZE PARTITION {partition_names | ALL}
  | CHECK PARTITION {partition_names | ALL}
  | OPTIMIZE PARTITION {partition_names | ALL}
  | REBUILD PARTITION {partition_names | ALL}
  | REPAIR PARTITION {partition_names | ALL}
  | REMOVE PARTITIONING

partition_definition:
    PARTITION partition_name
        [VALUES 
            {LESS THAN {(expr | value_list) | MAXVALUE} 
            | 
            IN (value_list)}]
        [[STORAGE] ENGINE [=] engine_name]
        [COMMENT [=] 'comment_text' ]
        [DATA DIRECTORY [=] 'data_dir']
        [INDEX DIRECTORY [=] 'index_dir']
        [MAX_ROWS [=] max_number_of_rows]
        [MIN_ROWS [=] min_number_of_rows]
        [TABLESPACE [=] tablespace_name]
        [NODEGROUP [=] node_group_id]
        [(subpartition_definition [, subpartition_definition] ...)]

3.1. 将非分区表转换成分区表,该操作实质是删除原表并按照分区表方式重建,大数据量表慎用

CREATE TABLE trb3 (id INT, name VARCHAR(50), purchased DATE)

PARTITION BY RANGE( YEAR(purchased) ) (

PARTITION p0 VALUES LESS THAN (1990),

PARTITION p1 VALUES LESS THAN (1995),

PARTITION p2 VALUES LESS THAN (2000),

PARTITION p3 VALUES LESS THAN (2005)

);

ALTER TABLE trb3 PARTITION BY KEY(id) PARTITIONS 2;

3.2. 列表、范围分区管理:

范围分区时,可以增加一个大于最大分区的分区,但不可以在已存在分区或者最小分区之前增加分区。

mysql> ALTER TABLE members

> ADD PARTITION (

> PARTITION n VALUES LESS THAN (1960));

ERROR 1463 (HY000): VALUES LESS THAN value must be strictly »

increasing for each partition

可以通过重组第一个分区为两个新的范围分区:

mysql> ALTER TABLE members

> ADD PARTITION (

> PARTITION n VALUES LESS THAN (1960));

ERROR 1463 (HY000): VALUES LESS THAN value must be strictly »

increasing for each partition

同样列表分区也不能增加一个包含已有分区值的分区

CREATE TABLE tt (

id INT,

data INT

)

PARTITION BY LIST(data) (

PARTITION p0 VALUES IN (5, 10, 15),

PARTITION p1 VALUES IN (6, 12, 18)

);

mysql> ALTER TABLE tt ADD PARTITION

> (PARTITION np VALUES IN (4, 8, 12));

ERROR 1465 (HY000): Multiple definition of same constant »

in list partitioning

合并相邻分区:

ALTER TABLE members REORGANIZE PARTITION s0,s1 INTO (

PARTITION p0 VALUES LESS THAN (1970)

);

将多个分区合并为两个分区:

ALTER TABLE members REORGANIZE PARTITION p0,p1,p2,p3 INTO (

PARTITION m0 VALUES LESS THAN (1980),

PARTITION m1 VALUES LESS THAN (2000)

);

ALTER TABLE tt ADD PARTITION (PARTITION np VALUES IN (4, 8));

ALTER TABLE tt REORGANIZE PARTITION p1,np INTO (

PARTITION p1 VALUES IN (6, 18),

PARTITION np VALUES in (4, 8, 12)

);

关于列表分区和范围分区维护的要点:

l 新增分区不能和已有范围或者值重叠

l 合并分区应该考虑能够覆盖原有分区的范围或者值

l 合并范围分区只可以合并相邻分区,不能跨区合并

l 不能通过reorganize parition 语句改变表的分区类型,也不能改变分区键

3.3. 哈希和键值分区管理

可以使用ALTER TABLE ... COALESCE PARTITION语句合并分区

例如,将哈希分区表clients原12个分区减少为4个分区

CREATE TABLE clients (

id INT,

fname VARCHAR(30),

lname VARCHAR(30),

signed DATE

)

PARTITION BY HASH( MONTH(signed) )

PARTITIONS 12;

mysql> ALTER TABLE clients COALESCE PARTITION 4;

Query OK, 0 rows affected (0.02 sec)

将线性键值分区表clients原12分区减少为4个

mysql> CREATE TABLE clients_lk (

-> id INT,

-> fname VARCHAR(30),

-> lname VARCHAR(30),

-> signed DATE

-> )

-> PARTITION BY LINEAR KEY(signed)

-> PARTITIONS 12;

Query OK, 0 rows affected (0.03 sec)

mysql> ALTER TABLE clients_lk COALESCE PARTITION 4;

Query OK, 0 rows affected (0.06 sec)

Records: 0 Duplicates: 0 Warnings: 0

对于哈希分区和键值分区,合并表意味着减少分区,增加分区可以使用ALTER TABLE xxx ADD PARTITION:

ALTER TABLE clients ADD PARTITION PARTITIONS 6;

交换分区:

可以使用ALTER TABLE pt EXCHANGE PARTITION p WITH TABLE nt语句进行分区交换,但需要保证如下条件成立:

l nt表不是pt表的分区

l nt表不是临时表

l pt表和nt表的结构一致

l nt表不包括任何外键约束,其他表也不能有外键引用nt表

l nt表数据不能落在p分区范围外

l 必须有alter,insert,create,drop的权限

需要注意的是:

l 执行分区交换不会出发任何触发器,因为他是DDL操作

l 自增列将被重置

l 当使用交换分区语句时,ignore关键字无效

示例:

创建分区表

CREATE TABLE e (

id INT NOT NULL,

fname VARCHAR(30),

lname VARCHAR(30)

)

PARTITION BY RANGE (id) (

PARTITION p0 VALUES LESS THAN (50),

PARTITION p1 VALUES LESS THAN (100),

PARTITION p2 VALUES LESS THAN (150),

PARTITION p3 VALUES LESS THAN (MAXVALUE)

);

INSERT INTO e VALUES

(1669, "Jim", "Smith"),

(337, "Mary", "Jones"),

(16, "Frank", "White"),

(2005, "Linda", "Black");

创建表e2当做交换表

mysql> CREATE TABLE e2 LIKE e;

Query OK, 0 rows affected (1.34 sec)

mysql> ALTER TABLE e2 REMOVE PARTITIONING;

Query OK, 0 rows affected (0.90 sec)

Records: 0 Duplicates: 0 Warnings: 0

查询分区表信息:

mysql> SELECT PARTITION_NAME, TABLE_ROWS

-> FROM INFORMATION_SCHEMA.PARTITIONS

-> WHERE TABLE_NAME = 'e';

+----------------+------------+

| PARTITION_NAME | TABLE_ROWS |

+----------------+------------+

| p0 | 1 |

| p1 | 0 |

| p2 | 0 |

| p3 | 3 |

+----------------+------------+

执行分区交换:

mysql> ALTER TABLE e EXCHANGE PARTITION p0 WITH TABLE e2;

Query OK, 0 rows affected (0.28 sec)

查询分区信息,p0分区的数据变为0

mysql> SELECT PARTITION_NAME, TABLE_ROWS

-> FROM INFORMATION_SCHEMA.PARTITIONS

-> WHERE TABLE_NAME = 'e';

+----------------+------------+

| PARTITION_NAME | TABLE_ROWS |

+----------------+------------+

| p0 | 0 |

| p1 | 0 |

| p2 | 0 |

| p3 | 3 |

+----------------+------------+

4 rows in set (0.00 sec)

3.4. 重建分区:

实质类似于drop分区,然后将全部数据插入分区,目的是减少碎片。
示例:

ALTER TABLE t1 REBUILD PARTITION p0, p1;

3.5. 优化分区:

当分区内执行了大数据量delete操作,或者对有可变长度类型的行执行大量修改,可以使用该回收未使用空间和减少碎片

ALTER TABLE t1 OPTIMIZE PARTITION p0, p1;

和表优化操作一样,执行分区优化相当于在该分区上执行了CHECK PARTITION ,ANALYZE PARTITION , and REPAIR PARTITION

注意:一些存储引擎(包括InnoDB)并不支持分区优化,在5.6.9之后运行分区优化会重建整个表,一定要慎用,可以使用ALTER TABLE ...REBUILD PARTITION和ALTER TABLE ... ANALYZE PARTITION替代

示例:

ALTER TABLE t1 ANALYZE PARTITION p1;

ALTER TABLE trb3 CHECK PARTITION p1;

ALTER TABLE t1 REPAIR PARTITION p1;

3.6. 截断分区:

会删除整个分区的数据,由于是DDL操作,只产生少量日志,速度更快。

示例:

ALTER TABLE t1 TRUNCATE PARTITION p1;

3.7. 获取分区信息

l 使用SHOW CREATE TABLE语句查看创建分区表脚本

l 使用SHOW TABLE STATUS语句判断一个表是否是分区表,Create_options字段标注为partitioned

l 查询INFORMATION_SCHEMA.PARTITIONS获取分区信息

l 使用EXPLAIN PARTITIONS SELECT看查询语句是否使用分区裁剪

示例,查看分区裁剪信息

CREATE TABLE trb1 (id INT, name VARCHAR(50), purchased DATE)

PARTITION BY RANGE(id)

(

PARTITION p0 VALUES LESS THAN (3),

PARTITION p1 VALUES LESS THAN (7),

PARTITION p2 VALUES LESS THAN (9),

PARTITION p3 VALUES LESS THAN (11)

);

INSERT INTO trb1 VALUES

(1, 'desk organiser', '2003-10-15'),

(2, 'CD player', '1993-11-05'),

(3, 'TV set', '1996-03-10'),

(4, 'bookcase', '1982-01-10'),

(5, 'exercise bike', '2004-05-09'),

(6, 'sofa', '1987-06-05'),

(7, 'popcorn maker', '2001-11-22'),

(8, 'aquarium', '1992-08-04'),

(9, 'study desk', '1984-09-16'),

(10, 'lava lamp', '1998-12-25');

mysql> EXPLAIN PARTITIONS SELECT * FROM trb1\G

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: trb1

partitions: p0,p1,p2,p3

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 10

Extra: Using filesort

mysql> EXPLAIN PARTITIONS SELECT * FROM trb1 WHERE id < 5\G

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: trb1

partitions: p0,p1

type: ALL

possible_keys: NULL

key: NULL

key_len: NULL

ref: NULL

rows: 10

Extra: Using where

4. 分区裁剪

4.1. 自动分区裁剪

分区裁剪的概念很简单,就是没有匹配的记录就不扫描该分区,可以比通过全表或全分区扫描话费更少的时间,这一切都是优化器后台执行的,开发人员不需要关心。在查询时,MyISAM分区表由于设计原因,所有分区文件描述将会被打开。所以,使用MyISAM分区表的时候,要考虑文件打卡数量的限制。InnoDB不存在这样的问题。

使用分区裁剪的情况,适用于SELECT,DELETE,UPDATGE语句

l partition_column = constant(在特定情况下,也可以是<,>,<=,>=,<>,between and,是否能够分区裁剪这取决于优化器的分析结果)

l partition_column IN ( constant1, constant2, ..., constantN)

在时间范围分区中,分区键是时间函数表达式也可以进行分区裁剪:

示例1:范围分区的分区裁剪

CREATE TABLE t2 (

fname VARCHAR(50) NOT NULL,

lname VARCHAR(50) NOT NULL,

region_code TINYINT UNSIGNED NOT NULL,

dob DATE NOT NULL

)

PARTITION BY RANGE( YEAR(dob) ) (

PARTITION d0 VALUES LESS THAN (1970),

PARTITION d1 VALUES LESS THAN (1975),

PARTITION d2 VALUES LESS THAN (1980),

PARTITION d3 VALUES LESS THAN (1985),

PARTITION d4 VALUES LESS THAN (1990),

PARTITION d5 VALUES LESS THAN (2000),

PARTITION d6 VALUES LESS THAN (2005),

PARTITION d7 VALUES LESS THAN MAXVALUE

);

以下查询均可以用到分区裁剪:

SELECT * FROM t2 WHERE dob = '1982-06-23';

UPDATE t2 SET region_code = 8 WHERE dob BETWEEN '1991-02-15' AND '1997-04-25';

DELETE FROM t2 WHERE dob >= '1984-06-21' AND dob <= '1999-06-21'

示例2:列表分区的分区裁剪

CREATE TABLE t3 (

fname VARCHAR(50) NOT NULL,

lname VARCHAR(50) NOT NULL,

region_code TINYINT UNSIGNED NOT NULL,

dob DATE NOT NULL

)

PARTITION BY LIST(region_code) (

PARTITION r0 VALUES IN (1, 3),

PARTITION r1 VALUES IN (2, 5, 8),

PARTITION r2 VALUES IN (4, 9),

PARTITION r3 VALUES IN (6, 7, 10)

);

以下查询均可以用到分区裁剪:

select * from t3 where region_code =3

select * from t3 where region_code in(1,2,3)

select * from te where region_code between 1 and 3

示例3:哈希或者键值分区的分区裁剪

CREATE TABLE t4 (

fname VARCHAR(50) NOT NULL,

lname VARCHAR(50) NOT NULL,

region_code TINYINT UNSIGNED NOT NULL,

dob DATE NOT NULL

)

PARTITION BY KEY(region_code)

PARTITIONS 8;

以下均查询可以用到分区裁剪:

select * from t4 where region_code=7

select * from t4 where region_code > 2 and region_code < 6

select * from t4 between 3 and 5

4.2. 显示的指定分区

显示指定分区适用于如下语句:

l SELECT

l DELETE

l INSERT

l REPLACE

l UPDATE

l LOAD DATA.

l LOAD XML.

语法:

PARTITION ( partition_names)

partition_names :

partition_name , ...

示例:

CREATE TABLE employees (

id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,

fname VARCHAR(25) NOT NULL,

lname VARCHAR(25) NOT NULL,

store_id INT NOT NULL,

department_id INT NOT NULL

)

PARTITION BY RANGE(id) (

PARTITION p0 VALUES LESS THAN (5),

PARTITION p1 VALUES LESS THAN (10),

PARTITION p2 VALUES LESS THAN (15),

PARTITION p3 VALUES LESS THAN MAXVALUE

);

INSERT INTO employees VALUES

('', 'Bob', 'Taylor', 3, 2), ('', 'Frank', 'Williams', 1, 2),

('', 'Ellen', 'Johnson', 3, 4), ('', 'Jim', 'Smith', 2, 4),

('', 'Mary', 'Jones', 1, 1), ('', 'Linda', 'Black', 2, 3),

('', 'Ed', 'Jones', 2, 1), ('', 'June', 'Wilson', 3, 1),

('', 'Andy', 'Smith', 1, 3), ('', 'Lou', 'Waters', 2, 4),

('', 'Jill', 'Stone', 1, 4), ('', 'Roger', 'White', 3, 2),

('', 'Howard', 'Andrews', 1, 2), ('', 'Fred', 'Goldberg', 3, 3),

('', 'Barbara', 'Brown', 2, 3), ('', 'Alice', 'Rogers', 2, 2),

('', 'Mark', 'Morgan', 3, 3), ('', 'Karen', 'Cole', 3, 2);

mysql> SELECT * FROM employees PARTITION (p1);

+----+-------+--------+----------+---------------+

| id | fname | lname | store_id | department_id |

+----+-------+--------+----------+---------------+

| 5 | Mary | Jones | 1 | 1 |

| 6 | Linda | Black | 2 | 3 |

| 7 | Ed | Jones | 2 | 1 |

| 8 | June | Wilson | 3 | 1 |

| 9 | Andy | Smith | 1 | 3 |

+----+-------+--------+----------+---------------+

5 rows in set (0.00 sec)

注意:对于hash分区和线性分区表,没法在语句中指定分区名称的,MySQL会自动指定名称为p0,p1,p2,...,pN-1,N为定义的分区个数。如果还有自分区,那么自动指定子分区为pX(X为父分区编号),那么p1子分区的编号为p1sp1,p1sp2,...,p1spM-1,M为定义的子分区个数。

示例:

mysql> CREATE TABLE employees_sub (

-> id INT NOT NULL AUTO_INCREMENT,

-> fname VARCHAR(25) NOT NULL,

-> lname VARCHAR(25) NOT NULL,

-> store_id INT NOT NULL,

-> department_id INT NOT NULL,

-> PRIMARY KEY pk (id, lname)

-> )

-> PARTITION BY RANGE(id)

-> SUBPARTITION BY KEY (lname)

-> SUBPARTITIONS 2 (

-> PARTITION p0 VALUES LESS THAN (5),

-> PARTITION p1 VALUES LESS THAN (10),

-> PARTITION p2 VALUES LESS THAN (15),

-> PARTITION p3 VALUES LESS THAN MAXVALUE

-> );

Query OK, 0 rows affected (1.14 sec)

mysql> INSERT INTO employees_sub # re-use data in employees table

-> SELECT * FROM employees;

Query OK, 18 rows affected (0.09 sec)

Records: 18 Duplicates: 0 Warnings: 0

mysql> SELECT id, CONCAT(fname, ' ', lname) AS name

-> FROM employees_sub PARTITION (p2sp1);

+----+---------------+

| id | name |

+----+---------------+

| 10 | Lou Waters |

| 14 | Fred Goldberg |

+----+---------------+

2 rows in set (0.00 sec)

指定分区也可以用于关联:

mysql> SELECT

-> e.id AS 'Employee ID', CONCAT(e.fname, ' ', e.lname) AS Name,

-> s.city AS City, d.name AS department

-> FROM employees AS e

-> JOIN stores PARTITION (p1) AS s ON e.store_id=s.id

-> JOIN departments PARTITION (p0) AS d ON e.department_id=d.id

-> ORDER BY e.lname;

+-------------+---------------+-----------+------------+

| Employee ID | Name | City | department |

+-------------+---------------+-----------+------------+

| 14 | Fred Goldberg | Bellingen | Delivery |

| 5 | Mary Jones | Nambucca | Sales |

| 17 | Mark Morgan | Bellingen | Delivery |

| 9 | Andy Smith | Nambucca | Delivery |

| 8 | June Wilson | Bellingen | Sales |

+-------------+---------------+-----------+------------+

5 rows in set (0.00 sec)

5. 分区限制和特性

5.1. 分区的限制

l 自定义函数不可以用作分区表达式

l 用户变量不可以用于分区表达式

l 算数运算符/(除)不可以用于分区表达式

l 未操作运算符不可以用于分区表达式

l handler语句不支持分区表

l InnoDB的分区表不支持外键

l 不支持全文索引,空间类型列,ENUM类型

l 临时表、日志不能定义为分区表

l 自分区只能是哈希和键值分区

l 不支持DELAYED

l SQL mode的改变可能会引起用户自定义的的分区表崩溃或者丢失数据

l 唯一建、主键必须包含分区字段

5.2. 基于分区表的优化问题

l 创建和重建分区操作和文件系统有关,比如文件系统的类型、磁盘速度、交换分区大小等。在做这些操作时,确保large_files_support开启、open_files_limit设置较大

l MyISAM表需要打开更多的文件,所以如果分区表是MyISAM的,请设置合理的open_files_limit。

l 当对分区进行DDL操作时,会产生表锁,查询可以并发,但DML会阻塞到分区操作完成

l MyISAM分区表的性能要优于InnoDB

l 利用索引和分区裁剪能够大大提高查询效率,需要注意ICP(Index Condition Pushdown)不能应用于分区表

l 分区表能够加速LOAD DATA操作,每个分区需要130K的缓存用于此加速,分区表过多时会占用大量内存

l 5.6.7之前默认支持最大的分区数是1024,之后是8192,实际决定分区多少还需要考虑open_files_limit和系统本身支持也有关系。对于有几百个分区的表,往往不是一个很好的设计

l 不支持查询缓存,支持预存分区索引

5.3. 分区和锁

DML语句:

l update语句会在更新非分区字段时进行分区裁剪

l replace和insert会锁住插入行所在的分区,但如果是由自增列生成的分区则会锁住所有分区

l INSERT ... ON DUPLICATE KEY UPDATE如果非分区列更新将会分区裁剪

l INSERT...SELECT只锁那些需要读取的分区

DDL语句:

l CREATE VIEW将不会引起任何锁

l ALTER TABLE ... EXCHANGE PARTITION会进行分区裁剪

l ALTER TABLE ... TRUNCATE PARTITION会进行分区裁剪

l ALTER TABLE操作会产生表级别的metadata锁

mysql分区研究的更多相关文章

  1. mysql分区方案的研究

    笔者觉得,分库分表确实好的.但是,动不动搞分库分表,太麻烦了.分库分表虽然是提高数据库性能的常规办法,但是太麻烦了.所以,尝试研究mysql的分区到底如何. 之前写过一篇文章,http://www.c ...

  2. mysql分区

    <?php /* 分区 目录 18.1. MySQL中的分区概述 18.2. 分区类型 18.2.1. RANGE分区 18.2.2. LIST分区 18.2.3. HASH分区 18.2.4. ...

  3. 第18章:MYSQL分区

    第18章:分区 目录 18.1. MySQL中的分区概述 18.2. 分区类型 18.2.1. RANGE分区 18.2.2. LIST分区 18.2.3. HASH分区 18.2.4. KEY分区 ...

  4. Atitit 分区后的查询  mysql分区记录的流程与原理

    Atitit 分区后的查询  mysql分区记录的流程与原理 1.1.1. ibd是MySQL数据文件.索引文件1 1.2. 已经又数据了,如何分区? 给已有的表加上分区 ]1 1.3. 分成4个区, ...

  5. MySQL分区总结

    MySQL支持RANGE,LIST,HASH和KEY四种分区.其中,每个分区又都有一种特殊的类型.对于RANGE分区,有RANGE COLUMNS分区.对于LIST分区,有LIST COLUMNS分区 ...

  6. Mysql 分区处理NULL的得方式

    MySQL分区处理NULL值得方式 一般情况下,MySQL的分区把NULL当做零值,或者一个最小值进行处理 对于range分区 create table test_null( id int ) par ...

  7. mysql分区及实例演示

    一.为什么要分区? 需求:大数据.解决方案:分而治之,更细一点即为.将大表和大索引分为一个更小的操作单元 在mysql中,分区允许将表.索引和索引编排表细分为更小的单元.分区后,每个分区有自己单独的名 ...

  8. Mysql分区技术

    注:分区的语法可以看手册中有详细的写法和例子: show plugins; 此命令查看可有partition这个选项,有则mysql支持分区,没有的话,就可以升级一下mysql 实时监控一个命令执行情 ...

  9. 十五、mysql 分区之 分区管理

    1.mysql分区处理分区字段NULL值的方式 1.range分区null被当作最小值处理 2.list分区null值必须被枚举出来,否则将出错 3.hash/key分区 null值当作0处理 2.R ...

随机推荐

  1. xml+js+html的二级联动

    首先需要准备的文档是: cities.xml //主要是标注中国各省及其各省下的各个城市 内容如下: <?xml version="1.0" encoding="U ...

  2. day11 堡垒机

    项目实战:运维堡垒机开发 前景介绍 到目前为止,很多公司对堡垒机依然不太感冒,其实是没有充分认识到堡垒机在IT管理中的重要作用的,很多人觉得,堡垒机就是跳板机,其实这个认识是不全面的,跳板功能只是堡垒 ...

  3. PHP性能分析 - ngnx日志分析

    最终结果展示图: 图解:响应时间在40ms以内的请求数占请求总量的7%,40到80ms的的请求数占32.9%,依次类推... 性能问题有很多种可能,普通的情况通过xhprof可查得主要的性能损耗.但有 ...

  4. 浅谈C#中的接口和抽象类

    C#中接口为"其他方面互不相干"的类型提供公共的服务和特征:C#中class只支持但继承,使用接口却支持多继承,例如:C#中System.String是从System空间中的4个i ...

  5. Array.prototype.indexOf

    arr.indexOf(searchElement[, fromIndex = 0]) Array.prototype.indexOf()

  6. Gvim+Emmet.vim 那些事。

    转自:http://www.jianshu.com/p/67fa1e2114c5 背景 HTML和CSS的写法相对固定,而且重复的工作特别多,特别是尖括号实在是很烦.使用Emmet至少能帮你节省一半的 ...

  7. Trapping Rain Water

    Given n non-negative integers representing an elevation map where the width of each bar is 1, comput ...

  8. LEETCODE —— Single Number

    Given an array of integers, every element appears twice except for one. Find that single one. Note:Y ...

  9. winsock教程- windows下的socket编程(c语言实现)

    winsock教程- windows下的socket编程(c语言实现) 使用winsock进行socket 编程     这是一个学习windows下socket编程(c语言)的快速指南.这是因为一下 ...

  10. 【转】iOS bitcode实战 -- without full bitcode

    原文网址:http://www.voidcn.com/blog/GrowingGiant/article/p-5012705.html 关于bitcode的介绍,直接看下边两篇: 理解Bitcode: ...