按范围分区的表将被分区,以便每个分区包含的行的分区表达式值在给定的范围内。范围应该是连续的,但不能重叠,并使用 VALUES LESS THAN
运算符定义。例如,假设您创建了一个表来存储一链视频店的员工记录,编号从 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
表没有主键或唯一键。虽然这些示例在当前讨论中有效,但请注意,在实践中,表通常具有主键、唯一键或两者,并且允许的分区列选择取决于这些键的使用情况。有关这些问题的讨论,请参见 第 26.6.1 节,“分区键、主键和唯一键”。
该表可以按范围分区,以满足不同的需求。例如,您可以使用 store_id
列。例如,您可能决定将表分区 4 次,添加一个 PARTITION BY RANGE
子句,如下所示:
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)
);
在这个分区方案中,所有员工记录对应的行都存储在分区 p0
中,员工记录对应的行存储在分区 p1
中,以此类推。每个分区都是按顺序定义的,从最低到最高。这是 PARTITION BY RANGE
语法的要求;您可以将其视为一系列 C 或 Java 中的 if ... elseif ...
语句。
很容易确定一个新的行包含数据 (72, 'Mitchell', 'Wilson', '1998-06-25', DEFAULT, 7, 13)
将被插入到分区 p2
中,但当您的连锁店添加第 21 个店铺时会发生什么?在这个方案中,没有规则涵盖 store_id
大于 20 的行,因此服务器不知道将其放在哪里。您可以通过在 CREATE TABLE
语句中使用一个 “catchall” VALUES LESS THAN
子句来避免这种情况:
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
);
(与本章中的其他示例一样,我们假设默认存储引擎是 InnoDB
。)
避免在没有匹配值时出现错误的另一种方法是使用 IGNORE
关键字作为 INSERT
语句的一部分。有关示例,请参见 第 26.2.2 节,“LIST 分区”。
MAXVALUE
代表一个整数值,该值总是大于可能的最大整数值(在数学语言中,它充当最小上界)。现在,任何 store_id
列值大于或等于 16(最高定义值)的行都将被存储在分区 p3
中。在将来某个时候—当店铺数量增加到 25、30 或更多时—您可以使用 ALTER TABLE
语句来添加新的分区,例如店铺 21-25、26-30 等(请参见 第 26.3 节,“分区管理”,了解如何执行此操作)。
以类似的方式,您可以根据员工工作代码对表进行分区,即根据 job_code
列值的范围进行分区。例如—假设使用两位数工作代码用于普通(店内)员工、三位数代码用于办公和支持人员、四位数代码用于管理职位—您可以使用以下语句创建分区表:
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)
);
在这种情况下,所有与店内员工相关的行都将被存储在分区 p0
中,与办公和支持人员相关的行存储在 p1
中,而与管理人员相关的行存储在分区 p2
中。
您也可以在 VALUES LESS THAN
子句中使用表达式。但是,MySQL 必须能够将表达式的返回值作为 LESS THAN
(<
) 比较的一部分来评估。
相比于根据店铺编号拆分表数据,您可以使用基于两个DATE
列之一的表达式。例如,假设您想根据员工离开公司的年份进行分区,即YEAR(separated)
的值。下面是一个实现这种分区方案的CREATE TABLE
语句示例:
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
);
在这种方案中,对于所有在1991年之前离开的员工,行将被存储在分区p0
中;对于1991年至1995年之间离开的员工,在p1
中;对于1996年至2000年之间离开的员工,在p2
中;对于2000年之后离开的员工,在p3
中。
也可以根据TIMESTAMP
列的值使用UNIX_TIMESTAMP()
函数对表进行分区,如下所示:
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)
);
任何其他涉及TIMESTAMP
值的表达式都不允许。(见Bug #42849。)
范围分区特别适用于以下情况:
-
您想要或需要删除“旧”数据。如果您使用前面所示的分区方案对
employees
表进行分区,可以简单地使用ALTER TABLE employees DROP PARTITION p0;
删除所有在1991年之前离开的员工记录。(见第15.1.9节,“ALTER TABLE语句”和第26.3节,“分区管理”,以获取更多信息。)对于具有大量行的表,这比运行DELETE
查询,如DELETE FROM employees WHERE YEAR(separated) <= 1990;
,要高效得多。 -
您想要使用包含日期或时间值的列,或者包含来自其他系列的值。
-
您经常运行依赖于用于分区表的列的查询。例如,当执行查询,如
EXPLAIN SELECT COUNT(*) FROM employees WHERE separated BETWEEN '2000-01-01' AND '2000-12-31' GROUP BY store_id;
时,MySQL可以快速确定只有分区p2
需要被扫描,因为其他分区不能包含满足WHERE子句的记录。见第26.4节,“分区修剪”,以获取更多信息。
这种分区类型的变体是RANGE COLUMNS
分区。使用RANGE COLUMNS
分区可以使用多个列来定义分区范围,这些范围既用于行的放置也用于确定在执行分区修剪时包括或排除特定分区。见第26.2.3.1节,“RANGE COLUMNS分区”,以获取更多信息。
基于时间间隔的分区方案 如果您想在MySQL 8.3中实现基于时间间隔的分区方案,有两个选项:
-
使用
DATE
、TIME
或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 8.3中,也可以使用
TIMESTAMP
列的值和UNIX_TIMESTAMP()
函数对表进行分区,如下所示: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) );
在MySQL 8.3中,任何其他涉及
TIMESTAMP
值的表达式都不允许。(见Bug #42849。)Note在 MySQL 8.3 中,也可以使用
UNIX_TIMESTAMP(timestamp_column)
作为分区表达式,用于按LIST
分区的表。但是,这样做通常不太实际。 -
使用
RANGE COLUMNS
分区表,使用DATE
或DATETIME
列作为分区列。例如,members
表可以使用joined
列直接定义,如下所示: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 );