原文地址:http://blog.csdn.net/feihong247/article/details/7885199
一、 mysql分區簡介
數據庫分區
數據庫分區是一種物理數據庫設計技術。雖然分區技術可以實現很多效果,但其主要目的是為了在特定的SQL操作中減少數據讀寫的總量以縮減sql語句的響應時間,同時對於應用來說分區完全是透明的。
MySQL的分區主要有兩種形式:水平分區和垂直分區
水平分區(HorizontalPartitioning)
這種形式的分區是對根據表的行進行分區,通過這樣的方式不同分組里面的物理列分割的數據集得以組合,從而進行個體分割(單分區)或集體分割(1個或多個分區)。
所有在表中定義的列在每個數據集中都能找到,所以表的特性依然得以保持。水平分區一定要通過某個屬性列來分割。常見的比如年份,日期等。
垂直分區(VerticalPartitioning)
這種分區方式一般來說是通過對表的垂直划分來減少目標表的寬度,使某些特定的列被划分到特定的分區,每個分區都包含了其中的列所對應所有行。
可以用 showvariables like '%partition%';
命令查詢當前的mysql數據庫版本是否支持分區。
分區的作用:數據庫性能的提升和簡化數據管理
在掃描操作中,mysql優化器只掃描保護數據的那個分區以減少掃描范圍獲得性能的提高。
分區技術使得數據管理變得簡單,刪除某個分區不會對另外的分區造成影響,分區有系統直接管理不用手工干預。
mysql從5.1版本開始支持分區。每個分區的名稱是不區分大小寫。同個表中的分區表名稱要唯一。
二、 mysql分區類型
根據所使用的不同分區規則可以分成幾大分區類型。
RANGE 分區:
基於屬於一個給定連續區間的列值,把多行分配給分區。
LIST 分區:
類似於按RANGE分區,區別在於LIST分區是基於列值匹配一個離散值集合中的某個值來進行選擇。
HASH分區:
基於用戶定義的表達式的返回值來進行選擇的分區,該表達式使用將要插入到表中的這些行的列值進行計算。這個函數可以包含MySQL中有效的、產生非負整數值的任何表達式。
KEY
分區:類似於按HASH分區,區別在於KEY分區只支持計算一列或多列,且MySQL服務器提供其自身的哈希函數。必須有一列或多列包含整數值。
復合分區:
基於RANGE/LIST 類型的分區表中每個分區的再次分割。子分區可以是 HASH/KEY 等類型。
三、 mysql分區表常用操作示例
以部門員工表為例子:
1) 創建range分區
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date,
salary int
)
partition by range(salary)
(
partition p1 values less than (1000),
partition p2 values less than (2000),
partition p3 values less than maxvalue
);
以員工工資為依據做范圍分區。
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by range(year(birthdate))
(
partition p1 values less than (1980),
partition p2 values less than (1990),
partition p3 values less than maxvalue
);
以year(birthdate)表達式(計算員工的出生日期)作為范圍分區依據。這里最值得注意的是表達式必須有返回值。
2) 創建list分區
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by list(deptno)
(
partition p1 values in (10),
partition p2 values in (20),
partition p3 values in (30)
);
以部門作為分區依據,每個部門做一分區。
3) 創建hash分區
HASH分區主要用來確保數據在預先確定數目的分區中平均分布。在RANGE和LIST分區中,必須明確指定一個給定的列值或列值集合應該保存在哪 個分區中;而在HASH分區中,MySQL 自動完成這些工作,你所要做的只是基於將要被哈希的列值指定一個列值或表達式,以及指定被分區的表將要被分割成的分區數量。
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by hash(year(birthdate))
partitions 4;
4) 創建key分區
按照KEY進行分區類似於按照HASH分區,除了HASH分區使用的用戶定義的表達式,而KEY分區的哈希函數是由MySQL 服務器提供,服務器使用其自己內部的哈希函數,這些函數是基於與PASSWORD()一樣的運算法則。“CREATE TABLE ...PARTITION BY KEY”的語法規則類似於創建一個通過HASH分區的表的規則。它們唯一的區別在於使用的關鍵字是KEY而不是HASH,並且KEY分區只采用一個或多個 列名的一個列表。
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by key(birthdate)
partitions 4;
5) 創建復合分區
range - hash(范圍哈希)復合分區
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by range(salary)
subpartition by hash(year(birthdate))
subpartitions 3
(
partition p1 values less than (2000),
partition p2 values less than maxvalue
);
range- key復合分區
create table emp
(empno varchar(20) not null ,
empname varchar(20),
deptno int,
birthdate date not null,
salary int
)
partition by range(salary)
subpartition by key(birthdate)
subpartitions 3
(
partition p1 values less than (2000),
partition p2 values less than maxvalue
);
list - hash復合分區
CREATE TABLE emp (
empno varchar(20) NOT NULL,
empname varchar(20) ,
deptno int,
birthdate date NOT NULL,
salary int
)
PARTITION BY list (deptno)
subpartition by hash(year(birthdate))
subpartitions 3
(
PARTITION p1 VALUES in (10),
PARTITION p2 VALUES in (20)
)
;
list - key 復合分區
CREATE TABLE empk (
empno varchar(20) NOT NULL,
empname varchar(20) ,
deptno int,
birthdate date NOT NULL,
salary int
)
PARTITION BY list (deptno)
subpartition by key(birthdate)
subpartitions 3
(
PARTITION p1 VALUES in (10),
PARTITION p2 VALUES in (20)
)
;
6) 分區表的管理操作
刪除分區:
alter table emp drop partition p1;
不可以刪除hash或者key分區。
一次性刪除多個分區,alter table emp drop partition p1,p2;
增加分區:
alter table emp add partition (partition p3 values less than (4000));
alter table empl add partition (partition p3 values in (40));
分解分區:
Reorganizepartition關鍵字可以對表的部分分區或全部分區進行修改,並且不會丟失數據。分解前后分區的整體范圍應該一致。
alter table te
reorganize partition p1 into
(
partition p1 values less than (100),
partition p3 values less than (1000)
); ----不會丟失數據
合並分區:
Merge分區:把2個分區合並為一個。
alter table te
reorganize partition p1,p3 into
(partition p1 values less than (1000));
----不會丟失數據
重新定義hash分區表:
Alter table emp partition by hash(salary)partitions 7;
----不會丟失數據
重新定義range分區表:
Alter table emp partitionbyrange(salary)
(
partition p1 values less than (2000),
partition p2 values less than (4000)
); ----不會丟失數據
刪除表的所有分區:
Alter table emp removepartitioning;--不會丟失數據
重建分區:
這和先刪除保存在分區中的所有記錄,然后重新插入它們,具有同樣的效果。它可用於整理分區碎片。
ALTER TABLE emp rebuild partitionp1,p2;
優化分區:
如果從分區中刪除了大量的行,或者對一個帶有可變長度的行(也就是說,有VARCHAR,BLOB,或TEXT類型的列)作了許多修改,可以使用“ALTER TABLE ... OPTIMIZE PARTITION”來收回沒有使用的空間,並整理分區數據文件的碎片。
ALTER TABLE emp optimize partition p1,p2;
分析分區:
讀取並保存分區的鍵分布。
ALTER TABLE emp analyze partition p1,p2;
修補分區:
修補被破壞的分區。
ALTER TABLE emp repairpartition p1,p2;
檢查分區:
可以使用幾乎與對非分區表使用CHECK TABLE 相同的方式檢查分區。
ALTER TABLE emp CHECK partition p1,p2;
這個命令可以告訴你表emp的分區p1,p2中的數據或索引是否已經被破壞。如果發生了這種情況,使用“ALTER TABLE ... REPAIR PARTITION”來修補該分區。
【mysql分區表的局限性】
1. 在5.1版本中分區表對唯一約束有明確的規定,每一個唯一約束必須包含在分區表的分區鍵(也包括主鍵約束)。
CREATE TABLE emptt (
empno varchar(20) NOT NULL ,
empname varchar(20),
deptno int,
birthdate date NOT NULL,
salary int ,
primary key (empno)
)
PARTITION BY range (salary)
(
PARTITION p1 VALUES less than (100),
PARTITION p2 VALUES less than (200)
);
這樣的語句會報錯。MySQL Database Error: A PRIMARY KEY must include allcolumns in the table's partitioning function;
CREATE TABLE emptt (
empno varchar(20) NOT NULL ,
empname varchar(20) ,
deptno int(11),
birthdate date NOT NULL,
salary int(11) ,
primary key (empno,salary)
)
PARTITION BY range (salary)
(
PARTITION p1 VALUES less than (100),
PARTITION p2 VALUES less than (200)
);
在主鍵中加入salary列就正常。
2. MySQL分區處理NULL值的方式
如果分區鍵所在列沒有notnull約束。
如果是range分區表,那么null行將被保存在范圍最小的分區。
如果是list分區表,那么null行將被保存到list為0的分區。
在按HASH和KEY分區的情況下,任何產生NULL值的表達式mysql都視同它的返回值為0。
為了避免這種情況的產生,建議分區鍵設置成NOT NULL。
3. 分區鍵必須是INT類型,或者通過表達式返回INT類型,可以為NULL。唯一的例外是當分
區類型為KEY分區的時候,可以使用其他類型的列作為分區鍵( BLOB or TEXT 列除外)。
4. 對分區表的分區鍵創建索引,那么這個索引也將被分區,分區鍵沒有全局索引一說。
5. 只有RANG和LIST分區能進行子分區,HASH和KEY分區不能進行子分區。
6. 臨時表不能被分區。
四、 獲取mysql分區表信息的幾種方法
1. show create table 表名
可以查看創建分區表的create語句
2. show table status
可以查看表是不是分區表
3. 查看information_schema.partitions表
select
partition_name part,
partition_expression expr,
partition_description descr,
table_rows
from information_schema.partitions where
table_schema = schema()
and table_name='test';
可以查看表具有哪幾個分區、分區的方法、分區中數據的記錄數等信息
4. explain partitions select語句
通過此語句來顯示掃描哪些分區,及他們是如何使用的.
五、 分區表性能比較
1. 創建兩張表: part_tab(分區表),no_part_tab(普通表)
CREATE TABLEpart_tab
( c1 int defaultNULL, c2 varchar2(30) default NULL, c3 date not null)
PARTITION BYRANGE(year(c3))
(PARTITION p0VALUES LESS THAN (1995),
PARTITION p1 VALUESLESS THAN (1996) ,
PARTITION p2 VALUESLESS THAN (1997) ,
PARTITION p3 VALUESLESS THAN (1998) ,
PARTITION p4 VALUES LESS THAN (1999) ,
PARTITION p5 VALUESLESS THAN (2000) ,
PARTITION p6 VALUESLESS THAN (2001) ,
PARTITION p7 VALUESLESS THAN (2002) ,
PARTITION p8 VALUESLESS THAN (2003) ,
PARTITION p9 VALUESLESS THAN (2004) ,
PARTITION p10VALUES LESS THAN (2010),
PARTITION p11VALUES LESS THAN (MAXVALUE) );
CREATE TABLE no_part_tab
( c1 int defaultNULL, c2 varchar2(30) default NULL, c3 date not null);
2. 用存儲過程插入800萬條數據
CREATE PROCEDUREload_part_tab()
begin
declare v int default 0;
while v < 8000000
do
insert into part_tab
values (v,'testingpartitions',adddate('1995-01-01',(rand(v)*36520)mod 3652));
set v = v + 1;
end while;
end;
insert into no_part_tab select * frompart_tab;
3. 測試sql性能
查詢分區表:
selectcount(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (2.62 sec)
查詢普通表:
selectcount(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';
+----------+
| count(*) |
+----------+
| 795181 |
+----------+
1 row in set (7.33 sec)
分區表的執行時間比普通表少70%。
4. 通過explain語句來分析執行情況
mysql>explain select count(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
| id |select_type | table | type |possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | part_tab | ALL | NULL | NULL | NULL | NULL | 7980796 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+
1 rowin set
mysql>explain select count(*) from no_part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';
+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
| id |select_type | table | type |possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | no_part_tab | ALL | NULL | NULL | NULL | NULL | 8000206 | Using where |
+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+
1 rowin set
mysql >
分區表執行掃描了7980796行,而普通表則掃描了8000206行。