热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

高性能MySql进化论(十二):Mysql中分区表的使用总结

当数据量非常大时(表的容量到达GB或者是TB),如果仍然采用索引的方式来优化查询,由于索引本生的消耗以及大量的索引碎片的产生,查询的过程会导致大量的随机IO的产生,在这种场景下除非可以很好的利用覆盖索引,否则由于在查询的过程中需要根据索引回数据

当数据量非常大时(表的容量到达GB或者是TB),如果仍然采用索引的方式来优化查询,由于索引本生的消耗以及大量的索引碎片的产生,查询的过程会导致大量的随机I/O的产生,在这种场景下除非可以很好的利用覆盖索引,否则由于在查询的过程中需要根据索引回数据

当数据量非常大时(表的容量到达GB或者是TB),如果仍然采用索引的方式来优化查询,由于索引本生的消耗以及大量的索引碎片的产生,查询的过程会导致大量的随机I/O的产生,在这种场景下除非可以很好的利用覆盖索引,否则由于在查询的过程中需要根据索引回数据表查询,会导致性能受到很大的影响,这时可以考虑通过分区表的策略来提高查询的性能。

不同的数据库管理系统对分区的实现可能有所区别,本文主要以MYSQL为基础

1 分区的类型

1.1RANGE分区

按照RANGE分区的表是通过如下一种方式进行分区的,每个分区包含那些分区表达式的值位于一个给定的连续区间内的行。这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义

假定你想基于每个雇员离开公司的年份来分割表,也就是说,YEAR(separated)的值。实现这种分区模式的CREATE TABLE 语句的一个例子如下所示。

例如,你可能决定通过添加一个PARTITION BY RANGE子句把这个表分割成4个区间

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY RANGE (YEAR(separated)) (

    PARTITION p0 VALUES LESS THAN (1991),

    PARTITION p1 VALUES LESS THAN (1996),

    PARTITION p2 VALUES LESS THAN (2001),

    PARTITION p3 VALUES LESS THAN MAXVALUE

);


插入一些测试数据后发现P1的数据文件明显增大
mysql> DELIMITER $$

mysql> DROP PROCEDURE IF EXISTS SampleProc$$

Query OK, 0 rows affected (0.00 sec)

mysql> CREATE PROCEDURE SampleProc()

    -> BEGIN

    ->  DECLARE x INT;

    ->  SET x = 1000;

    ->  WHILE x<= 2000 DO

    ->          insert into employees(id,fname,lname,hired,separated,job_code,store_id) values(x,concat('firstname',x),concat('ai',x),'1994-01-01','1995-01-01',10,20);

    ->          SET  x = x + 1;

    ->  END WHILE;

    -> END$$

Query OK, 0 rows affected (0.00 sec)

mysql> call SampleProc() $$

Query OK, 1 row affected (22.55 sec)

mysql> delimiter ;



RANGE分区在如下场合特别有用:

· 当需要删除“旧的”数据时。如果你使用上面最近的那个例子给出的分区方案,你只需简单地使用 “ALTER TABLEemployees DROP PARTITION p0;”来删除所有在1991年前就已经停止工作的雇员相对应的所有行。(更多信息请参见13.1.2节,“ALTER TABLE语法18.3节,分区管理)。对于有大量行的表,这比运行一个如“DELETE FROM employees WHERE YEAR(separated) <= 1990;”这样的一个DELETE查询要有效得多。

· 想要使用一个包含有日期或时间&#20540;,或包含有从一些其他级数开始增长的&#20540;的列。

· 经常运行直接依赖于用于分割表的列的查询。例如,当执行一个如“SELECT COUNT(*) FROM employees WHERE YEAR(separated) = 2000 GROUP BYstore_id;”这样的查询时,MySQL可以很迅速地确定只有分区p2需要扫描,这是因为余下的分区不可能包含有符合该WHERE子句的任何记录。

· 分区表达式可以是MySQL 中有效的任何函数或其他表达式,只要它们返回一个既非常数、也非随机数的整数

1.2LIST分区

MySQL中的LIST分区在很多方面类&#20284;于RANGE分区。和按照RANGE分区一样,每个分区必须明确定义。它们的主要区别在于,LIST分区中每个分区的定义和选择是基于某列的&#20540;从属于一个&#20540;列表集中的一个&#20540;,而RANGE分区是从属于一个连续区间&#20540;的集合。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其中“expr” 是某列&#20540;或一个基于某个列&#20540;、并返回一个整数&#20540;的表达式,然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。

假定有20个音像店,分布在4个有经销权的地区,如下表所示:

地区

商店ID 号

北区

3, 5, 6, 9, 17

东区

1, 2, 10, 11, 19, 20

西区

4, 12, 13, 14, 18

中心区

7, 8, 15, 16

要按照属于同一个地区商店的行保存在同一个分区中的方式来分割表,可以使用下面的“CREATETABLE”语句

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY LIST(store_id)

    PARTITION pNorth VALUES IN (3,5,6,9,17),

    PARTITION pEast VALUES IN (1,2,10,11,19,20),

    PARTITION pWest VALUES IN (4,12,13,14,18),

    PARTITION pCentral VALUES IN (7,8,15,16)

);


相关的操作和range 分区类&#20284;,但有以下问题需要注意

· 在MySQL 5.1中,当使用LIST分区时,有可能只能匹配整数列表。

· 如果试图插入列&#20540;(或分区表达式的返回&#20540;)不在分区&#20540;列表中的一行时,那么“INSERT”查询将失败并报错。例如,假定LIST分区的采用上面的方案,下面的查询将失败:

· LIST分区没有类&#20284;如“VALUESLESS THAN MAXVALUE”这样的包含其他&#20540;在内的定义。将要匹配的任何&#20540;都必须在&#20540;列表中找到

1.3 HASH分区

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

例如,下面的语句创建了一个使用基于“store_id”列进行哈希处理的表,该表被分成了4个分区:

CREATE TABLE employees (

    id INT NOT NULL,

    fname VARCHAR(30),

    lname VARCHAR(30),

    hired DATE NOT NULL DEFAULT '1970-01-01',

    separated DATE NOT NULL DEFAULT '9999-12-31',

    job_code INT,

    store_id INT

)

PARTITION BY HASH(store_id)

PARTITIONS 4;



如果没有包括一个PARTITIONS子句,那么分区的数量将默认为1

1.4 KEY分区

KEY进行分区类&#20284;于按照HASH分区,除了HASH分区使用的用户定义的表达式,而KEY分区的 哈希函数是由MySQL 服务器提供。MySQL 簇(Cluster)使用函数MD5()来实现KEY分区;对于使用其他存储引擎的表,服务器使用其自己内部的 哈希函数,这些函数是基于与PASSWORD()一样的运算法则。

“CREATETABLE ... PARTITION BY KEY”的语法规则类&#20284;于创建一个通过HASH分区的表的规则。它们唯一的区别在于使用的关键字是KEY而不是HASH,并且KEY分区只采用一个或多个列名的一个列表。

通过线性KEY分割一个表也是可能的。下面是一个简单的例子:

CREATE TABLE tk (

    col1 INT NOT NULL,

    col2 CHAR(5),

    col3 DATE

) 

PARTITION BY LINEAR KEY (col1)

PARTITIONS 3;



在KEY分区中使用关键字LINEAR和在HASH分区中使用具有同样的作用,分区的编号是通过2的幂(powers-of-two)算法得到,而不是通过模数算法

1.5 更多的知识

上面列出的仅仅是常用的分区策略的用法,当创建了分区后往往还要对分区进行维护,具体请参见:

http://dev.mysql.com/doc/refman/5.1/zh/partitioning.html#partitioning-hash

无论使用何种类型的分区,分区总是在创建时就自动的顺序编号,且从0开始记录,记住这一点非常重要。当有一新行插入到一个分区表中时,就是使用这些分区编号来识别正确的分区。例如,如果你的表使用4个分区,那么这些分区就编号为0, 1, 2, 和3。对于RANGE和LIST分区类型,确认每个分区编号都定义了一个分区,很有必要。对HASH分区,使用的用户函数必须返回一个大于0的整数&#20540;。对于KEY分区,这个问题通过MySQL服务器内部使用的 哈希函数自动进行处理。

2 使用分区表的限制

· 一个数据表最多只能有1024个分区

· 在MYSQL5.1 版本中分区表达式的结果必须是整数,在MYSQL5.5分区表达式可以使用列

· 如果分区字段中有主键或者是唯一索引列,则所有的主键或者是唯一索引列必须全部包含进来

· 分区表无法使用外键

· 对于同一个表的各个分区表必须使用相同的存储引擎

· 分区函数有限制,只可以是MySQL 中有效的任何函数或其他表达式,且它们返回一个既非常数、也非随机数的整数

· 某些存储引擎不支持分区

3 使用分区表的“陷阱”

在数据量非常大的时候使用分区表可以使性能有较好的改善,但是前提是必须能有效的规避下面列出的一些陷阱

· NULL&#20540;

MySQL 中的分区在禁止空&#20540;(NULL)上没有进行处理,无论它是一个列&#20540;还是一个用户定义表达式的&#20540;。一般而言,对于NULL,或者是当表达式接收非法&#20540;时(e.g. YEAR(‘asdf-12-12’))返回的结果都是NULL,在这种情况下MySQL 把NULL视为0,如果大量的记录存在这种情况,最终会导致大量的记录都集中在一个分区中,也也就违背了分区的初衷。

如果你希望回避这种做法,你应该在设计表时不允许空&#20540;;最可能的方法是,通过声明列“NOT NULL”来实现这一点。

· 分区列和索引列不匹配

如果定义的索引列和分区列不匹配,则会导致查询无法进行分区过滤。例如在列a上定义分区,在列b上定义索引,因为每个分区都有独立的索引,所以扫描索引时需要扫描每个分区。

应该避免建立和分区列不匹配的索引,除非查询中包含了可以过滤分区的条件。

· 选择分区的成本很高

对于Range 和list类型的分区,每次进行操作时都需要遍历所有的分区条件,以判断相关的记录是属于哪个分区,如果分区的数量很多,会在选择分区上浪费较多的资源

为了避免这种情况 可以限制分区的数目(<100),或是选择hash分区

· 锁住底层表的成本很高

在查询访问分区表的时候,MYSQL会打开并锁住所有的底层表,该操作时再分区过滤之前发生而且和分区类型无关,会影响所有的分区查询。

可以通过批量更新的方式来降低该操作的次数,同时也需要限制分区的数目

· 维护成本高

增加/删除分区很快捷,但是重组或者是alter分区的过程类&#20284;于alter table,会进行大量的数据复制操作,效率很低。

4 优化查询语句

对于分区表的访问,最重要的一点是要在where条件中包含分区列,即使看起来是多余的,只有这样才能过滤不需要的分区,否则会访问所以的分区表。

看一个简单的例子。

Employees表使用store_id作为范围分区的条件,如果不使用store_id作为where条件,会查询所有的分区


Store_id作为where条件时,只查询对应的分区

关于where条件中的表达式有几点需要注意

· 单纯的使用分区列

Where条件中分区列必须是未经函数处理的,如果where条件写成where YEAR(store_id), 则分区过滤会失效,且查询时会检查所有的分区。这一点和索引类&#20284;

· 关联查询

如果分区表是关联操作的第二张表,且关联条件是分区建,则MYSQL只会在对应的分区里进行匹配

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
author-avatar
潜水的飞机537
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有