尝试下MySQL水平分区

看了看MySQL 5.1的新特性就特别想尝试下这个水平分区,Merge存储引擎同样可以将不同的MyISAM表集合起来形成一个逻辑上的大表,并且可以通过将里面的MyISAM表放置到不同的磁盘上来提高性能,但是Merge引擎在某些方面非常缺乏灵活性,比如往Merge表中插入纪录时,只有两种选择,由INSERT_METHOD控制,要么插入到第一张表中(FIRST),要么插入到最后一张表中(LAST)。水平分区可以提供极大的灵活性,它提供几种划分数据的模式,插入纪录时,根据模式判断一下,记录就会被乖巧地插入到它应该存在的表中。
        下面这个例子展示了水平分区的简单使用,分区使用的是range划分模式。首先使用下面这句SQL语句将表建好(不知道InnoDB引擎支不支持水平分区,照理说InnoDB引擎同样会受大文件的影响,也有分区的需求,使用下面的SQL语句,但将引擎改成InnoDB不会报错,本来也想测测其效率的,结果发现一百万行数据对InnoDB来说太痛苦了,用了一刻多钟还没好,要崩溃了,估计过程内部执行出错了,不想管了,MyISAM比起InnoDB确实足够快速)。
create table part_tab(
a int default null,
b varchar(30) default null,
c date default null
) engine=myisam
partition by range(year(c))
(
partition p0 values less than(1980), /*闷骚的70后*/
partition p1 values less than(1990), /*辛苦的80后*/
partition p2 values less than(2000), /*可爱的90后*/
partition p3 values less than(2010), /*传说中的00后*/
partition p4 values less than maxvalue /*即将到来的一代*/
);

"show tables"只显示有一个表,这也是分区同Merge表不同之处
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| part_tab       | 
+----------------+
1 row in set (0.00 sec)

可以看到数据库目录下生成了一对数据文件和索引文件,但是只有一个frm文件。此时可以看到所有的.MYI文件的大小均为1k,而数据文件大小都为0。
mysql> system ls -lh
总用量 40K
-rw-rw---- 1 mysql mysql   65 2009-08-17 21:19 db.opt
-rw-rw---- 1 mysql mysql 8.5K 2009-08-17 23:31 part_tab.frm
-rw-rw---- 1 mysql mysql   40 2009-08-17 23:31 part_tab.par
-rw-rw---- 1 mysql mysql    0 2009-08-17 23:31 part_tab#P#p0.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-17 23:31 part_tab#P#p0.MYI
-rw-rw---- 1 mysql mysql    0 2009-08-17 23:31 part_tab#P#p1.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-17 23:31 part_tab#P#p1.MYI
-rw-rw---- 1 mysql mysql    0 2009-08-17 23:31 part_tab#P#p2.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-17 23:31 part_tab#P#p2.MYI
-rw-rw---- 1 mysql mysql    0 2009-08-17 23:31 part_tab#P#p3.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-17 23:31 part_tab#P#p3.MYI
-rw-rw---- 1 mysql mysql    0 2009-08-17 23:31 part_tab#P#p4.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-17 23:31 part_tab#P#p4.MYI

       使用下面这个过程使劲往表中插入数据,"ROUND( ( rand(v) * 50 ) * 365 ) ) );"用于产生"0-40"年的总天数,执行完这个过程之后,各数据文件的大小差不多大小。记得不要插入过多的数据,否则会等待很久,这儿只插入了一百万个记录,网上的例子插入了八百万行记录,太疯狂了。
delimiter //
CREATE PROCEDURE load_part_tab()
begin
declare v int default 0;
while v < 1000000
do
insert into part_tab
values (v,'testing partitions',adddate('1970-01-01', ROUND( ( rand(v) * 50 ) * 365 ) ) );

set v = v + 1;
end while;
end
//
delimiter ;

总耗时:
mysql> call load_part_tab;
Query OK, 1 row affected ( 1 min 2.96 sec)

插入记录之后(没有建索引,所有的.MYI文件大小仍然为1K):
root@ecy-geek:/var/lib/mysql/test# ls -lh
总用量 31M
-rw-rw---- 1 mysql mysql   65 2009-08-17 21:19 db.opt
-rw-rw---- 1 mysql mysql 8.5K 2009-08-18 00:17 part_tab.frm
-rw-rw---- 1 mysql mysql   40 2009-08-18 00:17 part_tab.par
-rw-rw---- 1 mysql mysql  6.2M 2009-08-18 00:19 part_tab#P#p0.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:19 part_tab#P#p0.MYI
-rw-rw---- 1 mysql mysql  6.2M 2009-08-18 00:19 part_tab#P#p1.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:19 part_tab#P#p1.MYI
-rw-rw---- 1 mysql mysql  6.2M 2009-08-18 00:19 part_tab#P#p2.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:19 part_tab#P#p2.MYI
-rw-rw---- 1 mysql mysql  6.2M 2009-08-18 00:19 part_tab#P#p3.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:19 part_tab#P#p3.MYI
-rw-rw---- 1 mysql mysql  6.1M 2009-08-18 00:19 part_tab#P#p4.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:19 part_tab#P#p4.MYI

为了比较效率之差,我们再创建一个没有分区的表,如下:
create table no_part_tab(
a int default null,
b varchar(30) default null,
c date default null
)engine=myisam;

       将上面的那个过程名改成"no_part_tab","insert into part_tab"改成"insert into load_no_part_tab",同样插入一百万行记录,当数据都放到一个单一文件时,速度明显较快,往"no_part_tab"中插入一百万行记录耗时不到一分钟。
mysql> delimiter ;
mysql> call load_no_part_tab;
Query OK, 1 row affected (5 5.27 sec)

       数据文件的大小为32M,差不多是上面所有的分区表大小的总和:
-rw-rw---- 1 mysql mysql   31M 2009-08-18 00:28 no_part_tab.MYD
-rw-rw---- 1 mysql mysql 1.0K 2009-08-18 00:28 no_part_tab.MYI

Ok,现在可以比较二者查询的效率之差有多大了。
mysql> select count(*) from part_tab where c > date '1970-01-01' and c < date '1970-12-31';
+----------+
| count(*) |
+----------+
|    19896 | 
+----------+
1 row in set ( 0.21 sec)

mysql> select count(*) from no_part_tab where c > date '1970-01-01' and c < date '1970-12-31';
+----------+
| count(*) |
+----------+
|    19896 | 
+----------+
1 row in set ( 0.85 sec)

mysql> select count(*) from part_tab where c > date '1970-01-01' and c < date '1980-12-31';
+----------+
| count(*) |
+----------+
|   220035 | 
+----------+
1 row in set ( 0.35 sec)

mysql> select count(*) from no_part_tab where c > date '1970-01-01' and c < date '1980-12-31';
+----------+
| count(*) |
+----------+
|   220035 | 
+----------+
1 row in set ( 0.78 sec)

mysql> select count(*) from part_tab where c > date '1970-01-01';
+----------+
| count(*) |
+----------+
|   999974 | 
+----------+
1 row in set ( 0.72 sec)

mysql> select count(*) from no_part_tab where c > date '1970-01-01';
+----------+
| count(*) |
+----------+
|   999974 | 
+----------+
1 row in set ( 0.66 sec)


我发现这些数据极具规律性,上面主要做了三次比较:
1、查询70后的总个数,part_tab#P#p0.MYD和no_part_tab.MYD中的数据是没有秩序的,而no_part_tab.MYD大小是part_tab#P#p0.MYD的五倍左右,没有分区耗时0.85s,分区后耗时0.21s,相差4倍多点,为什么不接近5倍呢?我想大概是因为这么多分区多了,定位到一个分区也需要一点点时间吧。
2、第二次查询70后和80后的总数,这次二者相差两倍多点。
3、第三次统计所有的人数,发现未分区的表反而速度要快些,这是因为所有的数据都在一个文件中,当然比数据分散的表查询有利些。但是从另一方面看,分区后对于比这个年份大的表无须做判断直接加上其记录数目即可,而未分区的表由于记录完全没有顺序,对每一条记录都要做判断,效率更低才是,MyISAM的速度太快了,这儿的记录数量还是有点少,可能记录再多一点,分区表数量也多点,比较的结果可能就不一样了。
       对于一个大表做N次水平分区,在没有什么任何索引的情况下,效率大约提高N倍。如果没有任何索引,二者插入数据的效率应该相当,如果有索引,显然往分区后的表插入数据要快,因为它的索引要小,每个小索引只索引本分区的记录,更新起来要快。网上那个经典的例子分了10个表,往里面插入八百万行记录后,二者效率相差八九倍,也符合上面的推理。使用explain分析没有看到分区后同没有分区有什么区别来,查询的行数都一模一样,网上的例子则显示分区后只查询应该查询的行数,比如统计所有70后的数量,只会查询大约二十五万行。

mysql> explain select count(*) from part_tab where c > date '1970-01-01' and c < date '1980-12-31' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: part_tab
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows:  1000000
        Extra: Using where
1 row in set (0.00 sec)

mysql> explain select count(*) from no_part_tab where c > date '1970-01-01' and c < date '1980-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:  1000000
        Extra: Using where
1 row in set (0.00 sec)

         这儿只是简单地演示了一下水平分区的使用,实际应用中水平分区比这要强大灵活得多,建表时通过"DA TA DIRECTORY"和"INDEX DIRECTORY"可以将不同的分区放到不同的硬盘上,这样可以极大提高数据库的效率。Merge表可以将内含的MyISAM表放到不同的硬盘上,但是它不能只能地将数据插入到数据应该存放的表中,这样就不利于数据的分布存放了。
       "http://hi.baidu.com/jackli00/blog/item/e60371a616a0c09cd14358a1.html"上面有更详细的测试,作者在分区表上为分区表上分别建立索引,然后判断二者的效率,然后对Range,List,Hash,key等几种划分模式有比较详细的说明,值得一看 ^_^
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值