Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  HASH Partitioning

26.2.4 哈希分区

使用 HASH 分区可以确保数据在预定义的分区数中均匀分布。在范围或列表分区中,您必须明确指定哪个分区应该存储给定的列值或列值集;使用哈希分区,系统将为您处理这个决定,您只需要指定一个列值或基于列值的表达式来进行哈希处理,并指定要将表分区的数量。

要使用 HASH 分区表,需要在 CREATE TABLE 语句中添加 PARTITION BY HASH (expr) 子句,其中 expr 是一个返回整数的表达式。这可以简单地是整数类型列的名称。此外,您可能还想添加 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 在分区范围上进行“修剪”。也就是说,表达式的变化程度越接近于基于该列的值,MySQL 就可以更高效地使用该表达式进行哈希分区。

例如,其中 date_col 是类型为 DATE 的列,那么表达式 TO_DAYS(date_col) 被认为是直接随着 date_col 的值变化的,因为对于 date_col 的每个变化,表达式的值都以一致的方式变化。表达式 YEAR(date_col) 对于 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_col5 改为 6 将产生表达式值的变化为 -1,但将 int_col6 改为 7 将产生表达式值的变化为 -7

换言之,列值与表达式值之间的图形越接近直线 y=cx,其中 c 是某个非零常数,那么该表达式就越适合哈希。这与表达式的非线性程度有关,因为越非线性的表达式越容易在分区中产生不均匀的数据分布。

理论上,多列值的表达式也可以进行修剪,但是确定哪些表达式适合使用可以非常困难和耗时。因此,不太建议使用涉及多个列的哈希表达式。

当使用 PARTITION BY HASH 时,存储引擎根据表达式的结果的模数确定记录存储在哪个分区中。换言之,对于给定的表达式 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.3 还支持一种称为 线性哈希 的哈希分区变体,该变体使用更复杂的算法来确定插入到分区表中的新行的位置。请参阅 第 26.2.4.1 节,“LINEAR HASH 分区”,以获取该算法的描述。

用户提供的表达式将在每次插入或更新记录时被评估。它还可能—取决于情况—在删除记录时被评估。