MySQL 9.0 参考手册  /  ...  /  HASH 分区

26.2.4 HASH 分区

使用 HASH 进行分区主要用于确保数据在预定数量的分区之间均匀分布。对于范围或列表分区,您必须明确指定应在哪个分区中存储给定的列值或列值集;对于哈希分区,此决定由系统自动完成,您只需指定要进行哈希处理的列值或基于列值的表达式,以及要将分区表划分的目标分区数。

要使用 HASH 分区对表进行分区,需要在 CREATE TABLE 语句后附加一个 PARTITION BY HASH (expr) 子句,其中 expr 是一个返回整数的表达式。这可以简单地是其类型为 MySQL 整数类型之一的列的名称。此外,您很可能希望在其后加上 PARTITIONS num,其中 num 是一个正整数,表示要将表划分的目标分区数。

注意

为简单起见,以下示例中的表不使用任何键。您应该注意,如果表有任何唯一键,则此表的分区表达式中使用的每一列都必须是每个唯一键的一部分,包括主键。有关更多信息,请参阅 第 26.6.1 节“分区键、主键和唯一键”

以下语句创建一个表,该表在 store_id 列上使用哈希,并分为 4 个分区

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 HASH(store_id)
PARTITIONS 4;

如果您不包含 PARTITIONS 子句,则分区数默认为 1;使用 PARTITIONS 关键字而不带后面的数字会导致语法错误。

您还可以使用返回 expr 整数的 SQL 表达式。例如,您可能希望根据员工被雇用的年份进行分区。这可以按如下所示进行

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 HASH( YEAR(hired) )
PARTITIONS 4;

expr 必须返回非恒定、非随机的整数值(换句话说,它应该是可变的但确定的),并且不得包含 第 26.6 节“分区的限制” 中所述的任何禁止的构造。您还应该记住,每次插入或更新(或可能删除)行时都会计算此表达式;这意味着非常复杂的表达式可能会导致性能问题,尤其是在执行一次影响大量行的操作(例如批量插入)时。

最有效的哈希函数是对单个表列进行操作的函数,其值随列值一致地增加或减少,因为这允许对分区范围进行“修剪”。也就是说,表达式的变化与它所基于的列的值的变化越接近,MySQL 就越能有效地使用该表达式进行哈希分区。

例如,其中 date_col 是类型为 DATE 的列,则表达式 TO_DAYS(date_col) 被认为与 date_col 的值直接相关,因为对于 date_col 值的每次更改,表达式的值都会以一致的方式更改。表达式 YEAR(date_col) 相对于 date_col 的变化不如 TO_DAYS(date_col) 那么直接,因为并非 date_col 的所有可能变化都会在 YEAR(date_col) 中产生等效的变化。即便如此,YEAR(date_col) 也是哈希函数的良好候选者,因为它直接随 date_col 的一部分而变化,并且 date_col 中不可能发生导致 YEAR(date_col) 中不成比例变化的变化。

相比之下,假设您有一个名为 int_col 的列,其类型为 INT。现在考虑表达式 POW(5-int_col,3) + 6。这将是哈希函数的一个糟糕选择,因为 int_col 值的变化不能保证在表达式值中产生成比例的变化。将 int_col 的值更改给定量可能会在表达式值中产生 widely differing changes。例如,将 int_col5 更改为 6 会导致表达式值更改 -1,但将 int_col 的值从 6 更改为 7 会导致表达式值更改 -7

换句话说,列值与表达式值的关系图越接近于方程 y=cx(其中 c 是某个非零常数)绘制的直线,则该表达式越适合于哈希。这与以下事实有关:表达式越是非线性,它在分区之间产生的数据分布往往越不均匀。

从理论上讲,对于涉及多个列值的表达式,修剪也是可能的,但确定哪些表达式合适可能非常困难且耗时。因此,不特别建议使用涉及多列的哈希表达式。

使用 PARTITION BY HASH 时,存储引擎会根据表达式结果的模数确定使用 num 个分区中的哪个分区。换句话说,对于给定的表达式 expr,存储记录的分区是分区号 N,其中 N = MOD(expr, num)。假设表 t1 定义如下,以便它有 4 个分区

CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE)
    PARTITION BY HASH( YEAR(col3) )
    PARTITIONS 4;

如果在 t1 中插入一条记录,其 col3 值为 '2005-09-15',则存储它的分区确定如下

MOD(YEAR('2005-09-01'),4)
=  MOD(2005,4)
=  1

MySQL 9.0 还支持 HASH 分区的一种变体,称为 线性哈希,它采用更复杂的算法来确定插入到分区表中的新行的放置。有关此算法的描述,请参阅 第 26.2.4.1 节“LINEAR HASH 分区”

每次插入或更新记录时,都会计算用户提供的表达式。根据情况,在删除记录时也可能会对其进行计算。