HASH分区
分区依据HASH
主要用于确保在预定数量的分区之间均匀分布数据。使用范围或列表分区时,必须明确指定应将给定列值或一组列值存储在哪个分区中;使用散列分区时,这个决定会由您来解决,您只需要基于要散列的列值和要划分分区表的分区数来指定列值或表达式。
要使用分区对表进行HASH
分区,必须在CREATE TABLE
语句后附加一个子句,其中的是一个返回整数的表达式。这可以只是列的名称,该列的类型是MySQL的整数类型之一。此外,您很可能希望在后面加上,其中是一个正整数,代表该表将被划分为的分区数。PARTITION BY HASH(expr)
expr
PARTITIONS num
num
注意为简单起见,以下示例中的表不使用任何键。您应该注意,如果表具有任何唯一键,则该表的分区表达式中使用的每一列都必须是每个唯一键(包括主键)的一部分。有关更多信息,请参见“分区键,主键和唯一键”。
以下语句创建一个在store_id
列上使用哈希的表,并分为4个分区:
CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULLDEFAULT '1970-01-01', separated DATE NOT NULLDEFAULT '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 NULLDEFAULT '1970-01-01', separated DATE NOT NULLDEFAULT '9999-12-31', job_code INT, store_id INT )PARTITION BY HASH ( YEAR(hired) )PARTITIONS 4;
expr
必须返回一个非恒定的,非随机的整数值(换句话说,它应该是可变的但是确定的),并且不得包含“分区的限制和限制”中所述的任何禁止的构造。您还应该记住,每次插入或更新(或可能删除)一行时,都会对该表达式进行求值;这意味着非常复杂的表达式可能会引起性能问题,尤其是在执行一次影响很多行的操作(例如批处理插入)时。
最有效的散列函数是对单个表列进行操作的函数,其值与列值一致地增加或减少,因为这允许对分区范围进行“修剪”。也就是说,表达式随着其所基于的列的值变化的越紧密,MySQL可以更有效地使用该表达式进行哈希分区。
例如,where date_col
是type的列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
给定的数量会导致表达式的值产生很大不同的变化。例如,改变int_col
从5
对6
产生的变化-1
表达式的值,但改变的值int_col
,从6
对7
产生的变化-7
在表达式值中。
换句话说,列值与表达式的值的曲线越接近方程式所描绘的直线(其中某个非零常数),则表达式越适合哈希。这与以下事实有关:表达式越非线性,它倾向于产生的分区之间的数据分布就越不均匀。y=cx
c
从理论上讲,也可以对涉及多个列值的表达式进行修剪,但是要确定哪种表达式合适则可能非常困难且耗时。因此,不特别推荐使用涉及多列的哈希表达式。
当PARTITION BY HASH
被使用时,存储引擎确定哪个分区num
分区使用基于表达式的结果的模量。换句话说,对于给定的表达式expr
,存储记录的分区为分区编号N
,其中。假设该表定义如下,因此它具有4个分区:N= MOD(expr,num)
t1
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.0还支持一种HASH
称为线性哈希的分区变体,该变体采用一种更复杂的算法来确定插入分区表中的新行的位置。有关此算法的说明,请参见“线性哈希分区”。
每次插入或更新记录时,都会评估用户提供的表达式。当删除记录时,还可以根据情况对它进行评估。
线性哈希分区
MySQL还支持线性散列,这与常规散列的不同之处在于,线性散列使用线性二乘幂算法,而常规散列则使用散列函数值的模数。
从语法上讲,线性散列分区和常规散列之间的唯一区别是LINEAR
在PARTITION BY
子句中添加了关键字,如下所示:
CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30), hired DATE NOT NULLDEFAULT '1970-01-01', separated DATE NOT NULLDEFAULT '9999-12-31', job_code INT, store_id INT )PARTITION BY LINEAR HASH ( YEAR(hired) )PARTITIONS 4;
给定一个表达式expr
,使用线性哈希处理时存储记录的分区是分区中的分区号N
,其中的num
分区号N
是根据以下算法得出的:
找到大于2的下一个幂
num
。我们把这个值V
;可以计算为:V = POWER(2, CEILING(LOG(2, num)))
(假设
num
为13。LOG(2,13)
则为3.7004397181411。CEILING(3.7004397181411)
为4,V
==POWER(2,4)
为16。)- 设置
N
=F
(column_list
)&(V
-1) 而
N
>=num
:- 设置
V
=V
/ 2 - 设置
N
=N
&(V
--1)
- 设置
假设t1
使用以下语句创建使用线性哈希分区并具有6个分区的表:
CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATE) PARTITION BY LINEAR HASH( YEAR(col3) ) PARTITIONS 6;
现在,假设您要插入两个记录以使其t1
具有col3
列值'2003-04-14'
和'1998-10-19'
。其中第一个的分区号确定如下:
V = POWER(2, CEILING( LOG(2,6) )) = 8 N = YEAR('2003-04-14') & (8 - 1) = 2003 & 7 = 3 (3 >= 6 is FALSE: record stored in partition #3)
计算第二条记录所在的分区号,如下所示:
V = 8 N = YEAR('1998-10-19') & (8 - 1) = 1998 & 7 = 6 (6 >= 6 is TRUE: additional step required) N = 6 & ((8 / 2) - 1) = 6 & 3 = 2 (2 >= 6 is FALSE: record stored in partition #2)
通过线性哈希进行分区的优势在于,可以更快,更快速地添加,删除,合并和分割分区,这在处理包含大量数据(兆兆字节)的表时可能是有益的。缺点是,与使用常规哈希分区获得的分布相比,数据不太可能在分区之间均匀分布。