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_colDATE 類型時,則 TO_DAYS(date_col) 運算式會被認為與 date_col 的值直接變化,因為 date_col 的值每次變更,運算式的值都會以一致的方式變更。相較於 TO_DAYS(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 的值變更給定數量可能會在運算式的值中產生極大的差異變更。例如,將 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;

如果您將一筆 col3 值為 '2005-09-15' 的記錄插入到 t1 中,那麼該記錄的儲存分割區將按如下方式決定:

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

MySQL 8.4 也支援一種稱為線性雜湊HASH 分割變體,該變體採用更複雜的演算法來決定新插入分割表格的列之放置位置。有關此演算法的描述,請參閱第 26.2.4.1 節,「線性 HASH 分割」

每次插入或更新記錄時,都會評估使用者提供的表達式。它也可能在刪除記錄時被評估,這取決於具體情況。