MySQL 9.0 参考手册  /  分区  /  分区修剪

26.4 分区修剪

称为 分区修剪 的优化基于一个相对简单的概念,可以描述为 不扫描不可能存在匹配值的分区。假设通过以下语句创建分区表 t1

CREATE TABLE t1 (
    fname VARCHAR(50) NOT NULL,
    lname VARCHAR(50) NOT NULL,
    region_code TINYINT UNSIGNED NOT NULL,
    dob DATE NOT NULL
)
PARTITION BY RANGE( region_code ) (
    PARTITION p0 VALUES LESS THAN (64),
    PARTITION p1 VALUES LESS THAN (128),
    PARTITION p2 VALUES LESS THAN (192),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);

假设您希望从如下 SELECT 语句中获取结果

SELECT fname, lname, region_code, dob
    FROM t1
    WHERE region_code > 125 AND region_code < 130;

很容易看出,应该返回的所有行都不在分区 p0p3 中;也就是说,我们只需要搜索分区 p1p2 即可找到匹配的行。通过限制搜索范围,与扫描表中的所有分区相比,可以花费更少的时间和精力来查找匹配的行。这种“剪除” 不需要的分区的操作称为 修剪。当优化器可以在执行此查询时利用分区修剪时,查询的执行速度可以比针对包含相同列定义和数据的非分区表的相同查询快一个数量级。

只要 WHERE 条件可以简化为以下两种情况之一,优化器就可以执行修剪

  • partition_column = constant

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

在第一种情况下,优化器只需计算给定值的分区表达式,确定包含该值的分区,然后仅扫描此分区。在许多情况下,等号可以用另一个算术比较运算符代替,包括 <><=>=<>。在 WHERE 子句中使用 BETWEEN 的某些查询也可以利用分区修剪。请参阅本节后面的示例。

在第二种情况下,优化器计算列表中每个值的分区表达式,创建一个匹配分区列表,然后仅扫描此分区列表中的分区。

SELECTDELETEUPDATE 语句支持分区修剪。INSERT 语句在每个插入的行中也只访问一个分区;即使对于通过 HASHKEY 分区的表也是如此,尽管这在 EXPLAIN 的输出中当前未显示。

修剪也可以应用于短范围,优化器可以将这些范围转换为等效的值列表。例如,在前面的示例中,WHERE 子句可以转换为 WHERE region_code IN (126, 127, 128, 129)。然后,优化器可以确定列表中的前两个值位于分区 p1 中,其余两个值位于分区 p2 中,并且其他分区不包含相关值,因此无需搜索匹配的行。

对于使用 RANGE COLUMNSLIST COLUMNS 分区的表,优化器还可以对涉及多列上前述类型比较的 WHERE 条件执行修剪。

只要分区表达式由可以简化为一组等式的等式或范围组成,或者当分区表达式表示递增或递减关系时,就可以应用这种类型的优化。当分区表达式使用 YEAR()TO_DAYS() 函数时,也可以将修剪应用于在 DATEDATETIME 列上分区的表。当分区表达式使用 TO_SECONDS() 函数时,也可以将修剪应用于此类表。

假设使用此处所示的语句创建在 DATE 列上分区的表 t2

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

以下使用 t2 的语句可以利用分区修剪

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'

在最后一种情况下,优化器还可以执行以下操作

  1. 查找包含范围低端的分区.

    YEAR('1984-06-21') 产生值 1984,该值位于分区 d3 中。

  2. 查找包含范围高端的分区.

    YEAR('1999-06-21') 的计算结果为 1999,该值位于分区 d5 中。

  3. 仅扫描这两个分区以及可能位于它们之间的任何分区.

    在这种情况下,这意味着仅扫描分区 d3d4d5。可以安全地忽略其余分区(并且会被忽略)。

重要

在针对分区表的语句的 WHERE 条件中引用的无效 DATEDATETIME 值将被视为 NULL。这意味着诸如 SELECT * FROM partitioned_table WHERE date_column < '2008-12-00' 之类的查询不会返回任何值(请参阅错误 #40972)。

到目前为止,我们只看了使用 RANGE 分区的示例,但修剪也可以应用于其他分区类型。

考虑一个按 LIST 分区的表,其中分区表达式是递增或递减的,例如此处所示的表 t3。(在本例中,为了简洁起见,我们假设 region_code 列的值限制在 1 到 10 之间,包括 1 和 10。)

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 BETWEEN 1 AND 3 之类的语句,优化器会确定值 1、2 和 3 位于哪些分区中(r0r1),并跳过其余分区(r2r3)。

对于通过 HASH[LINEAR] KEY 分区的表,如果 WHERE 子句在分区表达式中使用的列上使用简单的 = 关系,则也可以进行分区修剪。考虑如下创建的表

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;

可以修剪将列值与常量进行比较的语句

UPDATE t4 WHERE region_code = 7;

修剪也可以用于短范围,因为优化器可以将此类条件转换为 IN 关系。例如,使用先前定义的同一个表 t4,可以修剪以下查询

SELECT * FROM t4 WHERE region_code > 2 AND region_code < 6;

SELECT * FROM t4 WHERE region_code BETWEEN 3 AND 5;

在这两种情况下,优化器都会将 WHERE 子句转换为 WHERE region_code IN (3, 4, 5)

重要

仅当范围大小小于分区数时,才使用此优化。考虑以下语句

DELETE FROM t4 WHERE region_code BETWEEN 4 AND 12;

WHERE 子句中的范围涵盖 9 个值(4、5、6、7、8、9、10、11、12),但 t4 只有 8 个分区。这意味着 DELETE 不能被修剪。

当表通过 HASH[LINEAR] KEY 分区时,修剪只能用于整数列。例如,以下语句不能使用修剪,因为 dobDATE

SELECT * FROM t4 WHERE dob >= '2001-04-14' AND dob <= '2005-10-15';

但是,如果表在 INT 列中存储年份值,则可以使用 WHERE year_col >= 2001 AND year_col <= 2005 修剪查询。

如果表使用提供自动分区的存储引擎(例如 MySQL 集群使用的 NDB 存储引擎),则如果对这些表进行显式分区,则可以对其进行修剪。