使用 HASH
分区可以确保数据在预定义的分区数中均匀分布。在范围或列表分区中,您必须明确指定哪个分区应该存储给定的列值或列值集;使用哈希分区,系统将为您处理这个决定,您只需要指定一个列值或基于列值的表达式来进行哈希处理,并指定要将表分区的数量。
要使用 HASH
分区表,需要在 CREATE TABLE
语句中添加 PARTITION BY HASH (
子句,其中 expr
)expr
是一个返回整数的表达式。这可以简单地是整数类型列的名称。此外,您可能还想添加 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 在分区范围上进行“修剪”。也就是说,表达式的变化程度越接近于基于该列的值,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_col
从 5
改为 6
将产生表达式值的变化为 -1
,但将 int_col
从 6
改为 7
将产生表达式值的变化为 -7
。
换言之,列值与表达式值之间的图形越接近直线 y=
,其中 c
xc
是某个非零常数,那么该表达式就越适合哈希。这与表达式的非线性程度有关,因为越非线性的表达式越容易在分区中产生不均匀的数据分布。
理论上,多列值的表达式也可以进行修剪,但是确定哪些表达式适合使用可以非常困难和耗时。因此,不太建议使用涉及多个列的哈希表达式。
当使用 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 分区”,以获取该算法的描述。
用户提供的表达式将在每次插入或更新记录时被评估。它还可能—取决于情况—在删除记录时被评估。