MySQL 5.7 范围分区

范围分区
按范围分区的表的分区方式是这样的:每个分区包含分区表达式值位于给定范围内的行。范围应该是连续的但不重叠的,并且使用VALUES LESS THAN操作符定义。在下面的几个例子中,假设您要创建一个表,如下面所示,用于保存20个音像店连锁店的人事记录,编号为1到20:

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
);

这里使用的employees表没有主键或唯一键。虽然这些例子的效果与当前讨论的目的一致,但你应该记住,表在实践中很可能有主键、唯一键, 或者两者都有,如果存在分区列,那么是否允许分区列取决于用于这些键的列。

根据用户的需要,可以通过多种方式对该表进行分区。一种方法是使用store_id列。例如,你可能决定用4种方式对表进行分区,比如添加一个 partition by RANGE子句,如下所示:

mysql> 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)
    -> );
Query OK, 0 rows affected (0.02 sec)

在这个分区方案中,在商店1 ~ 5工作的员工所对应的所有行存储在p0分区中,在商店6 ~ 10工作的员工所对应的行存储在p1分区中,以此类推 。注意,每个分区都是按从低到高的顺序定义的。这是分区范围语法的要求。你可以把它想象成C或Java中的一系列的if…elseif语句。

我们很容易就能确定要将包含数据(72,’Mitchell’, ‘Wilson’, ‘1998-06-25’, NULL, 13)的新行插入到分区p2中,但是当链增加了第21个存 储时会发生什么呢?在这种模式下,没有规则覆盖store_id大于20的行,因此会报错,因为服务器不知道把它放在哪里。为了避免这种情况,可 以在CREATE TABLE语句中使用包罗万象的VALUES LESS THAN子句,该子句提供所有大于显式指定的最大值的值:

mysql> 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 MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

另一种避免在找不到匹配值时出错的方法是在INSERT语句中使用IGNORE关键字。

MAXVALUE表示一个整数值,总是大于可能的最大整数值(在数学语言中,它用作最小上界)。现在,任何store_id列值大于或等于16(定义的最 大值)的行都存储在分区p3中。在未来的某个时刻,当存储的数量增加到25、30或更多时,用户可以使用ALTER TABLE语句为存储21 ~ 25、26 ~ 30等添加新的分区(有关如何操作的细节,请参见22.3节)。

类似地,用户也可以根据员工的工作代码对表进行分区,也就是说,根据job_code列值的范围进行分区。例如,假设两位数的工作代码用于普通 (店内)员工,三位的工作代码用于办公室和技术支持人员,四位的工作代码用于管理职位,可以使用以下语句创建分区表:

mysql> 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 (job_code) (
    -> PARTITION p0 VALUES LESS THAN (100),
    -> PARTITION p1 VALUES LESS THAN (1000),
    -> PARTITION p2 VALUES LESS THAN (10000)
    -> );
Query OK, 0 rows affected (0.01 sec)

在本例中,所有与店内员工相关的行都存储在p0分区中,与办公室和支持人员相关的行存储在p1分区中,与经理相关的行存储在p2分区中。

也可以在VALUES LESS THAN子句中使用表达式。然而,MySQL必须能够将表达式的返回值作为小于(< )比较的一部分进行计算。 您可以使用基于两个日期列之一的表达式,而不是根据存储编号拆分表数据。例如,假设你希望根据每个员工离开公司的年份进行划分;也就是 YEAR(separated)。下面是一个实现这种分区方案的CREATE TABLE语句的例子:

mysql> 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,
    -> store_id INT
    -> )
    -> PARTITION BY RANGE ( YEAR(separated) ) (
    -> PARTITION p0 VALUES LESS THAN (1991),
    -> PARTITION p1 VALUES LESS THAN (1996),
    -> PARTITION p2 VALUES LESS THAN (2001),
    -> PARTITION p3 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.01 sec)


在本方案中,对于1991年以前离职的所有员工,所有行都存储在p0分区中;对于1991年到1995年离开的人,p1;对于1996年到2000年离开的人, 在p2;对于2000年后离开的工人,也就是p3。

也可以根据时间戳列的值对表进行范围分区,使用UNIX_TIMESTAMP()函数,如下面的例子所示。
mysql> 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)
    -> );
Query OK, 0 rows affected (0.05 sec)

不允许使用任何其他涉及TIMESTAMP值的表达式。(参见Bug #42849。)

当满足以下一个或多个条件时,范围分区特别有用:
.你想要或者需要删除“旧”数据。如果用户使用前面给出的employees表分区方案,可以使用ALTER table employees drop partition p0语句 ;删除与1991年之前停止为公司工作的员工的所有行。对于包含很多行的表,这比执行DELETE查询(例如DELETE FROM employees WHERE YEAR (separated) < = 1990;)要高效。 .您希望使用包含日期或时间值的列,或者包含从其他系列产生的值。 .用户经常会执行直接依赖于用于分区表的列的查询。例如,当执行类似于EXPLAIN SELECT COUNT(*) FROM employees WHERE BETWEEN '2000- 01-01' AND '2000-12-31' GROUP BY store_id;的查询时,MySQL可以很快确定只有分区p2需要被扫描,因为剩余的分区不能包含任何满足 WHERE子句的记录。 这种分区的一种变体是范围列分区。根据范围列进行分区使得可以使用多个列来定义分区范围,这些范围既适用于分区中的行位置,也适用于在 执行分区修剪时确定包含或排除特定的分区。 基于时间间隔的分区方案。如果你想在MySQL 5.7中实现一个基于范围或时间间隔的分区方案,你有两个选择: 1.按范围对表进行分区,对于分区表达式,使用一个操作DATE、TIME或DATETIME列并返回整数值的函数,如下所示:

mysql> 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
    -> );
Query OK, 0 rows affected (0.02 sec)

在MySQL 5.7中,也可以使用UNIX_TIMESTAMP()函数,根据时间戳列的值对表进行范围分区,如下面的例子所示:
mysql> 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)
    -> );
Query OK, 0 rows affected (0.05 sec)

在MySQL 5.7中,任何涉及时间戳值的表达式都是不允许的。(参见Bug #42849。)

在MySQL 5.7中也可以使用UNIX_TIMESTAMP(timestamp_column)作为按列表分区的表的分区表达式。然而,这样做通常是不实际的。

2.按范围列对表进行分区,使用DATE或DATETIME列作为分区列。例如,成员表可以直接使用合并的列定义,如下所示:

mysql> 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 COLUMNS(joined) (
    -> PARTITION p0 VALUES LESS THAN ('1960-01-01'),
    -> PARTITION p1 VALUES LESS THAN ('1970-01-01'),
    -> PARTITION p2 VALUES LESS THAN ('1980-01-01'),
    -> PARTITION p3 VALUES LESS THAN ('1990-01-01'),
    -> PARTITION p4 VALUES LESS THAN MAXVALUE
    -> );
Query OK, 0 rows affected (0.02 sec)

使用除 DATE 或 DATETIME 之外的日期或时间类型的分区列,RANGE COLUMNS 不支持这种用法。

发表评论

电子邮件地址不会被公开。