mysql(十五):表分区

为什么要分表和分区?

日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。

什么是分表?

分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子表可以分布在同一块磁盘上,也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的子表名,然后去操作它。

什么是分区?

分区和分表相似,都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表,但数据散列到多个位置了。app读写的时候操作的还是大表名字,db自动去组织分区的数据。

mysql分表和分区有什么联系呢?
1.都能提高mysql的性高,在高并发状态下都有一个良好的表现。
2.分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式(如果merge这种分表方式,不能和分区配合的话,可以用其他的分表试),访问量不大,但是表数据很多的表,我们可以采取分区的方式等。
3.分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。采用merge好一些,但也要创建子表和配置子表间的union关系。
4.表分区相对于分表,操作方便,不需要创建子表。

分表的几种方式:

1、mysql集群

它并不是分表,但起到了和分表相同的作用。集群可分担数据库的操作次数,将任务分担到多台数据库上。集群可以读写分离,减少读写压力。从而提升数据库性能。

2、自定义规则分表

大表可以按照业务的规则来分解为多个子表。通常为以下几种类型,也可自己定义规则。

?

1

2

3

4

5

Range(范围)–这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区。

Hash(哈希)–这中模式允许通过对表的一个或多个列的HashKey进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。

Key(键值)-上面Hash模式的一种延伸,这里的HashKey是MySQL系统产生的。

List(预定义列表)–这种模式允许系统通过预定义的列表的值来对数据进行分割。

Composite(复合模式) –以上模式的组合使用 

分表规则与分区规则一样,在分区模块详细介绍。

下面以Range简单介绍下如何分表(按照年份表)。

假设表结构有4个字段:自增id,姓名,存款金额,存款日期

把存款日期作为规则分表,分别创建几个表

2011年:account_2011

2012年:account_2012

……

2015年:account_2015

app在读写的时候根据日期来查找对应的表名,需要手动来判定。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

var getTableName = function() {

    var data = {

        name:'tom',

        money: 2800.00,

        date:'201410013059'

    };

    var tablename = 'account_';

    varyear= parseInt(data.date.substring(0, 4));

    if (year< 2012) {

        tablename += 2011; // account_2011

    }elseif (year< 2013) {

        tablename += 2012; // account_2012

    }elseif (year< 2014) {

        tablename += 2013; // account_2013

    }elseif (year< 2015) {

        tablename += 2014; // account_2014

    }else{

        tablename += 2015; // account_2015

    }

    returntablename;

}

3、利用merge存储引擎来实现分表

merge分表,分为主表和子表,主表类似于一个壳子,逻辑上封装了子表,实际上数据都是存储在子表中的。

我们可以通过主表插入和查询数据,如果清楚分表规律,也可以直接操作子表。

子表2011年

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

CREATETABLE`account_2011` (

`id` int(11)NOTNULLAUTO_INCREMENT ,

`namevarchar(50)CHARACTERSETutf8 COLLATEutf8_general_ciNULLDEFAULTNULL ,

`money` floatNOTNULL ,

`tradeDate`  datetime NOTNULL

PRIMARYKEY(`id`)

)

ENGINE=MyISAM

DEFAULTCHARACTERSET=utf8COLLATE=utf8_general_ci

AUTO_INCREMENT=2

CHECKSUM=0

ROW_FORMAT=DYNAMIC

DELAY_KEY_WRITE=0

;

子表2012年

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

CREATETABLE`account_2012` (

`id` int(11)NOTNULLAUTO_INCREMENT ,

`namevarchar(50)CHARACTERSETutf8 COLLATEutf8_general_ciNULLDEFAULTNULL ,

`money` floatNOTNULL ,

`tradeDate`  datetime NOTNULL

PRIMARYKEY(`id`)

)

ENGINE=MyISAM

DEFAULTCHARACTERSET=utf8COLLATE=utf8_general_ci

AUTO_INCREMENT=2

CHECKSUM=0

ROW_FORMAT=DYNAMIC

DELAY_KEY_WRITE=0

;

主表,所有年

?

1

2

3

4

5

6

7

8

9

10

11

12

13

CREATETABLE`account_all` (

`id` int(11)NOTNULLAUTO_INCREMENT ,

`namevarchar(50)CHARACTERSETutf8 COLLATEutf8_general_ciNULLDEFAULTNULL ,

`money` floatNOTNULL ,

`tradeDate`  datetime NOTNULL

PRIMARYKEY(`id`)

)

ENGINE=MRG_MYISAM

DEFAULTCHARACTERSET=utf8COLLATE=utf8_general_ci

UNION=(`account_2011`,`account_2012`)

INSERT_METHOD=LAST

ROW_FORMAT=DYNAMIC

;

创建主表的时候有个INSERT_METHOD,指明插入方式,取值可以是:0 不允许插入;FIRST 插入到UNION中的第一个表; LAST 插入到UNION中的最后一个表。

通过主表查询的时候,相当于将所有子表合在一起查询。这样并不能体现分表的优势,建议还是查询子表。

分区的几种方式

Range:

?

1

2

3

4

5

6

7

8

9

10

createtablerange(

  idint(11),

  moneyint(11) unsigned notnull,

  datedatetime

  )partitionbyrange(year(date))(

  partition p2007 valuesless than (2008),

  partition p2008 valuesless than (2009),

  partition p2009 valuesless than (2010)

  partition p2010 valuesless than maxvalue

);

List:

?

1

2

3

4

5

6

7

createtablelist(

  aint(11),

  bint(11)

  )(partitionbylist (b)

  partition p0 valuesin(1,3,5,7,9),

  partition p1 valuesin(2,4,6,8,0)

 );

Hash:

?

1

2

3

4

5

createtablehash(

  aint(11),

  b datetime

  )partitionbyhash (YEAR(b)

  partitions 4;

Key:

?

1

2

3

4

5

createtablet_key(

  aint(11),

  b datetime)

  partitionbykey(b)

  partitions 4;

分区管理

新增分区

?

1

2

ALTER TABLE sale_data

ADD PARTITION (PARTITION p201010 VALUES LESS THAN (201011));

删除分区
--当删除了一个分区,也同时删除了该分区中所有的数据。
ALTER TABLE sale_data DROP PARTITION p201010;

分区的合并
下面的SQL,将p201001 - p201009 合并为3个分区p2010Q1 - p2010Q3

?

1

2

3

4

5

6

7

8

9

ALTER TABLE sale_data

REORGANIZE PARTITION p201001,p201002,p201003,

p201004,p201005,p201006,

p201007,p201008,p201009 INTO

(

PARTITION p2010Q1 VALUES LESS THAN (201004),

PARTITION p2010Q2 VALUES LESS THAN (201007),

PARTITION p2010Q3 VALUES LESS THAN (201010)

);

MySQL表分区(1)范围分区-range

创建表分区

create table teacher
  (id varchar(20) not null ,
   name varchar(20),
   age varchar(20),
   birthdate date not null,
   salary int
  )
partition by range(year(birthdate))
(
   partition p1 values less than (1970),
   partition p2 values less than (1990),
   partition p3 values less than maxvalue
);

如果表已经创建完毕,需要后期分区:

ALTER TABLE teacher 
  partition by range(year(birthdate))
  (
    partition p1 values less than (1970),
    partition p2 values less than (1990),
    partition p3 values less than maxvalue
  );

查看数据文件

准备数据

创建存储过程,因为要根据生日birthday进行range分区,因此分别插入不同日期(birthday字段)的数据:

begin
   declare i int;
     set i=0;
      while i<1000 do
        insert into teacher(id,name,age,birthdate,salary) values(i,"leo","30","2016-08-05",3000);
    set i=i+1;
   end while;
end

看看数据库文件的变化过程:

插入生日小于1970年的数据时,p1文件由96k变成了144k,因此可以判断这部分数据存放在该区域。

插入生日大于1970年且小于1990年的数据时:

插入生日大于19900年的数据时:

数据文件

    上面看到在分区完成后多个4个文件,分别是idb和frm格式,下面来说说这两种数据文件。

.ibd文件和.ibdata文件:这两种文件都是存放innodb数据的文件,之所以用两种文件来存放innodb的数据,是因为innodb的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独立表空间存放存储数据。独立表空间存储方式使用.ibd文件,并且每个表一个ibd文件。共享表空间存储方式使用.ibdata文件,所有表共同使用一个ibdata文件。

MySQL表分区(2)列表分区-list

2016年08月05日 15:05:12

阅读数:474

list列表分区和range分区应该说都是一样的,不同的是range分区在分区时的依据是一段连续的区间,而list分区时依据是一组分布的散列值。

创建表分区

partition by list(expr)

  “expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,然后通过“values in (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。注释:在MySQL 5.1中,当使用LIST分区时,有可能只能匹配整数列表。

<span style="font-family:FangSong_GB2312;">create table student
  (id varchar(20) not null ,
  studentno int(20) not null,
  name varchar(20),
  age varchar(20)
  )
 partition by list(studentno)
 (
   partition p1 values in (1,2,3,4),
   partition p2 values in (5,6,7,8),
   partition p3 values in (9,10,11)
 );
</span>

如上创建表student,可以理解为将学号1-11的学生分为三个组,分别为p1、p2、p3三个分区。需要注意的是一般情况下,针对表的分区字段为int等数值类型。

优点

  这使得在表中增加或删除指定组的学生记录变得容易起来。例如,假定要删除p1组的所有学员。查询“ALTER TABLE student(表名) DROP PARTITION p1(分区名);”来进行删除,这种操作方式比同样作用的删除作用的“delete from student where studentno IN (1,2,3,4)要有效果的多,我看到可怕的“in”。

【注意】:如果试图插入列值(或分区表达式的返回值)不在分区值列表中的一行时。即这是11个学生已经分好组了,要再来一个学生那可不行了,就这么多。“INSERT”将失败并报错。例如,假定LIST分区的采用上面的方案,下面的查询将失败:




  这是因为“studentno”列值12不能在用于定义分区p1, p2, p3l的列表中找到。所以插入12列时不知道要放到那个分区。要重点注意的是,LIST分区没有类似如“VALUES LESS THAN MAXVALUE”这样的包含其他值在内的定义。将要匹配的任何值都必须在值列表中找到。LIST分区除了能和RANGE分区结合起来生成一个复合的子分区,与HASH和KEY分区结合起来生成复合的子分区也是可以的。

MySQL表分区(3)哈希分区-hash

2016年08月05日 15:08:37

阅读数:2797

  哈希分区。哈希分区主要是依据表的某个字段以及指定分区的数量。

创建表分区

   要使用HASH分区来分割一个表,要在CREATE TABLE 语句上添加一个“PARTITION BY HASH (expr)”子句,其中“expr”是一个返回一个整数的表达式。它可以仅仅是字段类型为MySQL整型的一列的名字。此外,你很可能需要在后面再添加一个“PARTITIONS num”子句,其中num 是一个非负的整数,它表示表将要被分割成分区的数量。

<span style="font-family:FangSong_GB2312;color: rgb(51, 51, 51); font-size: 15px; line-height: 1.42857; white-space: pre-wrap; background-color: rgb(245, 245, 245);">create table user (
  id int(20) not null,
  role varchar(20) not null,
  description varchar(50) 
)
partition by hash(id) 
partitions 10;</span>

分区后,数据文件被分成10份:

缺点

『注意』如上创建user表,并将user表平均分为十个分区。比较有限制的就是需要知道表的数据有多少才能更好平均分配分区。

MySQL表分区(4)key分区-key

2016年08月05日 15:10:08

阅读数:426

创建表分区

create table role( id int(20) not null,name varchar(20) not null)
partition by linear key(id)
partitions 10;

猜你喜欢

转载自blog.csdn.net/qq_20610631/article/details/82057255