0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MySQL分区表的类型及原理

jf_78858299 来源:基础技术研究 作者:王晓华 2023-03-29 13:58 次阅读

分区是将一个表的数据按照某种方式,比如按照月、天或者其他方式,分成多个较小的、更容易管理的部分,也就是物理存储根据一定规则放在不同文件中,但是逻辑上所有的数据仍在一个表中。如下图所示:

图片

MySQL实现分区表的方式是对底层表的封装,意味着索引也是按照分区的子表定义的,没有全局索引。这和Oracle不同,在Oracle中可以更加灵活的定义索引和表是否进行分区。

分区表的实现原理

分区表由多个相关的底层表实现,这些底层表也是由句柄对象表示,因此也可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样,所有的底层表都必须使用相同的存储引擎,分区表的索引只是在各个底层表各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和一个普通表没有任何不同,存储引擎也无需知道这是一个普通表还是一个分区表的一部分。

那么在分区表上的操作是怎样进行的呢?其实常规的CRUD操作以及返回结果和普通表没有任何区别。具体分区层实现是先打开并锁定所有底层表,优化器先判断是否可以过滤部分分区,然后调用对应的存储引擎接口访问各个分区的数据进行相应的操作。

分区表的分区类型

分区表的类型主要包括RANGE、LIST、HASH、KEY四种,另外还有一种COLUMNS分区类型,因不经常用,本文不做介绍。

  • RANGE分区

给定一个连续的区间范围(区间要求连续并且不能重叠),某个字段的值满足这个范围就会被分配到该分区。适用于字段的值是连续的字段,比如日期范围, 连续的数字等。

示例代码:

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 NOT NULL,
    store_id INT NOT NULL
)
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
)

该示例中根据每个员工离开公司的年份进行划分,对于1991年之前离职员工存储在分区p0中,1991年至1995年离职的人存储在分区p1,1996年至2000年离职的人存储在分区p2中。

如果没有创建LESS THAN MAXVALUE分区,那么可能存在插入超过2000年离职的人的数据丢失不存储,因此RANGE分区为了防止丢数据会加入该分区。MAXVALUE表示一个始终大于最大可能整数值的整数值,因此2001年以后离开的所有人存储在分区p3中。

若设置了 LESS THAN MAXVALUE分区,添加新分区时需要重新分区,此时存储在LESS THAN MAXVALUE分区中的数据会根据重新分区规则进行数据重新分配,如下所示:

ALTER TABLE employees  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 (2006),
  PARTITION p4 VALUES LESS THAN MAXVALUE
);

对于日期类型的字段,也可以使用UNIX_TIMESTAMP()函数,根据TIMESTAMP列的值按RANGE对表进行分区,如下例所示:

CREATE TABLE quarterly_report_status (
    report_id INT NOT NULL,
    report_status VARCHAR(20) NOT NULL,
    report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (
    PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-01-01 00:00:00') ),
    PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-04-01 00:00:00') ),
    PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2020-07-01 00:00:00') ),
    PARTITION p3 VALUES LESS THAN (MAXVALUE)
);
  • LIST分区

和RANGE分区方式相似, LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择分区。LIST分区没有类似RANGE分区中“VALUES LESS THAN MAXVALUE”包含其他值在内的定义,其要匹配的任何值都必须在值列表中找到,如果不在列表中的数据插入表中操作会失败。

示例代码:

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)
 );
  • HASH分区

基于用户定义表达式的返回值来进行分区,该表达式使用将要插入到表中的这些行的列值进行计算。

MySQL支持两种HASH分区,常规HASH(HASH)分区和线性HASH(LINEARHASH) 分区。常规HASH分区使用取模算法,对应一个表达式expr可以计算出它被保存到哪个分区中,N = MOD(expr, num)。线性HASH分区使用一个线性的2的幂运算法则,V = POWER(2, CEILING(LOG(2,num)))。

常规HASH示例代码:

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( YEAR(hired) )
PARTITIONS 4;

常规HASH分区非常简便,使用HASH函数值的模数,可以让数据平均的分布到每一个分区,但是由于分区在创建表的时候已经固定了,如果新增或者收缩分区的数据迁移比较大。

因此MySQL还支持线性HASH分区,线性HASH分区采用线性二乘幂算法,可以增加分区维护(增加、删除、合并、拆分分区)时数据迁移和处理速度,但是也会存在各个分区之间数据的分布不太均衡的情况。

语法上线性HASH分区和HASH分区之间的唯一区别是在PARTITION BY子句中添加了LINEAR关键字。如下所示:

线性HASH示例代码:

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 LINEAR HASH( YEAR(hired) )
PARTITIONS 6;

线性HASH分区的计算原理及步骤如下:

  1. V = POWER(2,CEILING(LOG(2, num))),其中:
  • num为分区的数量。
  • LOG(2, num) 代表计算num以2为底的对数。
  • CEILING() 代表向上取整。
  • POWER(2, x) 代表取2的x幂次。

如果num的值是2的幂数,那么这个表达式计算出来的结果不变。

假设num为13。则LOG(2,13)为3.7004397181411。CEILING(3.7004397181411)为4,而V = POWER(2,4)为16。

  1. Set N =F(column_list) & (V - 1)。
  • N代表计算出来的数据所在分区编号。
  • F代表对分区键进行的操作,该操作返回一个整数值。
  • &代表位与运算。

当num是2的倍数时由于V计算出来的结果不变,这个时候线性HASH算法的计算结果F(column_list)&(V-1)=MOD(F(column_list)/num)和常规HASH取模算出的结果是一致的。

  1. While N>= num:
    Set V = V / 2
    
     Set N = N & (V - 1)
    

特别的,如果步骤2中计算出来的N比总分区数量num大,则需要执行本步骤操作,直到N

接下来用一个示例解释下线性HASH算法找具体分区号的实现原理。创建一个有六个分区的线性HASH分区表,如下所示:

CREATE TABLE t1(
   Col1  INT ,
   Col2  CHAR(5) ,
   Col3  DATE 
)
PARTITION BY LINEAR HASH( YEAR(col3) )
PARTITIONS 6;

现在给该表中插入两条记录,Col3的值分别为'2003-04-14'和'1998-10-19',其中上文中的F()在这里对应YEAR(),那么根据算法原理可以确定第一个分区号如下所示:

V = POWER(2, CEILING(LOG(2,6) )) = 8

N = YEAR('2003-04-14')& (8 - 1)

= 2003 & 7

= 3

判断3>=6不成立,所以存储在第3号分区,注意这里的3指的是P3,分区号是从P0开始。

当插入的值是'1998-10-19'计算所存储的分区号如下所示:

V = 8

N = YEAR('1998-10-19')& (8 - 1)

= 1998 & 7

= 6

判断6>=6成立,所以需要做下一步运算。

N = 6 & ((8 / 2) -1)

= 6 & 3

= 2

判断2>=6不成立,所以存储在第2号分区,同理这里的2指的是P2分区。

下面是用EXCEL做的一个简单的测试,测算了一下MySQL使用线性HASH分区算法,将分区键值取(1-1048575),分区数量分别为4、5、6、7、8个时,各分区数据数量如图所示:

图片

从图中我们可以看到官网说它是“线性”,但是又可能不太平均,是比较严谨的。从算法角度考虑,在分区键值平均分布的前提下,为了各分区数据量尽量平均,线性HASH推荐分区数量尽量为2的幂次,比如2,4,8,16。如果不能保证,则应尽量让LOG(2, num)越接近于某个2的幂次。

常规HASH和线性HASH增加和收缩分区原理是一样的。增加和收缩分区后原来的数据会根据现有的分区数量重新分布。HASH分区不能删除分区,所以不能使用DROP PARTITION操作进行分区删除操作。可以通过ALTER TABLE ... COALESCE PARTITION num合并分区,这里的num是减去的分区数量。可以通过ALTERTABLE ... ADD PARTITION PARTITIONS num来增加分区,这里是num是增加的分区数量。

  • KEY分区

和HASH分区类似,但是KEY分区不允许使用自定义的表达式,需要使用MySQL Server提供的HASH函数。

MYSQL支持两种KEY分区, 常规KEY(KEY)分区和线性KEY(LINEARKEY) 分区,其中计算区别和HASH分区一样。

常规KEY示例代码:

CREATE TABLE tm1 (
    s1 CHAR(32) PRIMARY KEY
)
PARTITION BY KEY(s1)
PARTITIONS 10;

线性KEY示例代码:

CREATE TABLE tk (
    col1 INT NOT NULL,
    col2 CHAR(5),
    col3 DATE
)
PARTITION BY LINEAR KEY (col1)
PARTITIONS 3;

分区键的注意事项

无论采用哪种分区,要么分区表上没有主键/唯一键,要么分区表的主键/唯一键都必须包含分区键,即不能使用主键/唯一键字段之外的其它字段分区。

针对只包含唯一键的表给出示例分析如下:

下面的三个例子是不正确的:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2)
)
PARTITION BY HASH(col3)
PARTITIONS 4;


CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1),
    UNIQUE KEY (col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;


CREATE TABLE t3 (
         col1 INT NOT NULL,
         col2 DATE NOT NULL,
         col3 INT NOT NULL,
         col4 INT NOT NULL,
         UNIQUE KEY (col1, col2),
         UNIQUE KEY (col3)
 )
 PARTITION BY HASH(col1 + col3)
 PARTITIONS 4;

在以上三个示例中的表都有至少一个唯一键,但是在唯一键中不包括分区表达式中使用的所有列。因此在创建的时候就会报错:ERROR 1491 (HY000): A PRIMARY KEY must include all columns in thetable's partitioning function。

以下三个示例是正确的:

CREATE TABLE t1 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col2, col3)
)
PARTITION BY HASH(col3)
PARTITIONS 4;


CREATE TABLE t2 (
    col1 INT NOT NULL,
    col2 DATE NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3)
)
PARTITION BY HASH(col1 + col3)
PARTITIONS 4;


CREATE TABLE t3 (
      col1 INT NOT NULL,
      col2 DATE NOT NULL,
      col3 INT NOT NULL,
      col4 INT NOT NULL,
      UNIQUE KEY (col1, col2, col3),
      UNIQUE KEY (col3)
 )
PARTITION BY HASH(col3)
PARTITIONS 4;

在以上三个示例中表的分区键都是所属表的所有唯一键包含的字段,因此可以成功创建。

CREATE TABLE t4 (
    col1 INT NOT NULL,
    col2 INT NOT NULL,
    col3 INT NOT NULL,
    col4 INT NOT NULL,
    UNIQUE KEY (col1, col3),
    UNIQUE KEY (col2, col4)
);

在上面这个示例表是没有办法分区的,因为无法在分区键中包含属于两个唯一键的任何列。

关于主键和唯一键是一样的,这里不重复做示例分析,秉承一个原则:主键/唯一键必须包含分区键的所有列。

分区表优缺点

大多数互联网公司都不建议用MySQL分区表,虽然它有不少好处,但是不足之处也同样很多。接下来讲解下对应的优缺点。

优点

  • 分区表对业务透明,只需要维护一个表的数据结构,DML操作和普通表没有区别。
  • DML操作加锁仅影响操作的分区,不会影响未访问分区。
  • 通过TRUNCATE操作可以快速清理特定分区数据,通过DROP操作也可以直接删除掉不需要的分区和对应的数据。
  • 通过大数据量分区能有效降低索引层数,提高查询性能。

缺点:

  • DDL操作需要锁定所有分区,导致所有分区上操作都被阻塞。
  • 当表数据量较小时,分区表和非分区表性能相近,分区表效果有限。
  • 当表数据量较大时,对分区表进行DDL或其他运维操作难度大、风险高。
  • 分区表在行业内使用较少,社区资料有限,存在未知风险多。
  • 当单台服务器性能无法满足时,对分区表进行分拆的成本较高。
  • 当分区表操作不当导致访问所有分区时,会导致严重的性能问题。
  • 使用分库分表可以有效降低运维操作影响,对1亿数据量表做DDL操作需要谨慎评估,而对10万数据量表做DDL操作可以默认其很快完成。
  • 使用分库分表可以有效减小宕机或其他故障影响,将数据分库分表到10套群集上,一套群集发生故障仅影响一部分的业务。
  • MySQL不支持自动分区扩展,需要手动新增分区并进行数据再均衡。

总 结:

对于上亿行或者更大数量的普通表清理,只能采用DELETE的方式,该方式不但性能差,而且无法真正回收空间。分区表可以通过删除分区等方式,对历史数据进行清理的同时数据文件也做了回收,真正释放了空间、而且效率很高。

很多互联网公司不建议用MySQL分区表,那么我个人的见解是:

对于特定场景是可以考虑采用分区表,如历史数据有明确的分区范围,访问不垮分区,极少的变化操作,查询语句逻辑简单,无性能瓶颈。

对于Oracle这些商业数据库,由于商业授权导致横向扩展成本较高,且分区表功能稳定,因此通过硬件扩展和分区来承担大数据量带来的负载。

对于MySQL开源数据库,企业有资源有能力将很多需求迁移到数据库外通过代码逻辑或者其它替代方式实现,因此更追求MySQL使用过程中的简单、稳定、可靠,且通过增加服务器以及分库分表更能有效处理数据量爆炸式增长带来的性能问题。

因此个人不建议大量使用MySQL分区表,尤其是在重要的业务上。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储
    +关注

    关注

    13

    文章

    4209

    浏览量

    85551
  • 分区表
    +关注

    关注

    0

    文章

    3

    浏览量

    6419
  • MySQL
    +关注

    关注

    1

    文章

    795

    浏览量

    26385
收藏 人收藏

    评论

    相关推荐

    硬盘分区表错误与解决办法

    ,则会显示“Hard Disk Partition table - OK ”信息,否则会依据分区表故障类型给出相应的信息,这时可以按下“F6”按钮查看硬盘分区表,或者直接按下“Y”按钮进行引导扇区的修复
    发表于 02-26 15:56

    关于Linux nandflash分区表的疑问?

    Linux的分区表在那个文件.移植的时候根据不同的处理器会用不用的文件分区表吗?另外uboot里面也有分区表.在那个文件.不同版本的uboot怎么找到分区表文件.感觉
    发表于 09-17 19:47

    uboot源码怎么查看spiflash分区表

    请问 uboot 源码怎么查看 spiflash 分区表在那个工程文件里面?因为我换了 spiflash
    发表于 06-21 07:27

    MySQL分区类型及介绍

    MySQL5.7社区版本的分区功能是默认开启的。但当中含有主键或唯一键时,则每个被用作分区函数的字段必须是
    发表于 06-29 16:31

    详解Hive分区表

    黑猴子的家:Hive 分区表基本操作
    发表于 05-16 10:47

    关于MySQL分区和分的详细介绍

    MySQL分区和分
    发表于 07-10 07:40

    怎样去修改分区表参数文件呢

    怎样去创建可以修改分区日期数据备份呢?怎样去修改分区表参数文件呢?
    发表于 03-04 07:13

    android8.1 virtualBox 虚拟机 Ubuntu 18.04是怎样修改分区表

    android8.1 virtualBox 虚拟机 Ubuntu 18.04是怎样修改分区表的?android8.1 virtualBox 虚拟机 Ubuntu 18.04是怎样添加分区文件静态信息的?
    发表于 03-07 06:40

    Rockchip使用GPT作为其主要分区表资料推荐

    1、Rockchip 固件分区排列在开放源代码支持中,Rockchip使用 GPT作为其主要分区表。我们将GPT存储在LBA0〜LBA63中。U-boot支持标准的 GPT分区表格式。注1:如果
    发表于 04-20 16:53

    瑞芯微在开源支持中使用GPT作为其主要分区表

    介绍瑞芯微在开源支持中使用GPT作为其主要分区表。我们将 GPT 存储在 LBA0 ~ LBA63。U-boot中的GPT分区表U-boot 支持标准 GPT 分区表格式。默认存储映射注1:如果
    发表于 04-21 14:48

    求助,默认分区表中关于app分区大小的理解

    请问默认分区表文件中指示app分配了1M,是不是意味着芯片存储空间中,出去bootloader和分区表存储空间外,其它位置都是空余着可以被分配过来存储数据?
    发表于 02-10 08:44

    默认分区表中关于app分区大小的问题求解

    请问默认分区表文件中指示app分配了1M,是不是意味着芯片存储空间中,出去bootloader和分区表存储空间外,其它位置都是空余着可以被分配过来存储数据?
    发表于 03-03 08:16

    零磁道和分区表修复是什么意思

    零磁道和分区表修复是什么意思 零磁道的修复   零磁道处于硬盘上一个非常重要的位置,硬盘的主
    发表于 03-29 10:04 884次阅读

    硬盘分区表破坏,所有盘符或部分盘符丢失故障

    硬盘分区表破坏,所有盘符或部分盘符丢失故障 首先使用“FDISK/MBR”重建主引导区,再根据情况修正分区表。修正分区表的基本思路是查找以55AA为结束的扇区,再
    发表于 01-11 11:26 1050次阅读

    mysql中表分区的备份与恢复

    MySQL分区是一种将大型分成更小段的技术,这样可以提高查询效率、降低维护成本和减少数据备份恢复时间。在进行
    的头像 发表于 11-23 14:39 1018次阅读