mysql表分区使用及详细介绍

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql表分区使用及详细介绍相关的知识,希望对你有一定的参考价值。

参考技术A

一、分区概念

   分区是将一个表分成多个区块进行操作和保存,从而降低每次操作的数据,提高性能。而对于应用来说则是透明的,从逻辑上看只有一张表,但在物理上这个表可能是由多个物理分区组成的,每个分区都是独立的对象,可以进行独立处理。

二、分区作用

   1.可以逻辑数据分割,分割数据能够有多个不同的物理文件路径。

  2.可以存储更多的数据,突破系统单个文件最大限制。

  3.提升性能,提高每个分区的读写速度,提高分区范围查询的速度。

  4.可以通过删除相关分区来快速删除数据

  5.通过跨多个磁盘来分散数据查询,从而提高磁盘I/O的性能。

  6.涉及到例如SUM()、COUNT()这样聚合函数的查询,可以很容易的进行并行处理。

  7.可以备份和恢复独立的分区,这对大数据量很有好处。

三、分区能支持的引擎

   mysql支持大部分引擎创建分区,入MyISAM、InnoDB等;不支持MERGE和CSV等来创建分区。同一个分区表中的所有分区必须是同一个存储引擎。值得注意的是,在MySQL8版本中,MyISAM表引擎不支持分区。

四、确认MySQL支持分区

   从MySQL5.1开始引入分区功能,可以如下方式查看是否支持:

  老版本用:SHOW VARIABLES LIKE \'%partition%\';

  新版本用:show plugins;

五、分区类型

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

    例如,可以将一个表通过年份划分成两个分区,2001 -2010年、2011-2020。

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

    比如 根据字段 把值为1、3、5的放到一起,2、4、6的另外放到一起 等等...

  3. HASH分区:基于用户定义的表达式的返回值来进行选择分区,该表达式使用将要插入到表中的这些行的列值来进行计算,这个函数必须产生非负整数值。

  通过HASH运算来进行分区,分布的比较均匀

  4. KEY分区:类似于按HASH分区,由MySQL服务器提供其自身的哈希函数。

  按照KEY进行分区类似于按照HASH分区

六、分区创建注意事项

  1. 如果表中存在primary key 或者 unique key 时,分区的列必须是paimary key或者unique key的一个组成部分,也就是说,分区函数的列只能从pk或者uk这些key中取子集

  2. 如果表中不存在任何的paimary key或者unique key,则可以指定任何一个列作为分区列

  3. 5.5版本前的RANGE、LIST、HASH分区要求分区键必须是int;MySQL5.5及以上,支持非整形的RANGE和LIST分区,即:range columns 和 list columns (可以用字符串来进行分区)。

七、分区命名

  1. 分区的名字基本上遵循其他MySQL 标识符应当遵循的原则,例如用于表和数据库名字的标识符。应当注意的是, 分区的名字是不区分大小写的 。

  2. 无论使用何种类型的分区,分区总是在创建时就自动的顺序编号,且从0开始记录。

八、 创建分区

1. RANGE分区:

CREATE TABLE `test01` (

`dayid` int(11) DEFAULT NULL,

`mac` varchar(32) NOT NULL DEFAULT \'\',

`dtype` varchar(50) NOT NULL DEFAULT \'\'

) ENGINE=InnoDB DEFAULT CHARSET=utf8

/*!50100 PARTITION BY LIST (dayid)

(PARTITION p20171205 VALUES IN (20171205) ENGINE = InnoDB,

PARTITION p20171204 VALUES IN (20171204) ENGINE = InnoDB,

PARTITION p20171206 VALUES IN (20171206) ENGINE = InnoDB,

PARTITION p20171207 VALUES IN (20171207) ENGINE = InnoDB) */

解读:以上为 uuid小于5时放到p0分区下,uuid大于5且小于10放到p1分区下,uuid大于10且小于15放到p2分区下,uuid大于15 一直到最大值的存在p3分区下

2. LIST分区:

CREATE TABLE tbl_test (


uuid INT NOT NULL,


title VARCHAR(20)

)

)

PARTITION BY List (uuid) (


PARTITION p0 VALUES in (1,2,3,5),


PARTITION p1 VALUES in (7,9,10),


PARTITION p2 VALUES in (11,15)

)

);

解读:以上为uuid 等于1/2/3/5时放到p0分区,7/9/10放到p1分区,11/15放到p2分区。当时用insert into时 如果uuid的值不存在p0/p1/p2分区时,则会插入失败而报错。

3. HASH分区:

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

CREATE TABLE tbl_test (


uuid INT NOT NULL,


title VARCHAR(20)

))

PARTITION BY HASH (uuid) (


PARTITIONS 3

));

解读:MySQL自动创建3个分区,在执行insert into时,根据插入的uuid通过算法来自动分配区间。

注意:

  (1) 由于每次插入、更新、删除一行,这个表达式都要计算一次,这意味着非常复杂的表达式可能会引起性能问题,尤其是在执行同时影响大量行的运算(例如批量插入)的时候。

  (2) 最有效率的哈希函数是只对单个表列进行计算,并且它的值随列值进行一致的增大或减小,因为这考虑了在分区范围上的“修剪”。也就是说,表达式值和它所基于的列的值变化越接近,就越能有效地使用该表达式来进行HASH分区。

3.1:线性HASH分区

  线性HASH分区在“PARTITION BY”子句中添加“LINEAR”关键字。

  线性HASH分区的有点在于增加、删除、合并和拆分分区将变得更加快捷,有利于处理含有及其大量数据的表。它的缺点在于各个分区间数据的分布不大可能均衡。

4. KEY分区

类似于HASH分区,HASH分区允许用户自定义的表达式,而KEY分区则不允许使用用户自定义的表达式;HASH分区只支持整数分区,KEY分区支持除了blob和text类型之外的其他数据类型分区。

与HASH分区不同,创建KEY分区表的时候,可以不指定分区键,默认会选择使用主键或唯一键作为分区键,没有主键或唯一键,就必须指定分区键。

CREATE TABLE tbl_test (


uuid INT NOT NULL,


title VARCHAR(20)

))

PARTITION BY LINEAR Key (uuid)

PARTITIONS 3;

解读:根据分区键来进行分区

5. 子分区

子分区是分区表中,每个分区的再次分割,适合保存非常大量的数据。

CREATE TABLE tbl_test (


registerTime Date

))

PARTITION BY GANGE(YEAR(registerTime))


SUBPARTITION BY HASH (TO_DAYS(registerTime))


SUBPARTITIONS 2

(


PARTITION p0 VALUES LESS THAN (2017),


PARTITION p1 VALUES LESS THAN (2020),


PARTITION p2 VALUES LESS THAN MAXVALUE


);

解读:主分区使用RANGE按照年来进行分区,有3个RANGE分区。这3个分区中又被进一步分成了2个子分区,实际上,整个表被分成了3 * 2 = 6个分区。每个子分区按照天进行HASH分区。小于2017的放在一起,2017-2020的放在一起,大于2020的放在一起。

注意:

  (1) 在MySQL5.1中,对于已经通过RANGE或LIST分区了的表在进行子分区是可能的。子分区既可以使用HASH分区,也可以使用KEY分区。这也被称为复合分区。

  (2) 每个分区必须有相同数量的子分区。

  (3) 如果在一个分区表上的任何分区上使用SUBPARTITION来明确定义任何子分区,那么就必须定义所有的子分区。

  (4) 每个SUBPARTITION子句必须包含(至少)子分区的一个名字。

(5) 在每个子分区内,子分区的名字必须是惟一的,目前在整个表中,也要保持唯一。例如:

PARTITION BY RANGE(YEAR(registerTime))


SUBPARTITION BY HASH(TO_DAYS(registerTime))


(


PARTITION p0 VALUES LESS THAN (2017) (


SUBPARTITION s0,


SUBPARTITION s1


),


PARTITION p1 VALUES LESS THAN (2020) (


SUBPARTITION s2,


SUBPARTITION s3


),


PARTITION p2 VALUES LESS THAN MAXVALUE (


SUBPARTITION s4,


SUBPARTITION s5


)


)

子分区可以用于特别大的表,可以在多个磁盘间分配数据和索引。例如:

SUBPARTITION s0


DATA DIRECTORY = \'/disk0/data\'

INDEX DIRECTORY = \'/disk0/idx\'

,

,

SUBPARTITION s1


DATA DIRECTORY = \'/disk1/data\'

INDEX DIRECTORY = \'/disk1/idx\'

九、MySQL分区处理NULL值的方式

  MySQL中的分区禁止空值NULL上没有进行处理,无论它是一个列值还是一个用户定义表达式的值,一般而言,在这种情况下MySQL把NULL视为0。如果你希望回避这种做法,你应该在设计表时声明列“NOT NULL”。

十、分区管理概述

  可以对分区进行添加、删除、重新定义、合并或拆分等管理操作。

① RANGE和LIST分区的管理

  1. 删除分区语句如:alter table tbl_test drop partition p0;

  注意:

    (1) 当删除了一个分区,也同时删除了该分区中所有的数据。

    (2) 可以通过show create table tbl_test;来查看新的创建表的语句。

    (3) 如果是LIST分区的话,删除的数据不能新增进来,因为这些行的列值包含在已经删除了的分区的值列表中。

  2. 添加分区语句如:alter table tbl_test add partition(partition p3 values less than(50));

  注意:

    (1) 对于RANGE分区的表,只可以添加新的分区到分区列表的最高端。

    (2) 对于LIST分区的表,不能添加已经包含在现有分区值列表中的任意值。

  3. 如果希望能不丢失数据的条件下重新定义分区,可以使用如下语句:

    ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO(partition_definitions)

    (1) 拆分分区如:

    ALTER TABLE tbl_name REORGANIZE PARTITION partition_list INTO(partition s0 values less than(5),partition s1 values less than(10));

    或者如:

    ALTER TABLE tbl_name REORGANIZE PARTITION p0 INTO(partition s0 values in(1,2,3), partition s1 values in(4,5));

    (2) 合并分区如:ALTER TABLE tbl_name REORGANIZE PARTITION s0,s1 INTO(partition p0 values in(1,2,3,4,5));

  4. 删除所有分区,但保留数据,形式:ALTER TABLE tbl_name remove partitioning;

② HASH和KEY分区的管理

   1. 减少分区数量语句如:ALTER TABLE tbl_name COALESCE PARTITION 2;

  2. 添加分区数量语句如:ALTER TABLE tbl_name add PARTITION partitions 2;

③ 其他分区管理语句

   1. 重建分区 :类似于先删除保存在分区中的所有记录,然后重新插入它们,可用于整理分区碎片。如:ALTER table tbl_name REBUILD PARTITION p2,p3;

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

   3. 分析分区 :读取并保存分区的键分布,如:ALTER TABLE tbl_name ANALYZE PARTITION p2,p3;

   4. 检查分区 :检查分区中的数据或索引是否已经被破坏,如:ALTER TABLE tbl_name CHECK PARTITION p2,p3;

   5. 修补分区 :修补被破坏的分区,如:ALTER TABLE tbl_name REPAIR PARTITION p2,p3;

十、查看分区信息

  1. 查看分区信息:select * from information_schema.partitions where table_schema=\'arch1\' and table_name = \'tbl_test\' G;

  2. 查看分区上的数据:select * from tbl_test partition(p0);

  3. 查看MySQL会操作的分区:explain partitions select * from tbl_test where uuid = 2;

十一、 局限性

   1. 最大分区数目不能超过1024,一般建议对单表的分区数不要超过50个。

  2. 如果含有唯一索引或者主键,则分区列必须包含在所有的唯一索引或者主键在内。

  3. 不支持外键。

  4. 不支持全文索引,对分区表的分区键创建索引,那么这个索引也将被分区。

  5. 按日期进行分区很合适,因为很多日期函数可以用。但是对字符串来说合适的分区函数不太多。

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

  7. 临时表不能被分区。

  8. 分区表对于单条记录的查询没有优势。

  9. 要注意选择分区的成本,没插入一行数据都需要按照表达式筛选插入的分区。

  10. 分区字段尽量不要可以为null

MySQL分区表最佳实践

前言:

分区是一种表的设计模式,通俗地讲表分区是将一大表,根据条件分割成若干个小表。但是对于应用程序来讲,分区的表和没有分区的表是一样的。换句话来讲,分区对于应用是透明的,只是数据库对于数据的重新整理。本篇文章给大家带来的内容是关于MySQL中分区表的介绍及使用场景,有需要的朋友可以参考一下,希望对你有所帮助。

1.分区的目的及分区类型

MySQL在创建表的时候可以通过使用PARTITION BY子句定义每个分区存放的数据。在执行查询的时候,优化器根据分区定义过滤那些没有我们需要的数据的分区,这样查询就可以无需扫描所有分区,只需要查找包含需要数据的分区即可。

分区的另一个目的是将数据按照一个较粗的粒度分别存放在不同的表中。这样做可以将相关的数据存放在一起,另外,当我们想要一次批量删除整个分区的数据也会变得很方便。

下面简单介绍下四种常见的分区类型:

  • RANGE分区:最为常用,基于属于一个给定连续区间的列值,把多行分配给分区。最常见的是基于时间字段。
  • LIST分区:LIST分区和RANGE分区类似,区别在于LIST是枚举值列表的集合,RANGE是连续的区间值的集合。
  • HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式。
  • KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。

上述四种分区类型中,RANGE分区即范围分区是最常用的。RANGE分区的特点是多个分区的范围要连续,但是不能重叠,默认情况下使用VALUES LESS THAN属性,即每个分区不包括指定的那个值。

2.分区操作示例

本节内容以RANGE分区为例,介绍下分区表相关的操作。

# 创建分区表
mysql> CREATE TABLE `tr` (
    ->   `id` INT, 
    ->   `name` VARCHAR(50), 
    ->   `purchased` DATE
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    -> PARTITION BY RANGE( YEAR(purchased) ) (
    -> PARTITION p0 VALUES LESS THAN (1990),
    -> PARTITION p1 VALUES LESS THAN (1995),
    -> PARTITION p2 VALUES LESS THAN (2000),
    -> PARTITION p3 VALUES LESS THAN (2005),
    -> PARTITION p4 VALUES LESS THAN (2010),
    -> PARTITION p5 VALUES LESS THAN (2015)
    -> );
Query OK, 0 rows affected (0.28 sec)

# 插入数据
mysql> INSERT INTO `tr` VALUES
    ->     (1, ‘desk organiser‘, ‘2003-10-15‘),
    ->     (2, ‘alarm clock‘, ‘1997-11-05‘),
    ->     (3, ‘chair‘, ‘2009-03-10‘),
    ->     (4, ‘bookcase‘, ‘1989-01-10‘),
    ->     (5, ‘exercise bike‘, ‘2014-05-09‘),
    ->     (6, ‘sofa‘, ‘1987-06-05‘),
    ->     (7, ‘espresso maker‘, ‘2011-11-22‘),
    ->     (8, ‘aquarium‘, ‘1992-08-04‘),
    ->     (9, ‘study desk‘, ‘2006-09-16‘),
    ->     (10, ‘lava lamp‘, ‘1998-12-25‘);
Query OK, 10 rows affected (0.03 sec)
Records: 10  Duplicates: 0  Warnings: 0

创建后可以看到,每个分区都会对应1个ibd文件。上面创建语句还是很好理解的,在此分区表中,通过YEAR函数取出DATE日期中的年份并转化为整型,年份小于1990的存储在分区p0中,小于1995的存储在分区p1中,以此类推。请注意,每个分区的定义顺序是从最低到最高。为了防止插入的数据因找不到相应分区而报错,我们应该及时创建新的分区。下面继续展示关于分区维护的其他操作。

# 查看某个分区的数据
mysql> SELECT * FROM tr PARTITION (p2);
+------+-------------+------------+
| id   | name        | purchased  |
+------+-------------+------------+
|    2 | alarm clock | 1997-11-05 |
|   10 | lava lamp   | 1998-12-25 |
+------+-------------+------------+
2 rows in set (0.00 sec)

# 增加分区
mysql> alter table tr add partition(
    -> PARTITION p6 VALUES LESS THAN (2020)
    -> );
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 拆分分区
mysql> alter table tr reorganize partition p5 into(
    ->   partition s0 values less than(2012),
    ->   partition s1 values less than(2015)
    -> );
Query OK, 0 rows affected (0.26 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 合并分区
mysql> alter table tr reorganize partition s0,s1 into ( 
    ->     partition p5 values less than (2015) 
    -> );
Query OK, 0 rows affected (0.12 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 清空某分区的数据
mysql> alter table tr truncate partition p0;
Query OK, 0 rows affected (0.11 sec)

# 删除分区
mysql> alter table tr drop partition p1;
Query OK, 0 rows affected (0.06 sec)
Records: 0  Duplicates: 0  Warnings: 0

# 交换分区
# 先创建与分区表同样结构的交换表
mysql> CREATE TABLE `tr_archive` (
    ->   `id` INT, 
    ->   `name` VARCHAR(50), 
    ->   `purchased` DATE
    -> ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.28 sec)
# 执行exchange交换分区 
mysql> alter table tr exchange PARTITION p2 with table tr_archive;
Query OK, 0 rows affected (0.13 sec)    

3.分区注意事项及适用场景

其实分区表的使用有很多限制和需要注意的事项,参考官方文档,简要总结几点如下:

  • 分区字段必须是整数类型或解析为整数的表达式。
  • 分区字段建议设置为NOT NULL,若某行数据分区字段为null,在RANGE分区中,该行数据会划分到最小的分区里。
  • MySQL分区中如果存在主键或唯一键,则分区列必须包含在其中。
  • Innodb分区表不支持外键。
  • 更改sql_mode模式可能影响分区表的表现。
  • 分区表不影响自增列。

从上面的介绍中可以看出,分区表适用于一些日志记录表。这类表的特点是数据量大、并且有冷热数据区分,可以按照时间维度来进行数据归档。这类表是比较适合使用分区表的,因为分区表可以对单独的分区进行维护,对于数据归档更方便。

4.分区表为什么不常用

在我们项目开发中,分区表其实是很少用的,下面简单说明下几点原因:

  • 分区字段的选择有限制。
  • 若查询不走分区键,则可能会扫描所有分区,效率不会提升。
  • 若数据分布不均,分区大小差别较大,可能性能提升也有限。
  • 普通表改造成分区表比较繁琐。
  • 需要持续对分区进行维护,比如到了6月份前就要新增6月份的分区。
  • 增加学习成本,存在未知风险。

总结:

本文较为详细的介绍了MySQL分区相关内容,如果想使用分区表的话,建议提早做好规划,在初始化的时候即创建分区表并制定维护计划,使用得当还是比较方便的,特别是有历史数据归档需求的表,使用分区表会使归档更方便。当然,关于分区表的内容还有很多,有兴趣的同学可以找找官方文档,官方文档中有大量示例。

参考:

技术图片

以上是关于mysql表分区使用及详细介绍的主要内容,如果未能解决你的问题,请参考以下文章

Mysql分表和分区的区别分库分表介绍与区别

LinuxLinux目录结构及详细介绍

简述iptales四表五链及详细介绍iptables命令使用方法

Linux目录结构及详细介绍

MYSQL优化_MYSQL分区技术

话编程 | 详细介绍MySQL各个集群方案