mysql 分区

来源:互联网 发布:考勤系统数据读不出 编辑:程序博客网 时间:2024/06/05 17:09

教程一

1. 什么是表分区?

表分区,是指根据一定规则,将数据库中的一张表分解成多个更小的,容易管理的部分。从逻辑上看,只有一张表,但是底层却是由多个物理分区组成。

2. 表分区与分表的区别

分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。 分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。

3. 表分区有什么好处?

1)分区表的数据可以分布在不同的物理设备上,从而高效地利用多个硬件设备。 2)和单个磁盘或者文件系统相比,可以存储更多数据 3)优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率;涉及sum和count语句时,也可以在多个分区上并行处理,最后汇总结果。 4)分区表更容易维护。例如:想批量删除大量数据可以清除整个分区。 5)可以使用分区表来避免某些特殊的瓶颈,例如InnoDB的单个索引的互斥访问,ext3问价你系统的inode锁竞争等。

4. 分区表的限制因素

1)一个表最多只能有1024个分区 2) MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。 3)如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。 4)分区表中无法使用外键约束 5)mysql的分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区而不对表分区,也不能只对表的一部分数据分区。

5. 如何判断当前MySQL是否支持分区?

命令:show variables like '%partition%' 运行结果:

mysql> show variables like '%partition%';+-------------------+-------+| Variable_name     | Value |+-------------------+-------+| have_partitioning | YES   |+-------------------+-------+1 row in set (0.00 sec)

have_partintioning 的值为YES,表示支持分区。

6. MySQL支持的分区类型有哪些?

1)RANGE分区:按照数据的区间范围分区 2)LIST分区:按照List中的值分区,与RANGE的区别是,range分区的区间范围值是连续的。 3)HASH分区 4)KEY分区 说明 在MySQL5.1版本中,RANGE,LIST,HASH分区要求分区键必须是INT类型,或者通过表达式返回INT类型。但KEY分区的时候,可以使用其他类型的列(BLOB,TEXT类型除外)作为分区键。

7. Range分区

利用取值范围进行分区,区间要连续并且不能互相重叠。 语法:

partition by range(exp)( //exp可以为列名或者表达式,比如to_date(created_date)    partition p0 values less than(num))

例如:

mysql> create table emp(    -> id INT NOT null,    -> store_id int not null    -> )    -> partition by range(store_id)(    ->   partition p0 values less than(10),    ->   partition p1 values less than(20)    -> );

上面的语句创建了emp表,并根据store_id字段进行分区,小于10的值存在分区p0中,大于等于10,小于20的值存在分区p1中。 注意 每个分区都是按顺序定义的,从最低到最高。上面的语句,如果将less than(10) 和less than (20)的顺序颠倒过来,那么将报错,如下:

ERROR 1493 (HY000): VALUES LESS THAN value must be strictly increasing for each partition

RANGE分区存在的问题

  1. range范围覆盖问题:当插入的记录中对应的分区键的值不在分区定义的范围中的时候,插入语句会失败。 上面的例子,如果我插入一条store_id = 30的记录会怎么样呢? 我们上面分区的时候,最大值是20,如果插入一条超过20的记录,会报错:
    mysql> insert into emp(id,store_id) values(2,30);ERROR 1526 (HY000): Table has no partition for value 30

    提示30这个值没有对应的分区。 解决办法 A. 预估分区键的值,及时新增分区。 B. 设置分区的时候,使用values less than maxvalue 子句,MAXVALUE表示最大的可能的整数值。 C. 尽量选择能够全部覆盖的字段作为分区键,比如一年的十二个月等。

  2. Range分区中,分区键的值如果是NULL,将被作为一个最小值来处理。

8. LIST分区

List分区是建立离散的值列表告诉数据库特定的值属于哪个分区。 语法:

   partition by list(exp)( //exp为列名或者表达式        partition p0 values in (3,5)  //值为3和5的在p0分区    )

与Range不同的是,list分区不必生命任何特定的顺序。例如:

mysql> create table emp1(    -> id int not null,    -> store_id int not null    -> )    -> partition by list(store_id)(    ->   partition p0 values in (3,5),    ->   partition p1 values in (2,6,7,9)    -> );

注意 如果插入的记录对应的分区键的值不在list分区指定的值中,将会插入失败。并且,list不能像range分区那样提供maxvalue。

9. Columns分区

MySQL5.5中引入的分区类型,解决了5.5版本之前range分区和list分区只支持整数分区的问题。 Columns分区可以细分为 range columns分区和 list columns分区,他们都支持整数,日期时间,字符串三大数据类型。(不支持text和blob类型作为分区键) columns分区还支持多列分区(这里不详细展开)。

10. Hash分区

Hash分区主要用来分散热点读,确保数据在预先确定个数的分区中尽可能平均分布。 MySQL支持两种Hash分区:常规Hash分区和线性Hash分区。 A. 常规Hash分区:使用取模算法 语法:

partition by hash(store_id) partitions 4;

上面的语句,根据store_id对4取模,决定记录存储位置。 比如store_id = 234的记录,MOD(234,4)=2,所以会被存储在第二个分区。

常规Hash分区的优点和不足 优点:能够使数据尽可能的均匀分布。 缺点:不适合分区经常变动的需求。假如我要新增加两个分区,现在有6个分区,那么MOD(234,6)的结果与之前MOD(234,4)的结果就会出现不一致,这样大部分数据就需要重新计算分区。为解决此问题,MySQL提供了线性Hash分区。

B. 线性Hash分区:分区函数是一个线性的2的幂的运算法则。 语法:

partition by LINER hash(store_id) partitions 4;

与常规Hash的不同在于,“Liner”关键字。 算法介绍: 假设要保存记录的分区编号为N,num为一个非负整数,表示分割成的分区的数量,那么N可以通过以下步骤得到:

Step 1. 找到一个大于等于num的2的幂,这个值为V,V可以通过下面公式得到:
V = Power(2,Ceiling(Log(2,num)))
例如:刚才设置了4个分区,num=4,Log(2,4)=2,Ceiling(2)=2,power(2,2)=4,即V=4

Step 2. 设置N=F(column_list)&(V-1)
例如:刚才V=4,store_id=234对应的N值,N = 234&(4-1) =2

Step 3. 当N>=num,设置V=Ceiling(V/2),N=N&(V-1)
例如:store_id=234,N=2<4,所以N就取值2,即可。
假设上面算出来的N=5,那么V=Ceiling(2.5)=3,N=234&(3-1)=1,即在第一个分区。

线性Hash的优点和不足 优点:在分区维护(增加,删除,合并,拆分分区)时,MySQL能够处理得更加迅速。 缺点:与常规Hash分区相比,线性Hash各个分区之间的数据分布不太均衡。

11. Key分区

类似Hash分区,Hash分区允许使用用户自定义的表达式,但Key分区不允许使用用户自定义的表达式。Hash仅支持整数分区,而Key分区支持除了Blob和text的其他类型的列作为分区键。 语法:

partition by key(exp) partitions 4;//exp是零个或多个字段名的列表

key分区的时候,exp可以为空,如果为空,则默认使用主键作为分区键,没有主键的时候,会选择非空惟一键作为分区键。

12. 子分区

分区表中对每个分区再次分割,又成为复合分区。

13. 分区对于NULL值的处理

MySQ允许分区键值为NULL,分区键可能是一个字段或者一个用户定义的表达式。一般情况下,MySQL在分区的时候会把NULL值当作零值或者一个最小值进行处理。

注意
Range分区中:NULL值被当作最小值来处理
List分区中:NULL值必须出现在列表中,否则不被接受
Hash/Key分区中:NULL值会被当作零值来处理

14. 分区管理

分区管理包括对于分区的增加,删除,以及查询。

增加分区:

对于Range分区和LIst分区来说:

alter table table_name add partition (partition p0 values ...(exp))

values后面的内容根据分区的类型不同而不同。

对于Hash分区和Key分区来说:

alter table table_name add partition partitions 8;

上面的语句,指的是新增8个分区 。

删除分区

对于Range分区和List分区:

alter table table_name drop partition p0; //p0为要删除的分区名称

删除了分区,同时也将删除该分区中的所有数据。同时,如果删除了分区导致分区不能覆盖所有值,那么插入数据的时候会报错。

对于Hash和Key分区:

alter table table_name coalesce partition 2; //将分区缩减到2个

coalesce [ˌkəʊəˈles] vi. 联合,合并

分区查询 1)查询某张表一共有多少个分区

mysql> select  ->   partition_name, ->   partition_expression, ->   partition_description, ->   table_rows -> from  ->   INFORMATION_SCHEMA.partitions -> where ->   table_schema='test' ->   and table_name = 'emp';+----------------+----------------------+-----------------------+------------+| partition_name | partition_expression | partition_description | table_rows |+----------------+----------------------+-----------------------+------------+| p0             | store_id             | 10                    |          0 || p1             | store_id             | 20                    |          1 |+----------------+----------------------+-----------------------+------------+

即,可以从information_schema.partitions表中查询。

2)查看执行计划,判断查询数据是否进行了分区过滤

mysql> explain partitions select * from emp where store_id=10 \G;*************************** 1. row ***************************        id: 1select_type: SIMPLE     table: emppartitions: p1      type: systempossible_keys: NULL       key: NULL   key_len: NULL       ref: NULL      rows: 1     Extra: 1 row in set (0.00 sec)

上面的结果:partitions:p1 表示数据在p1分区进行检索。

教程二

一、       mysql分区简介

数据库分区

数据库分区是一种物理数据库设计技术。虽然分区技术可以实现很多效果,但其主要目的是为了在特定的SQL操作中减少数据读写的总量以缩减sql语句的响应时间,同时对于应用来说分区完全是透明的。

MySQL的分区主要有两种形式:水平分区和垂直分区

 

水平分区(HorizontalPartitioning)

这种形式的分区是对根据表的行进行分区,通过这样的方式不同分组里面的物理列分割的数据集得以组合,从而进行个体分割(单分区)或集体分割(1个或多个分区)。
所有在表中定义的列在每个数据集中都能找到,所以表的特性依然得以保持。水平分区一定要通过某个属性列来分割。常见的比如年份,日期等。

 

垂直分区(VerticalPartitioning)

这种分区方式一般来说是通过对表的垂直划分来减少目标表的宽度,使某些特定的列被划分到特定的分区,每个分区都包含了其中的列所对应所有行。
可以用  showvariables like '%partition%';

命令查询当前的mysql数据库版本是否支持分区。

分区的作用:数据库性能的提升和简化数据管理

在扫描操作中,mysql优化器只扫描保护数据的那个分区以减少扫描范围获得性能的提高。

分区技术使得数据管理变得简单,删除某个分区不会对另外的分区造成影响,分区有系统直接管理不用手工干预。

mysql从5.1版本开始支持分区。每个分区的名称是不区分大小写。同个表中的分区表名称要唯一。

二、       mysql分区类型

根据所使用的不同分区规则可以分成几大分区类型。

RANGE 分区:

基于属于一个给定连续区间的列值,把多行分配给分区。

LIST 分区:

类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。

HASH分区:

基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式。

KEY
分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。

复合分区:

基于RANGE/LIST 类型的分区表中每个分区的再次分割。子分区可以是 HASH/KEY 等类型。

 

三、       mysql分区表常用操作示例

以部门员工表为例子:

1)       创建range分区

 

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date,

salary int

)

partition by range(salary)

(

partition p1 values less than (1000),

partition p2 values less than (2000),

partition p3 values less than maxvalue

);

以员工工资为依据做范围分区。

 

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date not null,

salary int

)

partition by range(year(birthdate))

(

partition p1 values less than (1980),

partition p2 values less than (1990),

partition p3 values less than maxvalue

);

year(birthdate)表达式(计算员工的出生日期)作为范围分区依据。这里最值得注意的是表达式必须有返回值。

 

2)       创建list分区

 

create table emp

(empno  varchar(20not null ,

empname varchar(20),

deptno  int,

birthdate date not null,

salary int

)

partition by list(deptno)

(

partition p1 values in  (10),

partition p2 values in  (20),

partition p3 values  in  (30)

);

以部门作为分区依据,每个部门做一分区。

 

3)       创建hash分区

HASH分区主要用来确保数据在预先确定数目的分区中平均分布。在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中;而在HASH分区中,MySQL 自动完成这些工作,你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date not null,

salary int

)

partition by hash(year(birthdate))

partitions 4;

4)       创建key分区

按照KEY进行分区类似于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的哈希函数是由MySQL 服务器提供,服务器使用其自己内部的哈希函数,这些函数是基于与PASSWORD()一样的运算法则。“CREATE TABLE ...PARTITION BY KEY”的语法规则类似于创建一个通过HASH分区的表的规则。它们唯一的区别在于使用的关键字是KEY而不是HASH,并且KEY分区只采用一个或多个列名的一个列表。

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date not null,

salary int

)

partition by key(birthdate)

partitions 4;

 

 

5)       创建复合分区

 

range - hash(范围哈希)复合分区

 

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date not null,

salary int

)

partition by range(salary)

subpartition by hash(year(birthdate))

subpartitions 3

(

partition p1 values less than (2000),

partition p2 values less than maxvalue

);

range- key复合分区

 

create table emp

(empno varchar(20not null ,

empname varchar(20),

deptno int,

birthdate date not null,

salary int

)

partition by range(salary)

subpartition by key(birthdate)

subpartitions 3

(

partition p1 values less than (2000),

partition p2 values less than maxvalue

);

list - hash复合分区

CREATE TABLE emp (

empno varchar(20NOT NULL,

empname varchar(20) ,

deptno int,

birthdate date NOT NULL,

salary int

)

PARTITION BY list (deptno)

subpartition by hash(year(birthdate))

subpartitions 3

(

PARTITION p1 VALUES in  (10),

PARTITION p2 VALUES in  (20)

)

;

list - key 复合分区

 

CREATE TABLE empk (

empno varchar(20NOT NULL,

empname varchar(20) ,

deptno int,

birthdate date NOT NULL,

salary int

)

PARTITION BY list (deptno)

subpartition by key(birthdate)

subpartitions 3

(

PARTITION p1 VALUES in  (10),

PARTITION p2 VALUES in  (20)

)

;

6)       分区表的管理操作

删除分区:

alter table emp drop partition p1;

不可以删除hash或者key分区。

一次性删除多个分区,alter table emp drop partition p1,p2;

 

增加分区:

alter table emp add partition (partition p3 values less than (4000));

alter table empl add partition (partition p3 values in (40));

 

分解分区:

Reorganizepartition关键字可以对表的部分分区或全部分区进行修改,并且不会丢失数据。分解前后分区的整体范围应该一致。

alter table te

reorganize partition p1 into

(

partition p1 values less than (100),

partition p3 values less than (1000)

); ----不会丢失数据

 

合并分区:

Merge分区:把2个分区合并为一个。
alter table te

reorganize partition p1,p3 into

(partition p1 values less than (1000));

----不会丢失数据

 

重新定义hash分区表:

Alter table emp partition by hash(salary)partitions 7;

----不会丢失数据

重新定义range分区表:

Alter table emp partitionbyrange(salary)

(

partition p1 values less than (2000),

partition p2 values less than (4000)

); ----不会丢失数据

 

删除表的所有分区:


Alter table emp removepartitioning;--不会丢失数据

 

重建分区:

这和先删除保存在分区中的所有记录,然后重新插入它们,具有同样的效果。它可用于整理分区碎片。

ALTER TABLE emp rebuild partitionp1,p2;

 

优化分区:

如果从分区中删除了大量的行,或者对一个带有可变长度的行(也就是说,有VARCHAR,BLOB,或TEXT类型的列)作了许多修改,可以使用“ALTER TABLE ... OPTIMIZE PARTITION”来收回没有使用的空间,并整理分区数据文件的碎片。

ALTER TABLE emp optimize partition p1,p2;

 

分析分区:

读取并保存分区的键分布。

ALTER TABLE emp analyze partition p1,p2;

 

修补分区:

修补被破坏的分区。

ALTER TABLE emp repairpartition p1,p2;

 

检查分区:

可以使用几乎与对非分区表使用CHECK TABLE 相同的方式检查分区。

ALTER TABLE emp CHECK partition p1,p2;

这个命令可以告诉你表emp的分区p1,p2中的数据或索引是否已经被破坏。如果发生了这种情况,使用“ALTER TABLE ... REPAIR PARTITION”来修补该分区。

 

 

【mysql分区表的局限性】

1.      在5.1版本中分区表对唯一约束有明确的规定,每一个唯一约束必须包含在分区表的分区键(也包括主键约束)。

 

CREATE TABLE emptt (

empno varchar(20NOT NULL  ,

empname varchar(20),

deptno int,

birthdate date NOT NULL,

salary int ,

primary key (empno)

)

PARTITION BY range (salary)

(

PARTITION p1 VALUES less than (100),

PARTITION p2 VALUES less than (200)

);

这样的语句会报错。MySQL Database Error: A PRIMARY KEY must include allcolumns in the table's partitioning function;

CREATE TABLE emptt (

empno varchar(20NOT NULL  ,

empname varchar(20) ,

deptno int(11),

birthdate date NOT NULL,

salary int(11) ,

primary key (empno,salary)

)

PARTITION BY range (salary)

(

PARTITION p1 VALUES less than (100),

PARTITION p2 VALUES less than (200)

);

在主键中加入salary列就正常。

 

2.      MySQL分区处理NULL值的方式

如果分区键所在列没有notnull约束。

如果是range分区表,那么null行将被保存在范围最小的分区。

如果是list分区表,那么null行将被保存到list为0的分区。

在按HASH和KEY分区的情况下,任何产生NULL值的表达式mysql都视同它的返回值为0。

为了避免这种情况的产生,建议分区键设置成NOT NULL。

 

3.      分区键必须是INT类型,或者通过表达式返回INT类型,可以为NULL。唯一的例外是当分

区类型为KEY分区的时候,可以使用其他类型的列作为分区键( BLOB or TEXT 列除外)。

 

4.      对分区表的分区键创建索引,那么这个索引也将被分区,分区键没有全局索引一说。

5.      只有RANG和LIST分区能进行子分区,HASH和KEY分区不能进行子分区。

6.      临时表不能被分区。

 

四、       获取mysql分区表信息的几种方法

1.     show create table 表名
可以查看创建分区表的create语句 

2.     show table status 
可以查看表是不是分区表 

3.     查看information_schema.partitions表 
select 
  partition_name part,  
  partition_expression expr,  
  partition_description descr,  
  table_rows  
from information_schema.partitions  where 
  table_schema = schema()  
  and table_name='test';  
可以查看表具有哪几个分区、分区的方法、分区中数据的记录数等信息

4.     explain partitions select语句
通过此语句来显示扫描哪些分区,及他们是如何使用的.

 

五、       分区表性能比较

1.     创建两张表: part_tab(分区表),no_part_tab(普通表)

CREATE TABLEpart_tab

( c1 int defaultNULL, c2 varchar2(30) default NULL, c3 date not null)

PARTITION BYRANGE(year(c3))

(PARTITION p0VALUES LESS THAN (1995),

PARTITION p1 VALUESLESS THAN (1996) ,

PARTITION p2 VALUESLESS THAN (1997) ,

PARTITION p3 VALUESLESS THAN (1998) ,

 PARTITION p4 VALUES LESS THAN (1999) ,

PARTITION p5 VALUESLESS THAN (2000) ,

PARTITION p6 VALUESLESS THAN (2001) ,

PARTITION p7 VALUESLESS THAN (2002) ,

PARTITION p8 VALUESLESS THAN (2003) ,

PARTITION p9 VALUESLESS THAN (2004) ,

PARTITION p10VALUES LESS THAN (2010),

PARTITION p11VALUES LESS THAN (MAXVALUE) );

CREATE TABLE no_part_tab

( c1 int defaultNULL, c2 varchar2(30) default NULL, c3 date not null);

 

2.     用存储过程插入800万条数据

CREATE PROCEDUREload_part_tab()

    begin

    declare v int default 0;

    while v < 8000000

    do

        insert into part_tab

        values (v,'testingpartitions',adddate('1995-01-01',(rand(v)*36520)mod 3652));

         set v = v + 1;

    end while;

end;

insert into no_part_tab  select * frompart_tab;

3.     测试sql性能

查询分区表:

selectcount(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';

+----------+
| count(*) |
+----------+
|   795181 |
+----------+
1 row in set (2.62 sec)

查询普通表:

selectcount(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';

+----------+
| count(*) |
+----------+
|   795181 |
+----------+
1 row in set (7.33 sec)

分区表的执行时间比普通表少70%。

 

4.     通过explain语句来分析执行情况

mysql>explain select count(*) from part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';

+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+

| id |select_type | table    | type |possible_keys | key  | key_len | ref  | rows   | Extra       |

+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+

|  1 | SIMPLE      | part_tab | ALL  | NULL          | NULL | NULL    | NULL | 7980796 | Using where |

+----+-------------+----------+------+---------------+------+---------+------+---------+-------------+

1 rowin set

 

mysql>explain select count(*) from no_part_tab where c3 > date '1995-01-01'and c3 < date '1995-12-31';

+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+

| id |select_type | table       | type |possible_keys | key  | key_len | ref  | rows   | Extra       |

+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+

|  1 | SIMPLE      | no_part_tab | ALL  | NULL          | NULL | NULL    | NULL | 8000206 | Using where |

+----+-------------+-------------+------+---------------+------+---------+------+---------+-------------+

1 rowin set

mysql >

分区表执行扫描了7980796行,而普通表则扫描了8000206行。