經過分區(Partition)提高MySQL性能php
——MySQL5.1新特性翻譯系列html
幾年前,俺寫過一篇題爲「The Foundation of Excellent Performance」的文章(如今仍然能夠在http://www.tdan.com/i016fe03.htm看到),俺對SQL語句是影響數據庫驅動系統性能的第一要素的觀點有點質疑。其實在那時我在文章中就堅信數據庫的物理設計在對高級數據庫的性能影響上遠比其餘因素重要。同時俺還給你們看了Oracle的研究,他們解釋了爲何拙劣的物理設計是數據庫停機(不管是有計劃的仍是沒計劃的)背後的主要緣由。這麼多年都過來啦(幸虧沒多少人朝俺扔磚頭),俺的觀點是改變了一些,但在這點上俺仍是堅持DBA若是想要高性能的數據庫就必須在數據庫的物理設計上多思考的觀點,這樣才能減小響應時間使終端用戶滿意而不是引來罵聲一片。(陳朋奕語:不要那麼嚴肅,嘿嘿)mysql
俺今天這麼激動又想寫文章的緣由是MySQL5.1的發佈帶來了設計超強動力數據庫的強有力的武器,任何MySQL的DBA都應該儘快學習並使用它。俺以爲若是能很好滴使用這個5.1版帶來的新特性,DBA可使本身管理的VLDB(不知道什麼是VLDB?告訴你,是好大好大的數據庫的意思,Very Large DB)或數據倉庫奇蹟般的得到巨大的性能提高。sql
什麼是數據庫分區?數據庫
數據庫分區是一種物理數據庫設計技術,DBA和數據庫建模人員對其至關熟悉。雖然分區技術能夠實現不少效果,但其主要目的是爲了在特定的SQL操做中減小數據讀寫的總量以縮減響應時間。數據庫設計
分區主要有兩種形式://這裏必定要注意行和列的概念(row是行,column是列)ide
在數據庫供應商開始在他們的數據庫引擎中創建分區(主要是水平分區)時,DBA和建模者必須設計好表的物理分區結構,不要保存冗餘的數據(不一樣表中同時都包含父表中的數據)或相互聯結成一個邏輯父對象(一般是視圖)。這種作法會使水平分區的大部分功能失效,有時候也會對垂直分區產生影響。性能
在MySQL 5.1中進行分區學習
MySQL5.1中最激動人心的新特性應該就是對水平分區的支持了。這對MySQL的使用者來講確實是個好消息,並且她已經支持分區大部分模式:測試
Range(範圍) – 這種模式容許DBA將數據劃分不一樣範圍。例如DBA能夠將一個表經過年份劃分紅三個分區,80年代(1980's)的數據,90年代(1990's)的數據以及任何在2000年(包括2000年)後的數據。
Hash(哈希) – 這中模式容許DBA經過對錶的一個或多個列的Hash Key進行計算,最後經過這個Hash碼不一樣數值對應的數據區域進行分區,。例如DBA能夠創建一個對錶主鍵進行分區的表。
Key(鍵值) – 上面Hash模式的一種延伸,這裏的Hash Key是MySQL系統產生的。
List(預約義列表) – 這種模式容許系統經過DBA定義的列表的值所對應的行數據進行分割。例如:DBA創建了一個橫跨三個分區的表,分別根據2004年2005年和2006年值所對應的數據。
Composite(複合模式) - 很神祕吧,哈哈,實際上是以上模式的組合使用而已,就不解釋了。舉例:在初始化已經進行了Range範圍分區的表上,咱們能夠對其中一個分區再進行hash哈希分區。
分區帶來的好處太多太多了,有多少?俺也不知道,本身猜去吧,要是以爲沒有多少就別用,反正俺也不求你用。不過在這裏俺強調兩點好處:
性能的提高(Increased performance) - 在掃描操做中,若是MySQL的優化器知道哪一個分區中才包含特定查詢中須要的數據,它就能直接去掃描那些分區的數據,而不用浪費不少時間掃描不須要的地方了。須要舉個例子?好啊,百萬行的表劃分爲10個分區,每一個分區就包含十萬行數據,那麼查詢分區須要的時間僅僅是全表掃描的十分之一了,很明顯的對比。同時對十萬行的表創建索引的速度也會比百萬行的快得多得多。若是你能把這些分區創建在不一樣的磁盤上,這時候的I/O讀寫速度就「不堪設想」(沒用錯詞,真的太快了,理論上100倍的速度提高啊,這是多麼快的響應速度啊,因此有點不堪設想了)了。
對數據管理的簡化(Simplified data management) - 分區技術可讓DBA對數據的管理能力提高。經過優良的分區,DBA能夠簡化特定數據操做的執行方式。例如:DBA在對某些分區的內容進行刪除的同時能保證餘下的分區的數據完整性(這是跟對錶的數據刪除這種大動做作比較的)。
此外分區是由MySQL系統直接管理的,DBA不須要手工的去劃分和維護。例如:這個例如沒意思,不講了,若是你是DBA,只要你劃分了分區,之後你就不用管了就是了。
站在性能設計的觀點上,俺們對以上的內容也是至關感興趣滴。經過使用分區和對不一樣的SQL操做的匹配設計,數據庫的性能必定能得到巨大提高。下面我們一塊兒用用這個MySQL 5.1的新功能看看。
下面全部的測試都在Dell Optiplex box with a Pentium 4 3.00GHz processor, 1GB of RAM機器上(炫耀啊……),Fedora Core 4和MySQL 5.1.6 alpha上運行經過。
如何進行實際分區
看看分區的實際效果吧。咱們創建幾個一樣的MyISAM引擎的表,包含日期敏感的數據,但只對其中一個分區。分區的表(表名爲part_tab)咱們採用Range範圍分區模式,經過年份進行分區:
mysql> 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 );
Query OK, 0 rows affected (0.00 sec)
注意到了這裏的最後一行嗎?這裏把不屬於前面年度劃分的年份範圍都包含了,這樣才能保證數據不會出錯,你們之後要記住啊,否則數據庫平白無故出錯你就爽了。那下面咱們創建沒有分區的表(表名爲no_part_tab):
mysql> create table no_part_tab
-> (c1 int(11) default NULL,
-> c2 varchar(30) default NULL,
-> c3 date default NULL) engine=myisam;
Query OK, 0 rows affected (0.02 sec)
下面咱寫一個存儲過程(感謝Peter Gulutzan給的代碼,若是你們須要Peter Gulutzan的存儲過程教程的中文翻譯也能夠跟我要,chenpengyi◎gmail.com),它能向咱剛纔創建的已分區的表中平均的向每一個分區插入共8百萬條不一樣的數據。填滿後,咱就給沒分區的克隆表中插入相同的數據:
mysql> delimiter //
mysql> 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
-> //
Query OK, 0 rows affected (0.00 sec)
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
表都準備好了。咱開始對這兩表中的數據進行簡單的範圍查詢吧。先分區了的,後沒分區的,跟着有執行過程解析(MySQL Explain命令解析器),能夠看到MySQL作了什麼:
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程序也告訴咱們在對已分區的表的查詢過程當中僅對第一個分區進行了掃描,其餘都跳過了。
嗶厲吧拉,說阿說……反正就是這個分區功能對DBA頗有用拉,特別對VLDB和須要快速反應的系統。
對Vertical Partitioning的一些見解
雖然MySQL 5.1自動實現了水平分區,但在設計數據庫的時候不要輕視垂直分區。雖然要手工去實現垂直分區,但在特定場合下你會收益很多的。例如在前面創建的表中,VARCHAR字段是你日常不多引用的,那麼對它進行垂直分區會不會提高速度呢?我們看看測試結果:
mysql> desc part_tab;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| c1 | int(11) | YES | | NULL | |
| c2 | varchar(30) | YES | | NULL | |
| c3 | date | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.03 sec)
mysql> alter table part_tab drop column c2;
Query OK, 8000000 rows affected (42.20 sec)
Records: 8000000 Duplicates: 0 Warnings: 0
mysql> desc part_tab;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| c1 | int(11) | YES | | NULL | |
| c3 | date | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 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 (0.34 sec)
在設計上去掉了VARCHAR字段後,不止是你,俺也發現查詢響應速度上得到了另外一個90%的時間節省。因此你們在設計表的時候,必定要考慮,表中的字段是否真正關聯,又是否在你的查詢中有用?
補充說明
這麼簡單的文章確定不能說全MySQL 5.1 分區機制的全部好處和要點(雖然對本身寫文章水平頗有信心),下面就說幾個感興趣的:
站在性能主導的觀點上來講,MySQL 5.1的分區功能能給數據性能帶來巨大的提高的同時減輕DBA的管理負擔,若是分區合理的話。若是須要更多的資料能夠去http://dev.mysql.com/doc/refman/5.1/en/partitioning.html或http://forums.mysql.com/list.php?106得到相關資料。
關於MySQL分區的使用方法很快發佈上來,這裏有什麼錯誤歡迎指出,或給我來信
——2006-05-05陳朋奕
(http://www.fanqiang.com)