MySQL 8.4 参考手册  /  ...  /  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 关键字而不带数字会导致语法错误。

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

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 变化。例如,将 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 8.4 还支持 HASH 分区的变体,称为 线性哈希,它采用更复杂的算法来确定插入到分区表中的新行的放置位置。有关此算法的说明,请参阅 第 26.2.4.1 节“LINEAR HASH 分区”

每次插入或更新记录时都会评估用户提供的表达式。它也可能(取决于情况)在删除记录时进行评估。