一、表分區
1、對錶分區的原因
數據庫數據越來越大,導致單個表中數據太多。以至於增刪改查詢速度變慢,而且由於表的鎖機制導致應用操作也受到嚴重影響,出現了數據庫性能瓶頸。
2、表分區
表分區就是將一個表的數據按照一定的規則水平劃分爲不同的邏輯塊,並分別進行物理存儲,這個規則就叫做分區函數,可以有不同的分區規則,通過”show plugins”語句可以查看當前MySQL是否支持表分區功能,mysql5.7社區版默認開啓了表分區功能。
3、進行表分區的優勢
(1)可以允許在一個表裏存儲更多的數據,突破磁盤限制或者文件系統限制
(2)對於從表裏將過期或歷史的數據移除在表分區很容易實現,只要將對應的分區移除即可
(3)對某些查詢和修改語句來說,可以自動將數據範圍縮小到一個或幾個表分區上,優化語句執行效率。而且可以通過顯示指定表分區來執行語句
3、表分區的類型
(1)RANGE表分區:範圍表分區,按照一定的範圍值來確定每個分區包含的數據
(2)LIST表分區:列表表分區,按照一個確定的值來確定每個分區包含的數據
(3)HASH表分區:哈希表分區,按照一個自定義的函數返回值來確定每個分區包含的數據
(4)KEY表分區 :key表分區,與哈希表分區類似,只是用MySQL自己的HASH函數來確定每個分區包含的數據
二、各種類型表分區的使用
1、RANGE表分區的使用
RANG表分區(範圍表分區),按照一定的範圍值來確定每個分區包含的數據,分區函數使用的字段必須只能是整數類型,分區的定義範圍必須是連續的,且不能有重疊部分,通過使用VALUES LESS THAN來定義分區範圍,表分區的範圍定義是從小到大定義的。
mysql> create table range_test(id int not null,
name1 varchar(30),name2 varchar(30),
hired date not null default '1970-01-01',
separated date not null default '2999-12-31',
job_code int not null, store_id int not null) partition by range (store_id)
(partition p0 values less than(6),partition p1 values less than(12),
partition p2 values less than(18),partition p3 values less than(24));
# 插入數據
mysql> insert into range_test values(1,'dayi','dayi123',now(),now(),2,6),(2,'dy','liu',now(),now(),5,10);
# 查詢時可以指定具體的分區表查詢
mysql> select * from range_test partition (p1);
+----+-------+---------+------------+------------+----------+----------+
| id | name1 | name2 | hired | separated | job_code | store_id |
+----+-------+---------+------------+------------+----------+----------+
| 1 | dayi | dayi123 | 2018-09-30 | 2018-09-30 | 2 | 6 |
| 2 | dy | liu | 2018-09-30 | 2018-09-30 | 5 | 10 |
+----+-------+---------+------------+------------+----------+----------+
# 由於分區表p0中沒有數據所以查不到
mysql> select * from range_test partition (p0);
Empty set (0.00 sec)
# 查看數據文件時每個分區表都會生成一個獨立的數據文件
]# ls /data/mysql/data/test/range_test*
range_test.frm range_test#P#p0.ibd range_test#P#p1.ibd range_test#P#p2.ibd range_test#P#p3.ibd
當插入的數據的分區字段的值不在分區表指定的範圍時會報錯。這是需要修改下表的定義,可以使用MAXVALUE關鍵詞表示可能的最大值。
對timestamp字段類型可以使用的表達式目前僅有unix_timestamp(作用是將時間轉化爲時間戳),其他的表達式都不允許。對於date及datetime類型的數據可以將”year”提取出來。
# 依據timestamp類型字段表分區時將該字段轉化成時間戳
mysql> CREATE TABLE range_test02 ( id INT NOT NULL,name VARCHAR(20) NOT NULL,update_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )PARTITION BY RANGE ( UNIX_TIMESTAMP(update_date) )
( PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2017-01-01 00:00:00') ),
PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2018-01-01 00:00:00') ),
PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2019-01-01 00:00:00') ),
PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-10-01 00:00:00') ),
PARTITION p9 VALUES LESS THAN (MAXVALUE) );
mysql> insert into range_test02 values(1,'dayi123',now()),(2,'testname','2030-01-02 12:00:00');
Query OK, 2 rows affected (2.34 sec)
# 使用date類型的數據表分區時可將”year”提取出來利用”year”進行表分區
mysql> create table range_test04(tstamp date) partition by range(year(tstamp))(partition p0 values less than(2018));
2、list表分區的使用
使用list表分區時,PARTITION BY LIST(expr)分區函數表達式必須返回整數,取值範圍通過VALUES IN (value_list)定義。
# 創建基於list表分區的表
mysql> create table list_test01(id int not null,
name 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 list(store_id)
(partition p_north values in (1,4,7),
partition p_east values in (2,5,8),
partition p_west values in (3,6,9));
基於list表分區的表創建注意項:
(1)對List表分區來說,沒有MAXVALUE特殊值,所有的可能取值都需要再VALUES IN中包含,如果有定義的取值則會報錯.
(2)當有主鍵或者唯一鍵存在的情況下,分區函數字段需要包含在主鍵或唯一鍵中。
3、hash表分區的使用
哈希表分區是按照一個自定義的函數返回值來確定每個分區包含的數據,這個自定義函數也可以僅僅是一個字段名字
通過PARTITION BY HASH (expr)子句來表達哈希表分區,其中的expr表達式必須返回一個整數,基於分區個數的取模(%)運算。根據餘數插入到指定的分區,對哈希表分區來說只需要通過” PARTITIONS”定義分區的個數,其他的事情由內部完成;如果沒有寫明PARTITIONS字段,則默認爲1。
# 創建基於hash表分區的表
mysql> create table hash_test01(id int not null,name varchar(30),
hired date not null default '1970-01-01',
separated date not null default '2999-12-31',
job_code int,store_id INT) partition by hash(store_id)
partitions 5;
# 查看創建的hash表分區的分區表
mysql> select table_name,partition_name from information_schema.partitions where table_name='hash_test01';
+-------------+----------------+
| table_name | partition_name |
+-------------+----------------+
| hash_test01 | p0 |
| hash_test01 | p1 |
| hash_test01 | p2 |
| hash_test01 | p3 |
| hash_test01 | p4 |
+-------------+----------------+
4、key表分區的使用
key表分區與哈希表分區類似,只不過哈希表分區依賴於自定義的函數,而key表分區的哈希算法是依賴MySQL本身。
Key表分區通過CREATE TABLE ... PARTITION BY KEY ()語句創建,括號裏面可以包含0個或者多個字段,所引用的字段必須是主鍵或者主鍵的一部分,如果括號裏面沒有字段,則代表使用主鍵,如果表中沒有主鍵但有唯一鍵,則使用唯一鍵,但唯一鍵字段必須定義爲not null,否則報錯。Key表分區所引用的字段未必是整數類型,其他的類型也可以使用。
# 沒有指定字段,則使用主鍵
mysql> CREATE TABLE key_test01 ( id INT NOT NULL PRIMARY KEY, name VARCHAR(20) ) PARTITION BY KEY() PARTITIONS 2;
# 指定字符串作爲key表分區的字段
mysql> create table key_test02(id int,tname char(30) primary key) partition by key(tname) partitions 10;
5、創建子表分區
子表分區,是在表分區的基礎上再創建表分區的概念,每個表分區下的子表分區個數必須一致,在MySQL5.7版本中,子表分區必須是範圍/列表分區+哈希/key子表分區的組合。
# 創建字表分區
mysql> create table test_zb01(id int,purchased date)
partition by range(year(purchased))
subpartition by hash(to_days(purchased))
subpartitions 2
(partition p0 values less than(2000),
partition p1 values less than(2020),
partition p2 values less than maxvalue);
# 查看字表的表名
mysql> select table_name,partition_name,SUBPARTITION_NAME from information_schema.partitions where table_name='test_zb01';
+------------+----------------+-------------------+
| table_name | partition_name | SUBPARTITION_NAME |
+------------+----------------+-------------------+
| test_zb01 | p0 | p0sp0 |
| test_zb01 | p0 | p0sp1 |
| test_zb01 | p1 | p1sp0 |
| test_zb01 | p1 | p1sp1 |
| test_zb01 | p2 | p2sp0 |
| test_zb01 | p2 | p2sp1 |
+------------+----------------+-------------------+
字表在不指定表名時,表名時系統默認分配的。子表分區也可以顯示的指定子表分區的名字。
# 創建字表時指定子分區表名
mysql> create table test_zb02(id int,purchased date)
partition by range(year(purchased)) subpartition by hash(to_days(purchased)) subpartitions 2
(partition p0 values less than(2000)
(subpartition s1,subpartition s2),
partition p1 values less than(2020)
(subpartition s3,subpartition s4),
partition p2 values less than maxvalue
(subpartition s5,subpartition s6));
4、進行表分區時需要注意的事項
當表中含有主鍵或唯一鍵時,每個被用作分區函數的字段必須是表中唯一鍵和主鍵的全部或一部分,否則就無法創建分區表。
# 由於唯一鍵和主鍵沒有相同的字段,所以無法創建表分區
mysql> create table pattern_test01(
-> id int primary key,
-> name varchar(12) unique,
-> age int) partition by range (id)
-> (partition p0 values less than(10),
-> partition p1 values less than(20));
ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table's partitioning function
# 不使用唯一鍵可創建成功
mysql> create table pattern_test02(
-> id int primary key,
-> name varchar(12),
-> age int) partition by range (id)
-> (partition p0 values less than(10),
-> partition p1 values less than(20));
Query OK, 0 rows affected (0.16 sec)
# 同時將主鍵設置爲唯一鍵
mysql> create table pattern_test03(
-> id int,name varchar(12),age int,
-> primary key (id,name),
-> unique key (id)) partition by range (id)
-> (partition p0 values less than(10),
-> partition p1 values less than(20));
Query OK, 0 rows affected (0.14 sec)
三、表分區的管理
表分區創建完成後可以通過alter table命令可以執行增加,刪除,重新定義,合併或者拆分表分區的管理動作。對不同類型的表分區管理方法也有不同。
1、表分區的刪除
對範圍表分區和列表表分區來說,刪除一個表分區時使用命令” ALTER TABLE table_name DROP PARTITION partition_name”刪除;刪除表分區的動作不光會把分區刪掉,也會把表分區裏原來的數據給刪除掉。
# 查看創建表的語句
mysql> show create table range_test;
+------------+----------------------------------------------------+
|Tabl|CreateTable
+------------+-----------------------------------------+
| range_test | CREATE TABLE `range_test` (
`id` int(11) NOT NULL,
`name1` varchar(30) DEFAULT NULL,
`name2` varchar(30) DEFAULT NULL,
`hired` date NOT NULL DEFAULT '1970-01-01',
`separated` date NOT NULL DEFAULT '2999-12-31',
`job_code` int(11) NOT NULL,
`store_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY RANGE (store_id)
(PARTITION p0 VALUES LESS THAN (6) ENGINE = InnoDB,
PARTITION p1 VALUES LESS THAN (12) ENGINE = InnoDB,
PARTITION p2 VALUES LESS THAN (18) ENGINE = InnoDB,
PARTITION p3 VALUES LESS THAN (24) ENGINE = InnoDB) */ |
+------------+----------------------------------------------------------+
# 刪除分區表p3
mysql> alter table range_test drop partition p3;
2、增加表分區
(1)範圍表分區(range)上增加及重新組織表分區
在原分區上增加一個表分區可以通過alter table … add partition語句來完成。對範圍表分區來說,增加的表分區必須在尾部增加,在頭部或者在中間增加都會失敗,對此可以通過REORGANIZ增加表分區及重組表分區。
# 增加表分區
mysql> alter table range_test add partition (partition p3 values less than maxvalue);
# 使用REORGANIZ命令創建表分區,相當於將p0分成兩個表分區
mysql> alter table range_test REORGANIZE PARTITION p0 into (partition n0 values less than (3),partition n1 values less than (6));
#使用REORGANIZ命令也可將拆分的表分區合併
mysql> alter table range_test REORGANIZE PARTITION n0,n1 into (partition p0 values less than(6));
(2)列表表分區(list)上增加及重新組織表分區
對列表表分區來說,只要新增加的分區對應的值在之前的表分區中沒有出現過,就可以通過alter table… add partition來增加。也可以通過REORGANIZE命令將之前的多個分區合併成一個或幾個分區,但要保持分區值一致;對列表分區重組時,重新組織的分區必須是相鄰的分區。
# 增加表分區
mysql> alter table list_test01 add partition (partition pCentral values in(10,11,12));
# 重新組織表分區
mysql> alter table list_test01 REORGANIZE partition p_north,p_east,p_west into (partition p0 values in (1,2,3),partition p1 values in (4,5,6),partition p2 values in (7,8,9));
如果表裏已有的數據在新重組的分區中沒有指定的值,則數據會丟失
3、哈希表分區和key表分區的管理
對哈希表分區和KEY表分區的管理手段與範圍和列表表分區完全不同,比如不能刪除表分區,但可以通過ALTER TABLE ... COALESCE PARTITION語句合併表分區,其partition後面的數字代表縮減的個數,而不是縮減到的個數;如果是增加表分區,則可以使用add partition語句。
# 將表hash_test01的表分區縮減到三個
mysql> alter table hash_test01 COALESCE PARTITION 3;
# 對錶has_test01再增加10個表分區
mysql> alter table hash_test01 ADD PARTITION PARTITIONS 10;
四、表分區的其他管理操作
1、創建range和list表分區時,分區函數可以包含多個字段
分區多字段函數所涉及的字段類型可以包括TINYINT、SMALLINT、MEDIUMINT、INT (INTEGER)、 BIGINT、DATE、DATETIME、CHAR、VARCHAR、BINARY、 VARBINARY。
# 創建多字段表分區的rang分區表
mysql> create table columns_test(id int not null,name varchar(30),
tname varchar(30),store_id int) partition by range columns(id,tname,store_id)
(partition p0 values less than (100,'dayi123',3),
partition p1 values less than (200,'dy',6),
partition p2 values less than (MAXVALUE,MAXVALUE,MAXVALUE));
2、對null值得處理
(1)在範圍表分區中,如果插入的是NULL值,則將數據放到最小的分區表裏
(2)在list表分區中,支持NULL值的唯一情況就是某個分區的允許值中包含NULL
(3)對哈希表分區和Key表分區來說,NULL值會被當成0值對待
3、表分區數據的交換
對分區表可以通過ALTER TABLE pt EXCHANGE PARTITION p WITH TABLE nt命令將一個分區或者是子分區的數據與普通的表的數據相互交換,其本身的表結構不會變化;交換的分區表和目標表必須結構完全相同,包括字段,類型,索引,存儲引擎必須完全一樣。
# 創建用於數據交換分區的表
mysql> create table ext_test01 like range_test;
# 數據交換
mysql> alter table ext_test01 remove partitioning;
mysql> alter table range_test exchange partition p1 with table ext_test01;
# 查看數據交換後各表的數據
mysql> select * from ext_test01;
+----+-------+---------+------------+------------+----------+----------+
| id | name1 | name2 | hired | separated | job_code | store_id |
+----+-------+---------+------------+------------+----------+----------+
| 1 | dayi | dayi123 | 2018-09-30 | 2018-09-30 | 2 | 6 |
| 2 | dy | liu | 2018-09-30 | 2018-09-30 | 5 | 10 |
+----+-------+---------+------------+------------+----------+----------+
mysql> select * from range_test partition (p1);
Empty set (0.00 sec)
4、表分區的其他管理命令
(1)使用rebuild命令除去分區碎片
mysql> alter table range_test rebuild partition p0,p1;
(2)OPTIMIZE命令可以回收分區中未使用的空間和重新獲取統計資料
mysql> alter table range_test optimize partition p0,p1,p2;
(3)Analyzing partitions命令用於重新收集分區統計資料
mysql> alter table range_test analyze partition p3;
(4)Repairing partitions命令用於修復異常的分區
mysql> alter table range_test repair partition p0,p1;
(5)Checking partitions命令檢查分區中數據或者索引數據是否損壞
mysql> alter table range_test check partition p0;
(6)ALTER TABLE ... TRUNCATE PARTITION命令用來刪除分區中的所有數據
mysql> alter table range_test truncate partition p3;
5、表分區的修剪和選擇
表分區修剪是MySQL優化的一種,其核心就是隻掃描需要的分區。
表分區選擇和表分區修剪類似,只不過修剪是自動實現的,而表分區選擇是現實的指定分區範圍。