針對Mysql數據庫,表分區類型簡析。
【1】表分區類型
(1)Range分區:按范圍分區。按列值的范圍區間進行分區存儲;比如:id小於10存儲在一個分區;id大於10小於20存儲在另外一個分區;
(2)List分區:按離散值集合分區。與range分區類似,不過它是按離散值進行分區。
(3)Hash分區:按hash算法結果分區。對用戶定義的表達式所返回的hash值來進行分區。
可以寫partitions num(分區數目),或直接使用分區語句,比如partition p0 values in … ..
根據分區算法的不同,Hash分區分為一般(取模法)Hash分區和線性(線性Hash規則,詳見下線性hash分區內容)Hash分區。
(4)Key分區:Key分區類似於Hash分區,不同點如下:
[1] Key分區允許多列,而Hash分區只允許一列。
[2] 如果在有主鍵或者唯一鍵的情況下,Key中分區列可不指定,默認為主鍵或者唯一鍵;如果沒有,則必須顯性指定列。
[3] Key分區對象必須為列,而不能是基於列的表達式。
[4] Key分區和Hash分區的算法不一樣。PARTITION BY HASH (expr),MOD取值的對象是expr返回的值,而PARTITION BY KEY (column_list),基於列的MD5值。
(5)子分區:在一級分區的基礎上,再進行分區后才存儲。
【2】Range分區
Range分區創建表SQL語句:
-- ----------------------------BEGIN RANGE--------------------- -- 按Range范圍分區 -- [1]刪除舊表 DROP TABLE `t_partition_by_range`; -- [2]創建新表 CREATE TABLE `t_partition_by_range` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) DEFAULT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL ) ENGINE = INNODB PARTITION BY RANGE(id) ( PARTITION p0 VALUES LESS THAN(5), PARTITION p1 VALUES LESS THAN(10), PARTITION p2 VALUES LESS THAN(15) ); -- [3]添加源數據 INSERT INTO t_partition_by_range (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 1, 1801111, 0), ('suntao', 9, 'weinan', 3, 1803110, 1), ('liuyan', 16, 'hancheng', 2, 20190211, 0), ('xuhui', 22, 'hancheng', 4, 201904107, 1), ('wangqi', 18, 'xian', 10, 201910104, 1), ('baihua', 16, 'nanjing', 8, 201908105, 1), ('xiaoping', 15, 'shenzhen', 6, 20190603, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_range'; -- ----------------------------END RANGE---------------------
分區存儲信息結果:

如上表,理論與實際結合分析:
把id < 5的4個實體(wangchao、suntao、liuyan、xuhui)存儲在分區p0;
把5 < id < 10的3個實體(wangqi、baihua、xiaoping)存儲在分區p1;
把10 < id < 15的實體會存儲在分區p2;
【3】List分區
List分區創建表SQL語句:
-- ----------------------------BEGIN LIST--------------------- -- 按List范圍分區 -- [1]刪除舊表 DROP TABLE `t_partition_by_list`; -- [2]創建新表 CREATE TABLE `t_partition_by_list` ( `id` INT AUTO_INCREMENT , `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) NOT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL, PRIMARY KEY (`id`, `sGrade`) ) ENGINE = INNODB PARTITION BY LIST(sGrade) ( PARTITION p0 VALUES IN(1, 3), PARTITION p1 VALUES IN(2, 4, 6), PARTITION p3 VALUES IN(10) ); -- [3]添加源數據 INSERT INTO t_partition_by_list (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 1, 1801111, 0), ('suntao', 9, 'weinan', 3, 1803110, 1), ('liuyan', 16, 'hancheng', 2, 20190211, 0), ('xuhui', 22, 'hancheng', 4, 201904107, 1), ('wangqi', 18, 'xian', 10, 201910104, 1), ('baihua', 16, 'nanjing', 1, 201908105, 1), ('xiaoping', 15, 'shenzhen', 6, 20190603, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_list'; -- ----------------------------END LIST---------------------
分區存儲信息結果:

如上表,理論與實際結合分析:
把班級sgrade歸屬於離散值集合為(1、3)的3個實體(wangchao、suntao、baihua)存儲在分區p0;
把班級sgrade歸屬於離散值集合為(2、4、6)的3個實體(liuyan、xuhui、xiaoping)存儲在分區p1;
把班級sgrade歸屬於離散值集合為(10)的1個實體(wangqi)存儲在分區p2;
另外,關於list分區表,尤其注意:當分區字段的值不歸屬於任何一個離散值集合時,數據會插入失敗!
比如:插入源數據SQL語句:
INSERT INTO t_partition_by_list (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 1, 1801111, 0), ('suntao', 9, 'weinan', 3, 1803110, 1), ('liuyan', 16, 'hancheng', 2, 20190211, 0), ('xuhui', 22, 'hancheng', 4, 201904107, 1), ('wangqi', 18, 'xian', 10, 201910104, 1), ('baihua', 16, 'nanjing', 8, 201908105, 1), ('xiaoping', 15, 'shenzhen', 6, 20190603, 1);
插入失敗,失敗信息如下:
查詢:INSERT INTO t_partition_by_list (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 1, 1801111, 0), ('su...錯誤代碼: 1526
Table has no partition for value 8
【4】Hash分區
(1)一般Hash分區
Hash分區創建表SQL語句:
-- ----------------------------BEGIN HASH--------------------- -- 按Hash值分區 -- [1]刪除舊表 DROP TABLE `t_partition_by_hash`; -- [2]創建新表 CREATE TABLE `t_partition_by_hash` ( `id` INT AUTO_INCREMENT PRIMARY KEY, `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) NOT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL ) ENGINE = INNODB PARTITION BY HASH(id) PARTITIONS 4; -- [3]添加源數據 INSERT INTO t_partition_by_hash (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 3, 1803111, 0), ('suntao', 9, 'weinan', 6, 1806110, 1), ('liuyan', 16, 'hancheng', 8, 20190811, 0), ('xuhui', 22, 'hancheng', 12, 201912107, 1), ('wangqi', 18, 'xian', 11, 201911104, 1), ('baihua', 16, 'nanjing', 10, 201910105, 1), ('xiaoping', 15, 'shenzhen', 9, 20190103, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_hash'; -- ----------------------------END HASH---------------------
分區存儲信息結果:

如上表,理論與實際結合分析:我們知道,一般情況下(非一般情況比如線性hash),hash分區使用的哈希函數為求模數。
那我們先利用Mysql的求模數函數分析一下,執行SQL如下:
SELECT id, MOD(id, 4) AS p_index FROM t_partition_by_hash ORDER BY id;
結果如下圖:

現在再結合上圖分區信息的結果,我們得知:
把id哈希值為0的1個實體(xuhui)存儲在分區p0;
把id哈希值為1的2個實體(wangchao、wangqi)存儲在分區p1;
把id哈希值為2的2個實體(suntao、baihua)存儲在分區p2;
把id哈希值為3的2個實體(liuyan、xiaoping)存儲在分區p3;
好的,分析至此,我們查詢一下數據存儲的實際情況:
SQL語句如下:
SELECT * FROM t_partition_by_hash;
執行結果如下圖:

如上圖,查詢時,數據庫引擎會按照分區順序p0~p3依次讀取到各個分區的數據內容。
(2)線性Hash分區
線性Hash分區創建表SQL語句:
-- ----------------------------BEGIN LINEAR HASH--------------------- -- 按Linear Hash值分區 -- [1]刪除舊表 DROP TABLE `t_partition_by_linear_hash`; -- [2]創建新表 CREATE TABLE `t_partition_by_linear_hash` ( `id` INT NOT NULL, `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) NOT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL ) ENGINE = INNODB PARTITION BY LINEAR HASH(sGrade) PARTITIONS 6; -- [3]添加源數據 INSERT INTO t_partition_by_linear_hash (id, sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES (1, 'wangchao', 8, 'heyang', 3, 1803111, 0), (2, 'suntao', 9, 'weinan', 6, 1806110, 1), (3, 'liuyan', 16, 'hancheng', 8, 20190811, 0), (4, 'xuhui', 22, 'hancheng', 12, 201912107, 1), (5, 'wangqi', 18, 'xian', 11, 201911104, 1), (6, 'baihua', 16, 'nanjing', 10, 201910105, 1), (7, 'xiaoping', 15, 'shenzhen', 9, 20190103, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_linear_hash'; -- ----------------------------END LINEAR HASH---------------------
分區存儲信息結果:

學習一下線性Hash分區的規則:
假設分區個數num = 6, 那么N表示數據最終存儲的分區:
[1] 第一步,確定V值
V值計算公式:V = POWER(2, CEILING(LOG(2, num)))
LOG()是計算num以2為底的對數,CEILING()是向上取整,POWER()是取2的次方值。
假設num的值是2的倍數那么這個表達式計算出來的結果不變。
V = POWER(2, CEILING(LOG(2, 6)))
V = POWER(2, 3)
V = 8
[2] 第二步,計算N值
N值計算公式:N = values & (V - 1)
&位與運算,將兩個值都轉換成二進制進行求與運算。
N = values & (V - 1)
N = 3 & (8 - 1)
N = 3 & 7
N = 3
[3] 第三步(當N >= num場景)
因為分區個數為num,那么,當N >= num時,顯然分區仍未知,需要再求N值:
N值計算公式:N = N & (CEIL(V / 2) - 1)
N = 6 & (CEIL(8 / 2) - 1)
N = 6 & 3
N = 2
由於2不大於或等於6,所以可以確定存儲在分區p2。
依據以上規則,下面首先,我們計算一級分區存儲索引號,SQL語句如下:
SELECT id, sGrade & (POWER(2, CEILING(LOG(2, 6))) - 1) AS p_index FROM t_partition_by_linear_hash ORDER BY id;
結果如下圖:

很明顯,id為2的實體,一級N值為6,需要進行第三步再確認,經規則示例分析,再確認結果應該為2。
好的,至此為止,我們已經分析完成,驗證源數據存儲實際情況,結果如下圖:

經驗證,可知:
sGrade為8(id為3)的實體被存儲在分區p0;
sGrade為9(id為7)的實體被存儲在分區p1;
sGrade為6、10(id為2、6)的實體被存儲在分區p2;
sGrade為3、11(id為1、5)的實體被存儲在分區p3;
sGrade為12(id為4)的實體被存儲在分區p4;
綜上所述,實際與理論分析完全一致。
【5】Key分區
Key分區創建表SQL語句:
-- ----------------------------BEGIN KEY----------------------- -- [1]刪除舊表 DROP TABLE `t_partition_by_key`; -- [2]創建新表 CREATE TABLE `t_partition_by_key` ( `id` INT AUTO_INCREMENT, `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) NOT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL, PRIMARY KEY (`id`, `sGrade`) ) ENGINE = INNODB PARTITION BY KEY(sGrade) PARTITIONS 6; -- [3]添加源數據 INSERT INTO t_partition_by_key (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 3, 1803111, 0), ('suntao', 9, 'weinan', 3, 1803110, 1), ('liuyan', 16, 'hancheng', 8, 20190811, 0), ('xuhui', 22, 'hancheng', 8, 201908107, 1), ('wangqi', 18, 'xian', 10, 201910104, 1), ('baihua', 16, 'nanjing', 10, 201910105, 1), ('xiaoping', 15, 'shenzhen', 9, 20190903, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_key'; -- ----------------------------END KEY---------------------
分區存儲信息結果:

總結:從分區存儲信息結果可知,p2分區存儲了3個實體;p3分區存儲了2個實體;p5分區存儲了2個實體。的確已全部存儲。
鑒於目前只明確Key分區的規則是基於列的MD5值,但具體為什么7個實體分別放在了p2、p3、p5分區,細節分析未果,待再研究。
附加目前分析進展,MD5值如下圖:
查詢SQL語句:
SELECT id, sGrade, MD5(sGrade) FROM t_partition_by_key ORDER BY id;
結果集:

【6】子分區
子分區創建表SQL語句:
-- ----------------------------BEGIN SUB PARTITION--------------------- -- 按SUB PARTITION分區 -- [1]刪除舊表 DROP TABLE `t_partition_by_subpart`; -- [2]創建新表 CREATE TABLE `t_partition_by_subpart` ( `id` INT AUTO_INCREMENT, `sName` VARCHAR(10) NOT NULL, `sAge` INT(2) UNSIGNED ZEROFILL NOT NULL, `sAddr` VARCHAR(20) DEFAULT NULL, `sGrade` INT(2) NOT NULL, `sStuId` INT(8) DEFAULT NULL, `sSex` INT(1) UNSIGNED DEFAULT NULL, PRIMARY KEY (`id`, `sGrade`) ) ENGINE = INNODB PARTITION BY RANGE(id) SUBPARTITION BY HASH(sGrade) SUBPARTITIONS 2 ( PARTITION p0 VALUES LESS THAN(5), PARTITION p1 VALUES LESS THAN(10), PARTITION p2 VALUES LESS THAN(15) ); -- [3]添加源數據 INSERT INTO t_partition_by_subpart (sName, sAge, sAddr, sGrade, sStuId, sSex) VALUES ('wangchao', 8, 'heyang', 3, 1803111, 0), ('suntao', 9, 'weinan', 3, 1803110, 1), ('liuyan', 16, 'hancheng', 8, 20190811, 0), ('xuhui', 22, 'hancheng', 8, 201908107, 1), ('wangqi', 18, 'xian', 10, 201910104, 1), ('baihua', 16, 'nanjing', 10, 201910105, 1), ('xiaoping', 15, 'shenzhen', 9, 20190903, 1); -- [4]查詢分區信息 SELECT PARTITION_NAME, TABLE_ROWS, PARTITION_METHOD, PARTITION_EXPRESSION, PARTITION_DESCRIPTION, PARTITION_ORDINAL_POSITION FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't_partition_by_subpart'; -- ----------------------------END SUB PARTITION---------------------
分區存儲信息結果:

如上表,理論與實際結合分析:
把id < 5的4個實體(wangchao、suntao、liuyan、xuhui)存儲在分區p0;
把5 < id < 10的3個實體(wangqi、baihua、xiaoping)存儲在分區p1;
把10 < id < 15的實體會存儲在分區p2;
按子分區再分析:
子分區為hash分區,即以sGrade值取模值作為分區索引號,查詢SQL如下:
SELECT id, sGrade, MOD(sGrade, 2) FROM t_partition_by_subpart ORDER BY id;
分區索引號結果如下圖:

id為1、2、3、4的實體,按主range分區,應該歸屬於p0分區;再按子hash分區:
因為實體1、2(wangchao、suntao)的sGrade值均為3,取模后值為1,即存儲在p0的第二個子分區;
因為實體3、4(liuyan、xuhui)的sGrade值均為8,取模后置為0,即存儲在p0的第一個子分區;
id為5、6、7的實體,按主range分區,應該歸屬於p1分區;再按子hash分區:
因為實體5、6(wangqi、baihua)的sGrade值均為10,取模后值為0,即存儲在p1的第一個子分區;
因為實體7(xiaoping)的sGrade值為9,取模后值為1,即存儲在p1的第二個子分區。
好的,分析至此,我們查詢一下數據存儲的實際情況:
SQL語句如下:
SELECT * FROM t_partition_by_subpart;
實際情況:

如上圖,說明:
因為查詢時數據庫引擎會按照分區順序p0~p3依次讀取到各個分區(包括子分區)存儲的數據內容,可見與我們的理論分析結果相符。
Good Good Study, Day Day Up.
順序 選擇 循環 總結
