经过分区(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)