MYSQL分區mysql
分區鍵的引入。算法
查詢是否支持分區sql
mysql> show variables like '%partition%'; Empty set (0.01 sec) mysql> show variables like '%partition%'; +---------------------------------------+-------+ | Variable_name | Value | +---------------------------------------+-------+ | innodb_adaptive_hash_index_partitions | 1 | +---------------------------------------+-------+
MySQL支持建立分區的引擎:MyISam、InnoDB、Memory,不支持分區:MERGE、CSV數據庫
在MySQL5.1中,同一個分區表的全部分區必須使用同一個存儲引擎,可是在同一個MySQL服務器中或者同一個數據庫中、對於不一樣的分區表可使用不一樣的存儲引擎。express
MySQL的分區適用於一個表的全部數據和索引。服務器
設置引擎ENGINE必須在CREATE TABLE語句中的其餘任何部分以前less
mysql> create table emp(empid int,salay decimal(7,2),birth_date date) engine=innodb partition by hash(month(birth_date)) partitions 6; Query OK, 0 rows affected (0.06 sec)
在mysql5.1中:range、list、hash分區鍵必須是int類型,key還可使用blob、text。在mysql5.5中已經支持非整數類型作分區鍵函數
利用取值範圍將數據分紅分區,區間要連續且不能互相重疊。性能
RANGE分區中,分區鍵若是是NULL值會被看成一個最小值來處理。測試
mysql> create table emp_date( id int not null, ename varchar(30), hired date not null default '1970-01-01', separated date not null default '9999-12-31', job varchar(30) not null, store_id int not null ) partition by range (year(separated)) ( partition p0 values less than (1995), partition p1 values less than (2000), partition p2 values less than (2005) ); Query OK, 0 rows affected (0.04 sec)
超出最大分區範圍會報錯,要是有個最大值maxvalue
兜底就行了!你想要的都給你!
mysql> alter table emp_date add partition (partition p3 values less than maxvalue); Query OK, 0 rows affected (0.04 sec) Records: 0 Duplicates: 0 Warnings: 0
前面說了RANGE只支持int作分區鍵,太沒有人性了,現實業務場景那麼多,MySQL5.5起改進了這個問題,新增RANGE COLUMNS 分區支持非整型分區,這樣建立日期分區就不用經過函數畫蛇添足了。no code no bb!
mysql> drop table `emp_date`; Query OK, 0 rows affected (0.04 sec) mysql> create table emp_date( -> id int not null, -> ename varchar(30), -> hired date not null default '1970-01-01', -> separated date not null default '9999-12-31', -> job varchar(30) not null, -> store_id int not null -> ) -> partition by range columns (separated) ( -> partition p0 values less than ('1995-01-01'), -> partition p1 values less than ('2000-01-01'), -> partition p2 values less than ('2005-01-01') -> ); Query OK, 0 rows affected (0.04 sec)
這種操做還不夠經常使用,常常要按天分區怎麼搞?
MySQL5.1:分區日期處理函數只有year()
和to_days()
MySQL5.5:增長了to_seconds()
,把日期轉換成秒。
說了那麼多,RANGE分區功能特別適用哪些狀況?
alter table emp_date drop partition p0
刪除分區。對動輒成千上萬的數據,比運行delete要高效的多!mysql> insert into emp_date (id,ename,hired,separated,job,store_id) values('7934','miller','1995-01-01','1995-01-01','care',50); Query OK, 1 row affected (0.01 sec) mysql> explain partitions select count(1) from emp_date where store_id >=20\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp_date partitions: p0,p1,p2 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1 filtered: 100.00 Extra: Using where 1 row in set, 2 warnings (0.00 sec) ERROR: No query specified
特色:一個枚舉列表的值的集合。RANGE是連續區間值的集合
mysql> CREATE TABLE expenses ( expense_date date NOT NULL, category INT, amount DECIMAL ( 10, 3 ) ) PARTITION BY list ( category ) ( -> PARTITION p0 VALUES IN ( 3, 5 ), -> PARTITION p1 VALUES IN ( 1, 11 ), -> PARTITION p2 VALUES IN ( 4, 9 ), -> PARTITION p3 VALUES IN ( 2 ) -> ); Query OK, 0 rows affected (0.07 sec)
前面有說過,LIST也是僅支持整型,若是你是MySQL5.1,還得單獨建個表。
MYSQL5.5中支持非整型分區,真貼心!
mysql> CREATE TABLE expensess ( -> expense_date date NOT NULL, -> category varchar (30), -> amount DECIMAL ( 10, 3 ) -> ) -> PARTITION BY list columns ( category ) ( -> PARTITION p0 VALUES IN ('loading','food' ), -> PARTITION p1 VALUES IN ( 'ear', 'frist' ), -> PARTITION p2 VALUES IN ( 'hire','horse' ), -> PARTITION p3 VALUES IN ( 'fees' ) -> ); Query OK, 0 rows affected (0.06 sec)
LIST分區,整型是list (expr) ,字符串是list columns (expr)
HASH分區主要用來分散熱點讀,確保數據在預先肯定個數的分區中盡肯能平均分佈。對一個表執行HASH分區時,MYQSL會對分區鍵應用一個散列函數,以此肯定數據應當放在N個分區中的哪一個分區
語法:PARTITION BY HASH(expr) PARTITIONS num
expt:某列值或者一個基於某列值返回一個整數值的表達式
num:非負整數,分幾個區
實例:
CREATE TABLE emp_date ( id INT NOT NULL, ename VARCHAR ( 30 ), hired date NOT NULL DEFAULT '1970-01-01', separated date NOT NULL DEFAULT '9999-12-31', job VARCHAR ( 30 ) NOT NULL, store_id INT NOT NULL ) PARTITION BY HASH ( store_id ) partitions 4;
N=MOD(expr,num)
Store_id = 234;根據公式取模:N=MOD(234,4) = 2;分佈在第二個分區
測試:
insert into emp_date values(1,'care','2010-10-10','9999-12-31','tos',234);
經過執行計劃看看 :
mysql> EXPLAIN PARTITIONS SELECT * FROM emp_date WHERE store_id = 234\G; *************************** 1. row *************************** id: 1 select_type: SIMPLE table: emp_date partitions: p2 type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 1 filtered: 100.00 Extra: Using where 1 row in set, 2 warnings (0.00 sec) ERROR: No query specified
MySQL不推薦使用涉及多列的哈希表達式,expr能夠是非隨機很是數,每次增刪改都須要計算一次,存在性能問題!
實例:
CREATE TABLE emp_dates ( id INT NOT NULL, ename VARCHAR ( 30 ), hired date NOT NULL DEFAULT '1970-01-01', separated date NOT NULL DEFAULT '9999-12-31', job VARCHAR ( 30 ) NOT NULL, store_id INT NOT NULL ) PARTITION BY LINEAR HASH ( store_id ) partitions 4;
記錄將要保存到的分區是num 個分區中的分區N,其中N是根據下面的算法獲得: 找到下一個大於num.的、2的冪,咱們把這個值稱爲V
V = POWER(2, CEILING(LOG(2, num)))
N = F(column_list) & (V – 1)
當 N >= num: · 設置 V = CEIL(V / 2) · 設置 N = N & (V – 1)
是否是跟我同樣懵逼中?實例走一波!
設定4個分區,expr=234 V = 4; N = 234 & (4-1); N = 2; 由於N<= 4;會被分配到第二個分區
優勢:在分區維護(包含增長、刪除、合併、拆分分區)時,MySQL可以處理的更迅速
缺點:相比線性分區,各個分區之間數據的分佈不太均衡
與HASH分區類型。不一樣點:
key分區的語法:partition by keys(expr)
;expr是零個或者多個字段名名的列表
mysql> CREATE TABLE `emp1` ( -> id int not null, -> ename varchar(30), -> hired date not null DEFAULT '1970-01-01', -> separated date not null DEFAULT '9999-12-31', -> job varchar(30) not null, -> store_id int not null -> ) -> -> PARTITION BY key ( job ) partitions 4; Query OK, 0 rows affected (0.04 sec)
試試看不指定分區鍵,前提得有主鍵!
mysql> CREATE TABLE `emp2` ( -> id int not null, -> ename varchar(30), -> hired date not null DEFAULT '1970-01-01', -> separated date not null DEFAULT '9999-12-31', -> job varchar(30) not null, -> store_id int not null, -> primary key (id) -> ) -> -> PARTITION BY key ( ) partitions 4; Query OK, 0 rows affected (0.05 sec)
退一步,沒有主鍵也能夠,可是必需要有惟一鍵,unique key
,同時惟一鍵必須爲非空,你搞個空鬼才知道你要存哪一個分區!
既沒有主鍵又沒有惟一鍵,報錯!
在按照key分區的分區表上,不能執行alter table drop primary key
與HASH分區相似,可使用關鍵字LINEAR KEY分區時,分區的編號是經過2的冥算法獲得而不是取模。在處理大量數據時,可以有效的分散熱點!
子分區是分區表中對每一個分區的再次分割。也稱爲複合分區。
MySQL5.1開始支持對已經經過RANGE或者LIST分區了的表再進行子分區。子分區既可使用HASH分區,也可使用KEY分區。
mysql> CREATE TABLE `ts` ( -> id int, -> purchased date -> ) -> -> PARTITION by range (year(purchased)) -> SUBPARTITION by hash (TO_DAYS(purchased)) -> SUBPARTITIONS 2 -> ( -> PARTITION p0 VALUES LESS THAN (1900 ), -> PARTITION p1 VALUES LESS THAN (2000 ), -> PARTITION p2 VALUES LESS THAN MAXVALUE -> ); Query OK, 0 rows affected (0.06 sec)
原先有三個分區,p0、p一、p2
每一個分區又再被分爲兩個子分區,一共6個分區
子分區適合數據量很是大量的數據記錄
MySQL不由止在分區鍵上使用null值。
具體分區類型的null值
MySQL5.1提供了添加、刪除、重定義、合併、拆分分區的命令。均可以經過ALTER TABLE 來實現。
在添加、刪除、從新定義分區的處理上,RANGE和LIST分區很類似。
刪除分區:ALTER TABLE DROP PARTITION
刪除分區同時也會刪除該分區對應的數據
//新建分區 create table emp_date( id int not null, ename varchar(30), hired date not null default '1970-01-01', separated date not null default '9999-12-31', job varchar(30) not null, store_id int not null ) partition by range (YEAR(separated)) ( partition p0 values less than (1995), partition p1 values less than (2000), partition p2 values less than (2005), partition p3 values less than (2015), partition p4 values less than (2020) ); //插入數據 INSERT INTO `sakila`.`emp_date`(`id`, `ename`, `hired`, `separated`, `job`, `store_id`) VALUES (1, 'care', '1970-01-01', '1991-12-31', 'a', 1); INSERT INTO `sakila`.`emp_date`(`id`, `ename`, `hired`, `separated`, `job`, `store_id`) VALUES (2, 'tony', '1970-01-01', '1996-12-31', 'b', 2); INSERT INTO `sakila`.`emp_date`(`id`, `ename`, `hired`, `separated`, `job`, `store_id`) VALUES (3, 'pony', '1970-01-01', '2001-12-31', 'c', 3); INSERT INTO `sakila`.`emp_date`(`id`, `ename`, `hired`, `separated`, `job`, `store_id`) VALUES (4, 'foly', '1970-01-01', '2006-12-31', 'd', 4); INSERT INTO `sakila`.`emp_date`(`id`, `ename`, `hired`, `separated`, `job`, `store_id`) VALUES (5, 'quly', '1970-01-01', '2016-12-31', 'e', 5); //刪除p2分區 ALTER TABLE emp_date DROP PARTITION p2; //查看建表語句 mysql> SHOW CREATE TABLE emp_date\G; *************************** 1. row *************************** Table: emp_date Create Table: CREATE TABLE `emp_date` ( `id` int(11) NOT NULL, `ename` varchar(30) DEFAULT NULL, `hired` date NOT NULL DEFAULT '1970-01-01', `separated` date NOT NULL DEFAULT '9999-12-31', `job` varchar(30) NOT NULL, `store_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50100 PARTITION BY RANGE (year(separated)) (PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB, PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN (2015) ENGINE = InnoDB, PARTITION p4 VALUES LESS THAN (2020) ENGINE = InnoDB) */ 1 row in set (0.00 sec) ERROR: No query specified
查詢表的分區對應的狀況
SELECT partition_name part, partition_expression expr, partition_description descr, table_rows FROM information_schema.PARTITIONS WHERE table_schema = SCHEMA() AND table_name = 'emp_date'; +------+-----------------+-------+------------+ | part | expr | descr | table_rows | +------+-----------------+-------+------------+ | p0 | year(separated) | 1995 | 1 | | p1 | year(separated) | 2000 | 1 | | p3 | year(separated) | 2015 | 1 | | p4 | year(separated) | 2020 | 1 | +------+-----------------+-------+------------+
LIST分區由於是枚舉型,刪除分區以後若是不新建對應分區,直接插入原先被刪除的數據,報錯!
增長:`ALTER TABLE ADD PARTITION
剛纔不是刪了一個p2分區嗎?好!如今加上去
ALTER TABLE emp_date ADD PARTITION (PARTITION p2 VALUES less than (2005)); 或者 ALTER TABLE emp_date ADD PARTITION (PARTITION p5 VALUES less than (2005)); //報錯 1493 - VALUES LESS THAN value must be strictly increasing for each partition, Time: 0.001000s 只能從最末端添加 ALTER TABLE emp_date ADD PARTITION (PARTITION p2 VALUES less than (2025)); 這樣纔是正確的!
若是是想恢復此區間的分區,怎麼弄呢?
好比刪除:
ALTER TABLE emp_date DROP PARTITION p2;
此分區對應的範圍是:less than (2005)
如今要恢復這個範圍的分區怎麼辦?
實例:LIST增長分區
CREATE TABLE expenses ( expense_date date NOT NULL, category INT, amount DECIMAL ( 10, 3 ) ) PARTITION BY list ( category ) ( PARTITION p0 VALUES IN ( 3, 5 ), PARTITION p1 VALUES IN ( 1, 11), PARTITION p2 VALUES IN ( 4, 9), PARTITION p3 VALUES IN ( 2 ) ); 增長分區 ALTER TABLE expenses ADD PARTITION (PARTITION p4 values in (6,7,8)); LIST必需要注意的問題是枚舉值必須惟一 ALTER TABLE expenses ADD PARTITION (PARTITION p5 values in (8)); (報錯) 1495 - Multiple definition of same constant in list partitioning, Time: 0.000000s
剛開始定義分區發現分的很差,好比RANGE分區p4的範圍(2000~2015),後來這個區的數據太大了,須要從新分區,分紅p3(2000~2010),p4(2010~2020),前提以前沒有p2,p3分區!
//原有分區 create table emp_date( id int not null, ename varchar(30), hired date not null default '1970-01-01', separated date not null default '9999-12-31', job varchar(30) not null, store_id int not null ) partition by range (YEAR(separated)) ( partition p0 values less than (1995), partition p1 values less than (2000), partition p4 values less than (2020) ); //過一段時間發現臥槽,都集中在p4分區了,那怎麼行,趕忙重定義分區 ALTER TABLE emp_date REORGANIZE PARTITION p4 INTO( PARTITION p3 VALUES less than (2010), PARTITION p4 VALUES less than (2020) )
//合併以前的分區狀況 CREATE TABLE `emp_date` ( `id` int(11) NOT NULL, `ename` varchar(30) DEFAULT NULL, `hired` date NOT NULL DEFAULT '1970-01-01', `separated` date NOT NULL DEFAULT '9999-12-31', `job` varchar(30) NOT NULL, `store_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50100 PARTITION BY RANGE (YEAR(separated)) (PARTITION p0 VALUES LESS THAN (1995) ENGINE = InnoDB, PARTITION p1 VALUES LESS THAN (2000) ENGINE = InnoDB, PARTITION p3 VALUES LESS THAN (2010) ENGINE = InnoDB, PARTITION p4 VALUES LESS THAN (2020) ENGINE = InnoDB) */; //操做合併, p0\p1\p3合併成一個區p1 ALTER TABLE emp_date REORGANIZE PARTITION p0,p1,p3 INTO( PARTITION p1 VALUES less than (2010) )
CREATE TABLE expenses ( expense_date date NOT NULL, category INT, amount DECIMAL ( 10, 3 ) ) PARTITION BY list ( category ) ( PARTITION p0 VALUES IN ( 3, 5 ), PARTITION p1 VALUES IN ( 1, 11 ), PARTITION p2 VALUES IN ( 4, 9 ), PARTITION p3 VALUES IN ( 2 ), PARTITION p4 VALUES IN ( 6), PARTITION p5 VALUES IN ( 7,8 ) );
目標:將P4分區包含值(6,12)
方案一:和以前同樣新增分區 ALTER TABLE expenses ADD PARTITION (PARTITION p6 VALUES IN ( 6,12 )); //報錯 1495 - Multiple definition of same constant in list partitioning, Time: 0.000000s 方案二:先增長分區,後重定義分區 ALTER TABLE expenses ADD PARTITION (PARTITION p6 VALUES IN ( 12)); //p4,p5,p6重定義到 p4 (6,12) p5(7,8) ALTER TABLE expenses REORGANIZE PARTITION p4,p5,p6 INTO( PARTITION p4 VALUES IN (6,12), PARTITION p5 VALUES IN (7,8) )
重定義分區的時候:
HASH&KEY分區管理相似,以HASH舉例
CREATE TABLE emp( id INT NOT NULL, ename VARCHAR ( 30 ), hired date NOT NULL DEFAULT '1970-01-01', separated date NOT NULL DEFAULT '9999-12-31', job VARCHAR ( 30 ) NOT NULL, store_id INT NOT NULL ) PARTITION BY HASH ( store_id ) partitions 4; //減小分區 ALTER TABLE emp COALESCE PARTITION 2; //增長分區 coalesce不能用來增長分區的數量 ALTER TABLE emp COALESCE PARTITION 8; 報錯 1508 - Cannot remove all partitions, use DROP TABLE instead, Time: 0.003000s //注意是增長8個分區 ALTER TABLE emp add PARTITION PARTITIONS 8; mysql> SHOW CREATE TABLE emp \G; *************************** 1. row *************************** Table: emp Create Table: CREATE TABLE `emp` ( `id` int(11) NOT NULL, `ename` varchar(30) DEFAULT NULL, `hired` date NOT NULL DEFAULT '1970-01-01', `separated` date NOT NULL DEFAULT '9999-12-31', `job` varchar(30) NOT NULL, `store_id` int(11) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50100 PARTITION BY HASH ( store_id) PARTITIONS 10 */ 1 row in set (0.00 sec) ERROR: No query specified