分區的作用
分區是將一個表的數據按照某種方式,比如按照時間上的月份,分成多個較小的,更容易管理的部分,但是邏輯上仍是一個表。
個人理解起來,分區跟性能沒有必然關系,分區更多的是從管理的角度出發的。
MySQL分區表對分區字段的限制
分區的字段,必須是表上所有的唯一索引(或者主鍵索引)包含的字段的子集
換句話說就是:(所有的)字段必須出現在(所有的)唯一索引或者主鍵索引的字段中,
或者更通俗講就是,一個表上有一個或者多個唯一索引的情況下,分區的字段必須被包含在所有的主鍵或者唯一索引字段中。
關於這個限制,筆者是根據官方文檔中的示例,理解了好久,以下參考官方的示例。
示例1(如下語句報錯,無法創建分區表):
CREATE TABLE t1 ( col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL, UNIQUE KEY (col1, col2) ) PARTITION BY HASH(col3) PARTITIONS 4;
分區字段是col3, 主鍵是(col1, col2),col3沒有出現在主鍵字段中,因此不滿足“分區的字段,必須是唯一索引字段的子集”,無法創建分區表
如果要想按照col3分區,可以把col3加入到unique key中。
示例2(如下語句報錯,無法創建分區表):
CREATE TABLE t2 ( col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL, PRIMARY KEY (col1), UNIQUE KEY (col3) ) PARTITION BY HASH(col1 + col3) PARTITIONS 4;
分區字段是col1 + col3, 兩個unique key分別是col1和col3,分區字段沒有出現在任何一個unique(primary) key中,因此無法按照(col1 + col3)分區
如果要想按照ccol1 + col3,分區,創建一個col1 + col3的unique key(唯一索引),如果是col1+col3的唯一索引,那只能有一個了,不能兩個unique key的字段完全一致。
因此分區字段是 “所有的(如果有多個)” “索引唯一索引(或者主鍵索引)”的中字段的子集(或者全集)。
示例3(如下語句報錯,無法創建分區表):
如下情況下,無法為t4表分區,因為有兩個唯一索引,且唯一索引的字段沒有交集,
那么任何情況下,都不符合:分區字段是 “所有的(如果有多個)” “索引唯一索引(或者主鍵索引)”的子集(或者全集)
CREATE TABLE t4 ( col1 INT NOT NULL, col2 INT NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL, UNIQUE KEY (col1, col3), UNIQUE KEY (col2, col4)、 );
MySQL是局部分區,意思是一個分區中,包含分區的數據和其對應的索引,而不是索引是一個索引統一存放在一個地方,僅分區數據這種方式。
想一下,為什么MySQL的分區表會有這個么一個奇怪的要求:一個表上有一個或者多個唯一索引的情況下,分區的字段必須被包含在所有的主鍵或者唯一索引字段中?
分區類型
range分區,分區字段必須是整型或者轉換為整型
按照字段的區間划分數據的歸屬,典型的就是按照時間維度的月份分區
CREATE TABLE test_range_partition( id INT auto_increment, createdate DATETIME, primary key (id,createdate) ) PARTITION BY RANGE (TO_DAYS(createdate) ) ( PARTITION p201801 VALUES LESS THAN ( TO_DAYS('20180201') ), PARTITION p201802 VALUES LESS THAN ( TO_DAYS('20180301') ), PARTITION p201803 VALUES LESS THAN ( TO_DAYS('20180401') ), PARTITION p201804 VALUES LESS THAN ( TO_DAYS('20180501') ), PARTITION p201805 VALUES LESS THAN ( TO_DAYS('20180601') ), PARTITION p201806 VALUES LESS THAN ( TO_DAYS('20180701') ), PARTITION p201807 VALUES LESS THAN ( TO_DAYS('20180801') ), PARTITION p201808 VALUES LESS THAN ( TO_DAYS('20180901') ), PARTITION p201809 VALUES LESS THAN ( TO_DAYS('20181001') ), PARTITION p201810 VALUES LESS THAN ( TO_DAYS('20181101') ), PARTITION p201811 VALUES LESS THAN ( TO_DAYS('20181201') ), PARTITION p201812 VALUES LESS THAN ( TO_DAYS('20190101') ) ); insert into test_range_partition (createdate) values ('20180105'); insert into test_range_partition (createdate) values ('20180205'); insert into test_range_partition (createdate) values ('20180206'); insert into test_range_partition (createdate) values ('20180305'); insert into test_range_partition (createdate) values ('20180405'); insert into test_range_partition (createdate) values ('20180505'); insert into test_range_partition (createdate) values ('20180605'); insert into test_range_partition (createdate) values ('20180705'); insert into test_range_partition (createdate) values ('20180805'); insert into test_range_partition (createdate) values ('20180905'); insert into test_range_partition (createdate) values ('20181005'); insert into test_range_partition (createdate) values ('20181105'); select table_schema, table_name, partition_name, partition_ordinal_position, partition_method, partition_expression, table_rows from information_schema.`PARTITIONS` where table_schema = 'db01' and table_name = 'test_range_partition';
對應的物理文件
查看每個分區的信息
分區在查詢中的優化體現
並不是說一個表只要分區了,對於任何查詢都會實現查詢優化,只有查詢條件的數據分布在某一個分區的時候,查詢引擎只會去某一個分區查詢,而不是遍歷整個表
在管理層面,如果需要刪除某一個分區的數據,只需要刪除對應的分區即可
增加與刪除分區
ALTER TABLE test ADD PARTITION (PARTITION p201902 VALUES LESS THAN ( TO_DAYS('20190301') )); ALTER TABLE test DROP PARTITION p20180201;
對於range分區,分區字段必須是整型或者轉換為整型,如果分區字段是日期類型的字段,那么就必須將日期類型的字段轉換成整型類型
對於日期類型的轉換,優化器只支持year(),to_days,to_seconds,unix_timestamp()函數的轉換,其他的並不支持,
也就是說,在按日期字段分區的時候,如果不是使用上述幾個函數轉換的,查詢優化器將無法對相關查詢進行優化。
List分區,分區字段必須是整型或者轉換為整型
按照某個字段上的規則,不同的數據離散地分布在不同的區中。
create table test_list_partiotion ( id int auto_increment, data_type tinyint, primary key(id,data_type) )partition by list(data_type) ( partition p0 values in (0,1,2,3,4,5,6), partition p1 values in (7,8,9,10,11,12), partition p2 values in (13,14,15,16,17) );
對於List分區,分區字段必須是已知的,如果插入的字段不在分區時枚舉值中,將無法插入
Hash分區,分區字段必須是整型或者轉換為整型
Hash分區可以將數據均勻地分不到預先定義的分區中,使得各個分區的數據量分布基本上一致。同樣,分區字段必須是整型或者轉換為整型
drop table test_hash_partiotion; create table test_hash_partiotion ( id int auto_increment, create_date datetime, primary key(id,create_date) )partition by hash(year(create_date)) partitions 10;
一個很明顯的問題就是,如果分區字段本身的分布不勻均,那么hash分區之后存儲的分區也是不均勻的,hash分區時對於hash的字段,需要慎重。
對於單個值的查詢hash分區可以定位到某一個分區
hash分區在查詢優化方面,無法優化范圍查詢,因為無法確定一個某個字段經過hash計算之后究竟分布了在哪個分區之中。
Key分區,分區字段必須是整型或者轉換為整型
與hash分區不用的是,key分區使用MySQL自定義的庫函數進行分區,不需要hash分區那樣對字段整型進行轉換,同樣,分區字段必須是整型或者轉換為整型
create table test_key_partiotion ( id int auto_increment, create_date datetime, primary key(id,create_date) )partition by key(create_date) partitions 10;
對於查詢優化,Key分區的特點與Hash分區一致,對於單個字段可以
column 分區
解決了分區字段必須是整型或者必須轉換為整型的限制,可以對整型,date或者datetime進行支持。
create table test_column_partiotion ( id int auto_increment, data_type datetime, primary key(id,data_type) )partition by range columns(data_type) ( partition p0 values less than ('20180101'), partition p1 values less than ('20180201'), partition p2 values less than ('20180301'), partition p3 values less than ('20180401'), partition p4 values less than ('20180501'), partition p5 values less than ('20180601'), partition p6 values less than ('20180701'), partition p7 values less than ('20180801') );