MySQL 8.4 Reference Manual  /  ...  /  HASH Partitioning

26.2.4 HASH 分区

通过使用 HASH 分区,可以确保数据在预先确定的分区数中均匀分布。与范围或列表分区不同,你必须明确指定哪个分区应该存储给定的列值或一组列值;而使用哈希分区,你只需要指定一个基于列值或表达式的哈希函数以及将要分区的表格所划分的分区数。

为了使用 HASH 分区对表格进行分区,你需要在 CREATE TABLE 语句的末尾添加一个 PARTITION BY HASH (expr) 子句,其中 expr 是一个返回整数的表达式。这可以简单地是列名,该列类型为 MySQL 的整型之一。另外,你可能还想跟随这个子句使用 PARTITIONS num,其中 num 是一个正整数,表示表格将被划分成的分区数。

Note

为了简单起见,以下示例中的表格不使用任何键。你应该知道,如果表格有唯一键,每个用于分区表达式的列都必须是每个唯一键的一部分,包括主键。请参阅 第 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_ DAZE(date_ col) 被认为与 date_ col 的值变化一致,因为对于每个 date_ col 值的变化,表达式的值都发生了一个一致的变化。表达式 YEAR(date_ col)date_ col 的变化不如 TO_ DAZE(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 的值一个给定的量可以产生表达式值变化的巨大差异。例如,将 int_col5 更改为 6 会使表达式值减少 -1,但将 int_col6 更改为 7 会使表达式值减少 -7

换句话说,列值与表达式值之间的图形越接近直线,随着方程 y = c*x 描述的直线,其中 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;

如果你将一个值为 '2005-09-15' 的记录插入到 t1 中,那么它将被存储在哪个分区是通过以下方式确定的:

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

MySQL 8.4 还支持一种名为 线性哈希HASH 分区变体,该算法用于确定插入到分区表中的新行的位置。有关该算法的描述,请参阅Section 26.2.4.1, “LINEAR HASH Partitioning”

用户提供的表达式在每次插入或更新记录时都会被评估。它也可能—根据情况—在删除记录时被评估。