MySQL分區

MYSQL分區mysql

分區的優勢

  1. 存儲更多數據
  2. 優化查詢,只掃描必要的一個或者多個分區,針對count()和sum()只要對分區統計再彙總
  3. 對於過時或不須要保存的數據,操做分區更快
  4. 跨多個磁盤來分散數據查詢,以得到更大的查詢吞吐量

分區概述

分區鍵的引入。算法

查詢是否支持分區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)

分區類型

  1. RANGE 分區:基於屬於一個給定連續區間的列值,把多行分配給分區。
  2. LIST 分區:相似於RANGE分區,區別在於LIST分區是基於枚舉出的值列表分區,RANGE是基於給定的連續區間範圍分區
  3. HASH分區:基於給定的分區個數,把數據分配到不一樣的分區
  4. KEY分區:相似於RANGE分區

在mysql5.1中:range、list、hash分區鍵必須是int類型,key還可使用blob、text。在mysql5.5中已經支持非整數類型作分區鍵函數

分區時注意

  1. 要麼分區表上沒有主鍵/惟一鍵,要麼分區表主鍵/惟一鍵必須包含分區鍵。(不然會報錯)
  2. 分區的名字不區分大小寫

RANGE分區

利用取值範圍將數據分紅分區,區間要連續且不能互相重疊。性能

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分區功能特別適用哪些狀況?

  1. 當須要刪除過時的數據,好比某個分區的數據已經徹底沒有意義了,請執行alter table emp_date drop partition p0刪除分區。對動輒成千上萬的數據,比運行delete要高效的多!
  2. 常常運行包含分區鍵的查詢,MySQL很快能找到對應的分區,而且在對應的分區掃描。
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

LIST分區

特色:一個枚舉列表的值的集合。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分區主要用來分散熱點讀,確保數據在預先肯定個數的分區中盡肯能平均分佈。對一個表執行HASH分區時,MYQSL會對分區鍵應用一個散列函數,以此肯定數據應當放在N個分區中的哪一個分區

一、HASH分區分兩種

  1. 常規分區(HASH分區)—>取模算法
  2. 線性分區(LINEAR HASH分區)——>一個線性的2的冥的運算法則

二、常規分區

​ 語法: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;
根據expr算分區:

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能夠是非隨機很是數,每次增刪改都須要計算一次,存在性能問題!

優勢:數據平均的分佈在每一個分區、提升了效率
缺點:增長或合併分區,原來平均的數據須要從新經過取模再分配,不適合須要靈活變更分區的需求

三、線性HASH分區

實例:

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;會被分配到第二個分區
線性HASH分區優缺點

優勢:在分區維護(包含增長、刪除、合併、拆分分區)時,MySQL可以處理的更迅速

缺點:相比線性分區,各個分區之間數據的分佈不太均衡

KEY分區

與HASH分區類型。不一樣點:

  1. HASH分區容許使用用戶自定義的表達式,KEY分區不容許使用用戶自定義的表達式,須要使用HASH函數
  2. HASH分區只支持整數分區,KEY分區支持使用除BLOB 、TEXT類型外的其餘類型做爲分區鍵
  3. 建立KEY分區能夠不指定分區鍵,默認使用主鍵

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值的方式

MySQL不由止在分區鍵上使用null值。

具體分區類型的null值

  1. RANGE分區,null會被當成最小值
  2. LIST分區,null值必須出如今枚舉列表中,不然不會被接受(報錯)
  3. HASH/KEY分區,null值當成0;

分區管理

MySQL5.1提供了添加、刪除、重定義、合併、拆分分區的命令。均可以經過ALTER TABLE 來實現。

一、RANGE&LIST分區管理

在添加、刪除、從新定義分區的處理上,RANGE和LIST分區很類似。

1-一、刪除分區

刪除分區: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分區由於是枚舉型,刪除分區以後若是不新建對應分區,直接插入原先被刪除的數據,報錯!

1-二、增長分區

增長:`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
1-三、重定義分區

剛開始定義分區發現分的很差,好比RANGE分區p4的範圍(2000~2015),後來這個區的數據太大了,須要從新分區,分紅p3(2000~2010),p4(2010~2020),前提以前沒有p2,p3分區!

RANGE拆分分區
//原有分區
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)
)
RANGE合併分區
//合併以前的分區狀況
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)
)
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 ),
        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)
)
總結:

重定義分區的時候:

  1. 不能經過重定義分區改表原有分區類型
  2. RANGE和LIST只能從新定義相鄰的分區、從新定義的區間必須和原分區區間覆蓋相同的區間

二、HASH&KEY分區管理

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
相關文章
相關標籤/搜索