MySQL 分区表介绍与测试

一、基本介绍:

分区表是一个独立的逻辑表,底层由多个物理的文件组成,分区对于 SQL 层来说是透明的;

下图可以看出,表 em_elec_meter_data 的多个分区是使用了 # 进行标识。

image-20220428154613336

分区表的使用场景:

  • 表非常大以至于无法全部都放在内存中,或者热点数据只有在表的最后部分,其他都是历史数据。
  • 分区表的数据作用在不同的设备上,高效地利用多个硬件设备;例如有 1TB 的数据,服务器的硬盘容量只有 500 GB,那么可以使用其他设备平摊未能放下的数据。
  • 需要独立备份和恢复分区,这在非常大数据集的场景下效果非常好。

分区表的限制:

  • 一个表最多只能有 1024 个分区。
  • MySQL 5.1 中,分区表表达式必须是整数,或者返回整数表达式。在 MySQL 5.5 中,某些场景中可以直接使用列来进行分区。
  • 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须组合成一个主键;原因是防止同一个主键一个在 p0 区,一个在 p1 区。
  • 分区表中无法使用外键约束(现在也没人使用外键,都是程序保证约束)。
  • 在 MySQL 8 中不支持 myIsam 存储引擎使用分区表。

分区表的类型

  • 范围(range)分区,例如某年某月、不同省份的区域范围(常用)

    注:每个分区都是按顺序进行定义,从最低到最高,例如以下的 2023 不能在 2022 的前面,这是 PARTITION BY RANGE 语法的要求

    # 按照范围分区的表
    create table `range_partition1` (
      `id` bigint not null auto_increment,
      `date_time` datetime not null,
      `range_name` varchar(50) default null,
      primary key (`id`,
    `date_time`)
    ) ENGINE = InnoDB default CHARSET = utf8mb4 collate = utf8mb4_0900_ai_ci partition by range(year(date_time))
    (
        -- 必须按照顺序定义,否则报错
    	partition p0 values less than (2022),
    	partition p1 values less than (2023)
    )
    
  • 哈希(hash)分区将数据均匀的分布到预先定义的各个分区中,使用 partition by hash(expr) 指定为 hash 分区,需要注意 expr 必须返回整数或者指定的列为 MySQL 的整型。

    CREATE TABLE `hash_partition` (
      `id` bigint NOT NULL AUTO_INCREMENT,
      `date_time` datetime NOT NULL,
      `range_name` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id`,`date_time`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    partition by hash(id)
    partitions 10;
    

    partitions 10 的意思为表要被切分成 10 个分区

    image-20220505114704287

  • 键值(key)分区,KEY分区和HASH分区相似,不同之处在于 HASH 分区使用用户定义的函数进行分区,KEY 分区使用数据库提供的函数进行分区。

    CREATE TABLE `key_partition` (
      `id` bigint NOT NULL AUTO_INCREMENT,
      `date_time` datetime NOT NULL,
      `range_name` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id`,`date_time`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    partition by linear key(id)
    partitions 10;
    
  • 列表(list)分区,根据给定的 list 进行分区;以下例子为按照月份进行春夏秋冬四季的分区

    CREATE TABLE `list_partition` (
      `id` bigint NOT NULL AUTO_INCREMENT,
      `date_time` datetime NOT NULL,
      `range_name` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id`,`date_time`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
    partition by list(month(date_time))
    (
    	partition spring values in (3, 4, 5),
    	partition summer values in (6, 7, 8),
    	partition autumn values in (9, 10, 11),
    	partition winter values in (12, 1, 2)
    )
    
二、分区表的优缺点

优点:

  • 提升查询性能,对于分区表的查询操作,如果查询条件中包含分区键,则这个查询操作就只会被下推到符合条件的分区内进行,无关分区将自动过滤掉。
  • 对业务透明,将表从一个非分区表转换为分区表,业务端无需做任何改造。在开发看来,仍然是同一张表。
  • 维护方便,在对单个分区进行删除、迁移和维护时,不会影响到其它分区。
  • DML 操作加锁仅影响操作的分区,不会影响未访问分区。

缺点:

  • DDL 操作需要锁定所有分区,导致所有分区上操作都被阻塞。
  • 当表数据量较小时,分区表和非分区表性能相近,分区表效果有限。
  • 当表数据量较大时,对分区表进行 DDL 或其他运维操作难度大风险高。
  • 分区表使用较少,存在未知风险多,BUG多,BUG多,BUG多,MySQL社区版本免费,横向扩展成本低,分库分表实现简单且中间件完善。
  • 当单台服务器性能无法满足时,对分区表进行分拆的成本较高,而分库分表能很容易实现横向分拆。
  • 当分区表操作不当导致访问所有分区时,会导致严重的性能问题,而分库分表操作不当仅影响访问的表。
  • 使用分库分表可以有效运维降低运维操作影响,对1亿数据量表做 DDL 操作需要谨慎评估,而对 10 万数据量表做 DDL 操作可以默认其很快完成。
  • 使用分库分表可以有效减小宕机或其他故障影响,将数据分库分表到10套群集上,一套群集发生故障仅影响业务的一成。
三、测试:

新建表 em_elec_meter_data,按月份从 2019 年到 2023 年对表进行分区划分。

CREATE TABLE `em_elec_meter_data` (
  `id` bigint NOT NULL COMMENT '表主键',
  `date_time` datetime NOT NULL COMMENT '时间。',
  `create_time` datetime DEFAULT NULL COMMENT '改记录生成的时间',
  PRIMARY KEY (`id`,`date_time`),
  UNIQUE KEY `IDX_MIDDATE` (`meter_id`,`date_time`),
  KEY `IDX_DATETIME` (`date_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT=''
/*!50100 PARTITION BY RANGE (to_days(`date_time`))
(PARTITION md201901 VALUES LESS THAN (737456) ENGINE = InnoDB,
略.....
 PARTITION md202312 VALUES LESS THAN (739251) ENGINE = InnoDB) */

向表插入数据, em_elec_meter_data 有大概 7 千万条数据,下面演示使用分区与不使用分区的差别

image-20220505143837875

首先看不使用分区,执行以下 SQL,实测耗时 1 min 7 s

select
	sql_no_cache /* 为了更真实,不使用缓存 */
	*
from
	test.em_elec_meter_data
where
	dya = 10086

image-20220505145643703

查看它的执行计划,需要查找所有的分区,大概扫描 66184007 行记录才找到 dya = 10086 这条记录

image-20220505145742017

添加分区字段过滤,执行以下 SQL,实测耗时 1ms,性能提高约 67000 倍

select
	sql_no_cache /* 为了更真实,不使用缓存 */
	*
from
	test.em_elec_meter_data
where
	dya = 10086
	and date_time = '2019-01-01 13:56:59';

image-20220505145939778

再看看执行计划,只需查找 md201901 这个分区,且用到了 date_time索引

image-20220505150117200

参考:

高性能 MySQL

https://www.cnblogs.com/gaogao67/p/11013244.html?ivk_sa=1024320u

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值