一, 分區概念html
分區容許根據指定的規則,跨文件系統分配單個表的多個部分。表的不一樣部分在不一樣的位置被存儲爲單獨的表。MySQL從5.1.3開始支持Partition。mysql
分區和手動分表對比jquery
手動分表 | 分區 |
多張數據表 | 一張數據表 |
重複數據的風險 | 沒有數據重複的風險 |
寫入多張表 | 寫入一張表 |
沒有統一的約束限制 | 強制的約束限制 |
MySQL支持RANGE,LIST,HASH,KEY分區類型,其中以RANGE最爲經常使用:sql
Range(範圍)–這種模式容許將數據劃分不一樣範圍。例如能夠將一個表經過年份劃分紅若干個分區。 數據庫
Hash(哈希)–這中模式容許經過對錶的一個或多個列的Hash Key進行計算,最後經過這個Hash碼不一樣數值對應的數據區域進行分區。例如能夠創建一個對錶主鍵進行分區的表。 緩存
Key(鍵值)-上面Hash模式的一種延伸,這裏的Hash Key是MySQL系統產生的。 服務器
List(預約義列表)–這種模式容許系統經過預約義的列表的值來對數據進行分割。 ide
Composite(複合模式) –以上模式的組合使用 函數
二,分區能作什麼工具
邏輯數據分割
提升單一的寫和讀應用速度
提升分區範圍讀查詢的速度
分割數據可以有多個不一樣的物理文件路徑
高效的保存歷史數據
一個表上的約束檢查
不一樣的主從服務器分區策略,例如master按Hash分區,slave按range分區
三,分區的限制(截止5.1.44版)
• 只能對數據表的整型列進行分區,或者數據列能夠經過分區函數轉化成整型列
• 最大分區數目不能超過1024
• 若是含有惟一索引或者主鍵,則分區列必須包含在全部的惟一索引或者主鍵在內
• 不支持外鍵
• 不支持全文索引(fulltext)
按日期進行分區很很是適合,由於不少日期函數能夠用。可是對於字符串來講合適的分區函數不太多
四,何時使用分區
• 海量數據表
• 歷史錶快速的查詢,能夠採用ARCHIVE+PARTITION的方式。
• 數據表索引大於服務器有效內存
• 對於大表,特別是索引遠遠大於服務器有效內存時,能夠不用索引,此時分區效率會更有效。
五,分區實驗
實驗一:
使用 US Bureau of Transportation Statistics發佈的數據(CSV格式).目前, 包括 1.13 億條記錄,7.5 GB數據5.2 GB索引。時間從1987到2007。
服務器使用4GB內存,這樣數據和索引的大小都超過了內存大小。設置爲4GB緣由是數據倉庫大小遠遠超過可能內存的大小,可能達幾TB。對普通OLTP數據庫來講,索引緩存在內存中,能夠快速檢索。若是數據超出內存大小,須要使用不一樣的方式。
建立有主鍵的表,由於一般表都會有主鍵。表的主鍵太大致使索引沒法讀入內存,這樣通常來講不是高效的,意味着要常常訪問磁盤,訪問速度徹底取決於你的磁盤和處理器。目前在設計很大的數據倉庫裏,有一種廣泛的作法是不使用索引。因此也會比較有和沒有主鍵的性能。
測試方法:
使用三種數據引擘MyISAM, InnoDB, Archive.
對於每一種引擘, 建立一個帶主鍵的未分區表 (除了archive) 和兩個分區表,一個按月一個按年。分區表分區方式以下:
CREATE TABLE by_year (
d DATE
)
PARTITION BY RANGE (YEAR(d))
(
PARTITION P1 VALUES LESS THAN (2001),
PARTITION P2 VALUES LESS THAN (2002),
PARTITION P3 VALUES LESS THAN (2003),
PARTITION P4 VALUES LESS THAN (MAXVALUE)
)
CREATE TABLE by_month (
d DATE
)
PARTITION BY RANGE (TO_DAYS(d))
(
PARTITION P1 VALUES LESS THAN (to_days(‘2001-02-01′)), — January
PARTITION P2 VALUES LESS THAN (to_days(‘2001-03-01′)), — February
PARTITION P3 VALUES LESS THAN (to_days(‘2001-04-01′)), — March
PARTITION P4 VALUES LESS THAN (MAXVALUE)
)
每個都在 mysql服務器上的單獨的實例上測試, 每實例只有一個庫一個表。每種引擘, 都會啓動服務, 運行查詢並記錄結果, 而後關閉服務。服務實例經過MySQL Sandbox建立。
加載數據的狀況以下:
ID | 引擘 | 是否分區 | 數據 | 大小 | 備註 | 加載時間 (*) |
1 | MyISAM | none | 1.13億 | 13 GB | with PK | 37 min |
2 | MyISAM | by month | 1.13億 | 8 GB | without PK | 19 min |
3 | MyISAM | by year | 1.13億 | 8 GB | without PK | 18 min |
4 | InnoDB | none | 1.13億 | 16 GB | with PK | 63 min |
5 | InnoDB | by month | 1.13億 | 10 GB | without PK | 59 min |
6 | InnoDB | by year | 1.13億 | 10 GB | without PK | 57 min |
7 | Archive | none | 1.13億 | 1.8 GB | no keys | 20 min |
8 | Archive | by month | 1.13億 | 1.8 GB | no keys | 21 min |
9 | Archive | by year | 1.13億 | 1.8 GB | no keys | 20 min |
*在dual-Xeon服務器上
爲了對比分區在大的和小的數據集上的效果,建立了另外9個實例,每個包含略小於2GB的數據。
查詢語句有兩種
彙集查詢
SELECT COUNT(*)
FROM table_name
WHERE date_column BETWEEN start_date and end_date
指定記錄查詢
SELECT column_list
FROM table_name
WHERE column1 = x and column2 = y and column3 = z
對於第一種查詢,建立不一樣的日期範圍的語句。對於每個範圍,建立一組額外的相同範圍日期的查詢。每一個日期範圍的第一個查詢是冷查詢,意味着是第一次命中,隨後的在一樣範圍內的查詢是暖查詢,意味着至少部分被緩存。查詢語句在the Forge上。
結果:
1帶主鍵的分區表
第一個測試使用複合主鍵,就像原始數據表使用的同樣。主鍵索引文件達到5.5 GB. 能夠看出,分區不只沒有提升性能,主鍵還減緩了操做。由於若是使用主鍵索引查詢,而索引又不能讀入內存,則表現不好。提示咱們分區頗有用,可是必須使用得當。
+——–+—————–+—————–+—————–+
| 狀態 | myisam 不分區 | myisam 月分區 | myisam 年分區 |
+——–+—————–+—————–+—————–+
| cold | 2.6574570285714 | 2.9169642 | 3.0373419714286 |
| warm | 2.5720722571429 | 3.1249698285714 | 3.1294000571429 |
+——–+—————–+—————–+—————–+
ARCHIVE引擘
+——–+—————-+—————–+—————–+
| 狀態 | archive不分區 | archive月分區| archive年分區 |
+——–+—————-+—————–+—————–+
| cold | 249.849563 | 1.2436211111111 | 12.632532527778 |
| warm | 235.814442 | 1.0889786388889 | 12.600520777778 |
+——–+—————-+—————–+—————–+
注意ARCHIVE引擘月分區的響應時間比使用MyISAM好。
2不帶主鍵的分區表
由於若是主鍵的大小超出了可用的key buffer,甚至所有內存,全部使用主鍵的查詢都會使用磁盤。新的方式只使用分區,不要主鍵。性能有顯著的提升。
按月分區表獲得了70%-90%的性能提升。
+——–+——————+——————+——————+
| 狀態 | myisam 不分區 | myisam 月分區 | myisam 年分區 |
+——–+——————+——————+——————+
| cold | 2.6864490285714 | 0.64206445714286 | 2.6343286285714 |
| warm | 2.8157905714286 | 0.18774977142857 | 2.2084743714286 |
+——–+——————+——————+——————+
爲了使區別更明顯, 我使用了兩個大規模查詢,能夠利用分區的分區消除功能。
# query 1 – 按年統計
SELECT year(FlightDate) as y, count(*)
FROM flightstats
WHERE FlightDate BETWEEN 「2001-01-01″ and 「2003-12-31″
GROUP BY y
# query 2 – 按月統計
SELECT date_format(FlightDate,」%Y-%m」) as m, count(*)
FROM flightstats
WHERE FlightDate BETWEEN 「2001-01-01″ and 「2003-12-31″
GROUP BY m
結果顯示按月分區表有30%-60%,按年分區表有15%-30%性能提高。
+———-+———–+———–+———–+
| query_id | 不分 | 月分 | 年分 |
+———-+———–+———–+———–+
| 1 | 97.779958 | 36.296519 | 82.327554 |
| 2 | 69.61055 | 47.644986 | 47.60223 |
+———-+———–+———–+———–+
處理器因素
當以上測試在家用機(Intel Dual Core 2.3 MHz CPU)上測試的時候。對於原來的對於dual Xeon 2.66 MHz來講,發現新服務器更快!。
重複上面的測試,使人吃驚:
+——–+——————-+————-+—————–+
|狀態 | myisam 不分區 |myisam 月分區| myisam 年分區 |
+——–+——————-+————-+—————–+
| cold | 0.051063428571429 | 0.6577062 | 1.6663527428571 |
| warm | 0.063645485714286 | 0.1093724 | 1.2369152285714 |
+——–+——————-+————-+—————–+
myisam 不分區帶主鍵的表比分區表更快. 分區表的表現和原來同樣,但未分區表性能提升了,使得分區顯得沒必要要。既然這臺服務器彷佛充分利用了索引的好處,我在分區表的分區列上加入了索引。
# 原始表
create table flightstats (
AirlineID int not null,
UniqueCarrier char(3) not null,
Carrier char(3) not null,
FlightDate date not null,
FlightNum char(5) not null,
TailNum char(8) not null,
ArrDelay double not null,
ArrTime datetime not null,
DepDelay double not null,
DepTime datetime not null,
Origin char(3) not null,
Dest char(3) not null,
Distance int not null,
Cancelled char(1) default ‘n’,
primary key (FlightDate, AirlineID, Carrier, UniqueCarrier, FlightNum, Origin, DepTime, Dest)
)
# 分區表
create table flightstats (
AirlineID int not null,
UniqueCarrier char(3) not null,
Carrier char(3) not null,
FlightDate date not null,
FlightNum char(5) not null,
TailNum char(8) not null,
ArrDelay double not null,
ArrTime datetime not null,
DepDelay double not null,
DepTime datetime not null,
Origin char(3) not null,
Dest char(3) not null,
Distance int not null,
Cancelled char(1) default ‘n’,
KEY (FlightDate)
)
PARTITION BY RANGE …
結果是讓人滿意的,獲得35% 性能提升。
+——–+——————-+——————-+——————-+
|狀態 | myisam 不分區 |myisam 月分區 | myisam 年分區 |
+——–+——————-+——————-+——————-+
| cold | 0.075289714285714 | 0.025491685714286 | 0.072398542857143 |
| warm | 0.064401257142857 | 0.031563085714286 | 0.056638085714286 |
+——–+——————-+——————-+——————-+
結論:
1. 使用表分區並非性能提升的保證。它依賴於如下因素:
分區使用的列the column used for partitioning;
分區函數,若是原始字段不是int型;
服務器速度;
內存數量.
2. 在應用到生產系統前運行基準測試和性能測試
依賴於你的數據庫的用途,你可能獲得巨大的性能提升也可能一無所得。若是不當心,甚至有可能會下降性能。
好比:一個使用月分區的表,在老是進行日期範圍查詢時能夠獲得極優的速度。但若是沒有日期查詢,那麼會進行全表掃描。
分區對於海量數據性能提升是一個關鍵的工具。什麼纔是海量的數據取決於部署的硬件。盲目使用分區不能保證提升性能,可是在前期基準測試和性能測試的幫助下,能夠成爲完美的解決方案。
3. Archive 表能夠成爲一個很好的折衷方案
Archive 表分區後能夠獲得巨大的性能提升。固然也依賴於你的用途,沒有分區時任何查詢都是全表掃描。若是你有不須要變動的歷史數據,還要進行按時間的分析統計,使用Archive引擘是極佳的選擇。它會使用10-20%的原空間,對於彙集查詢有比MyISAM /InnoDB表更好的性能。
雖然一個很好的優化的分區MyISAM 表性能可能好於對應的Archive表, 可是須要10倍的空間。
實驗二:
1.建兩個表,一個按時間字段分區,一個不分區。
CREATE TABLE part_tab
(
c1 int default NULL,
c2 varchar(30) default NULL,
c3 date default NULL
) engine=myisam
PARTITION BY RANGE (year(c3)) (PARTITION p0 VALUES LESS THAN (1995),
PARTITION p1 VALUES LESS THAN (1996) , PARTITION p2 VALUES LESS THAN (1997) ,
PARTITION p3 VALUES LESS THAN (1998) , PARTITION p4 VALUES LESS THAN (1999) ,
PARTITION p5 VALUES LESS THAN (2000) , PARTITION p6 VALUES LESS THAN (2001) ,
PARTITION p7 VALUES LESS THAN (2002) , PARTITION p8 VALUES LESS THAN (2003) ,
PARTITION p9 VALUES LESS THAN (2004) , PARTITION p10 VALUES LESS THAN (2010),
PARTITION p11 VALUES LESS THAN MAXVALUE );
create table no_part_tab
(c1 int(11) default NULL,
c2 varchar(30) default NULL,
c3 date default NULL) engine=myisam;
2.建一個存儲過程, 利用該過程向兩個表插入各8百萬條不一樣數據。
delimiter //
CREATE PROCEDURE load_part_tab()
begin
declare v int default 0;
while v < 8000000
do
insert into part_tab
values (v,’testing partitions’,adddate(‘1995-01-01′,(rand(v)*36520) mod 3652));
set v = v + 1;
end while;
end
//
而後執行
mysql> delimiter ;
mysql> call load_part_tab();
Query OK, 1 row affected (8 min 17.75 sec)
mysql> insert into no_part_tab select * from part_tab;
Query OK, 8000000 rows affected (51.59 sec)
Records: 8000000 Duplicates: 0 Warnings: 0
3.開始對這兩表中的數據進行簡單的範圍查詢吧。並顯示執行過程解析:
mysql> select count(*) from no_part_tab where c3 > date ‘1995-01-01′ and c3 < date ‘1995-12-31′;
+———-+
| count(*) |
+———-+
| 795181 |
+———-+
1 row in set (38.30 sec)
mysql> select count(*) from part_tab where c3 > date ‘1995-01-01′ and c3 < date ‘1995-12-31′;
+———-+
| count(*) |
+———-+
| 795181 |
+———-+
1 row in set (3.88 sec)
mysql> explain select count(*) from no_part_tab where c3 > date ‘1995-01-01′ and c3 < date ‘1995-12-31′\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: no_part_tab
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 8000000
Extra: Using where
1 row in set (0.00 sec)
mysql> explain partitions select count(*) from part_tab where
-> c3 > date ‘1995-01-01′ and c3 < date ‘1995-12-31′\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: part_tab
partitions: p1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 798458
Extra: Using where
1 row in set (0.00 sec)
從上面結果能夠看出,使用表分區比非分區的減小90%的響應時間。命令解析Explain程序能夠看出在對已分區的表的查詢過程當中僅對第一個分區進行了掃描,其他跳過。進一步測試:
– 增長日期範圍
mysql> select count(*) from no_part_tab where c3 > date ‘-01-01′and c3 < date ‘1997-12-31′;
+———-+
| count(*) |
+———-+
| 2396524 |
+———-+
1 row in set (5.42 sec)
mysql> select count(*) from part_tab where c3 > date ‘-01-01′and c3 < date ‘1997-12-31′;
+———-+
| count(*) |
+———-+
| 2396524 |
+———-+
1 row in set (2.63 sec)
– 增長未索引字段查詢
mysql> select count(*) from part_tab where c3 > date ‘-01-01′and c3 < date
‘1996-12-31′ and c2=’hello’;
+———-+
| count(*) |
+———-+
| 0 |
+———-+
1 row in set (0.75 sec)
mysql> select count(*) from no_part_tab where c3 > date ‘-01-01′and c3 < date ‘1996-12-31′ and c2=’hello’;
+———-+
| count(*) |
+———-+
| 0 |
+———-+
1 row in set (11.52 sec)
結論:
分區和未分區佔用文件空間大體相同 (數據和索引文件)
若是查詢語句中有未創建索引字段,分區時間遠遠優於未分區時間
若是查詢語句中字段創建了索引,分區和未分區的差異縮小,分區略優於未分區。
對於大數據量,建議使用分區功能。
去除沒必要要的字段
根據手冊,增長myisam_max_sort_file_size 會增長分區性能