Day907.分区表 -MySQL实战

news2024/11/20 22:39:10

分区表

Hi,我是阿昌,今天学习记录的是关于分区表的内容。

经常被问到这样一个问题:

分区表有什么问题,为什么公司规范不让使用分区表呢?


一、分区表是什么?

为了说明分区表的组织形式,先创建一个表 t:

CREATE TABLE `t` (
  `ftime` datetime NOT NULL,
  `c` int(11) DEFAULT NULL,
  KEY (`ftime`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
PARTITION BY RANGE (YEAR(ftime))
(PARTITION p_2017 VALUES LESS THAN (2017) ENGINE = InnoDB,
 PARTITION p_2018 VALUES LESS THAN (2018) ENGINE = InnoDB,
 PARTITION p_2019 VALUES LESS THAN (2019) ENGINE = InnoDB,
PARTITION p_others VALUES LESS THAN MAXVALUE ENGINE = InnoDB);
insert into t values('2017-4-1',1),('2018-4-1',1);

图 1 表 t 的磁盘文件
在表 t 中初始化插入了两行记录,按照定义的分区规则,这两行记录分别落在 p_2018 和 p_2019 这两个分区上。

可以看到,这个表包含了一个.frm 文件和 4 个.ibd 文件,每个分区对应一个.ibd 文件。

也就是说:

  • 对于引擎层来说,这是 4 个表;
  • 对于 Server 层来说,这是 1 个表。

可能会觉得这两句都是废话。

其实不然,这两句话非常重要,可以理解分区表的执行逻辑。


二、分区表的引擎层行为

先给举个在分区表加间隙锁的例子,目的是说明对于 InnoDB 来说,这是 4 个表。

图 2 分区表间隙锁示例

这里顺便回忆一下,在加锁规则的一些问题介绍的间隙锁加锁规则。

初始化表 t 的时候,只插入了两行数据, ftime 的值分别是,‘2017-4-1’ 和’2018-4-1’ 。

session A 的 select 语句对索引 ftime 上这两个记录之间的间隙加了锁。

如果是一个普通表的话,那么 T1 时刻,在表 t 的 ftime 索引上,间隙和加锁状态应该是图 3 这样的。

图 3 普通表的加锁范围

也就是说,‘2017-4-1’ 和’2018-4-1’ 这两个记录之间的间隙是会被锁住的。那么,sesion B 的两条插入语句应该都要进入锁等待状态。但是,从上面的实验效果可以看出,session B 的第一个 insert 语句是可以执行成功的。

这是因为,对于引擎来说,p_2018 和 p_2019 是两个不同的表,也就是说 2017-4-1 的下一个记录并不是 2018-4-1,而是 p_2018 分区的 supremum。

所以 T1 时刻,在表 t 的 ftime 索引上,间隙和加锁的状态其实是图 4 这样的:

图 4 分区表 t 的加锁范围
由于分区表的规则,session A 的 select 语句其实只操作了分区 p_2018,因此加锁范围就是图 4 中深绿色的部分。

所以,session B 要写入一行 ftime 是 2018-2-1 的时候是可以成功的,而要写入 2017-12-1 这个记录,就要等 session A 的间隙锁。

图 5 就是这时候的 show engine innodb status 的部分结果。

图 5 session B 被锁住信息


看完 InnoDB 引擎的例子,再来一个 MyISAM 分区表的例子。

首先用 alter table t engine=myisam,把表 t 改成 MyISAM 表;然后,再用下面这个例子说明,对于 MyISAM 引擎来说,这是 4 个表。

图 6 用 MyISAM 表锁验证
在 session A 里面,我用 sleep(100) 将这条语句的执行时间设置为 100 秒。由于 MyISAM 引擎只支持表锁,所以这条 update 语句会锁住整个表 t 上的读。但看到的结果是,session B 的第一条查询语句是可以正常执行的,第二条语句才进入锁等待状态。

这正是因为 MyISAM 的表锁是在引擎层实现的,session A 加的表锁,其实是锁在分区 p_2018 上。

因此,只会堵住在这个分区上执行的查询,落到其他分区的查询是不受影响的。

可能会说,分区表看来还不错嘛,为什么不让用呢?

使用分区表的一个重要原因就是单表过大。那么,如果不使用分区表的话,就是要使用手动分表的方式。


接下来,一起看看手动分表和分区表有什么区别。

比如,按照年份来划分,就分别创建普通表 t_2017、t_2018、t_2019 等等。

手工分表的逻辑,也是找到需要更新的所有分表,然后依次执行更新。在性能上,这和分区表并没有实质的差别。

分区表和手工分表,一个是由 server 层来决定使用哪个分区,一个是由应用层代码来决定使用哪个分表。

因此,从引擎层看,这两种方式也是没有差别的。其实这两个方案的区别,主要是在 server 层上。

从 server 层看,我们就不得不提到分区表一个被广为诟病的问题:打开表的行为。


三、分区策略

每当第一次访问一个分区表的时候,MySQL 需要把所有的分区都访问一遍

一个典型的报错情况是这样的:如果一个分区表的分区很多,比如超过了 1000 个,而 MySQL 启动的时候,open_files_limit 参数使用的是默认值 1024,那么就会在访问这个表的时候,由于需要打开所有的文件,导致打开表文件的个数超过了上限而报错。

下图就是创建的一个包含了很多分区的表 t_myisam,执行一条插入语句后报错的情况。

图 7 insert 语句报错

可以看到,这条 insert 语句,明显只需要访问一个分区,但语句却无法执行。

这时,你一定从表名猜到了,这个表用的是 MyISAM 引擎。是的,因为使用 InnoDB 引擎的话,并不会出现这个问题。MyISAM 分区表使用的分区策略,我们称为通用分区策略(generic partitioning),每次访问分区都由 server 层控制。

通用分区策略,是 MySQL 一开始支持分区表的时候就存在的代码,在文件管理、表管理的实现上很粗糙,因此有比较严重的性能问题。

从 MySQL 5.7.9 开始,InnoDB 引擎引入了本地分区策略(native partitioning)。这个策略是在 InnoDB 内部自己管理打开分区的行为。MySQL 从 5.7.17 开始,将 MyISAM 分区表标记为即将弃用 (deprecated),意思是“从这个版本开始不建议这么使用,请使用替代方案。

在将来的版本中会废弃这个功能”。从 MySQL 8.0 版本开始,就不允许创建 MyISAM 分区表了,只允许创建已经实现了本地分区策略的引擎。

目前来看,只有 InnoDB 和 NDB 这两个引擎支持了本地分区策略。


四、分区表的 server 层行为

接下来,再看一下分区表在 server 层的行为。

如果从 server 层看的话,一个分区表就只是一个表。

这句话是什么意思呢?接下来,就用下面这个例子来和你说明。

如图 8 和图 9 所示,分别是这个例子的操作序列和执行结果图。

图 8 分区表的 MDL 锁
图 9 show processlist 结果

可以看到,虽然 session B 只需要操作 p_2017 这个分区,但是由于 session A 持有整个表 t 的 MDL 锁,就导致了 session B 的 alter 语句被堵住。

DBA 同学经常说的,分区表,在做 DDL 的时候,影响会更大。

如果使用的是普通分表,那么当在 truncate 一个分表的时候,肯定不会跟另外一个分表上的查询语句,出现 MDL 锁冲突。

到这里小结一下:

  1. MySQL 在第一次打开分区表的时候,需要访问所有的分区;
  2. 在 server 层,认为这是同一张表,因此所有分区共用同一个 MDL 锁;
  3. 在引擎层,认为这是不同的表,因此 MDL 锁之后的执行过程,会根据分区表规则,只访问必要的分区。

而关于“必要的分区”的判断,就是根据 SQL 语句中的 where 条件,结合分区规则来实现的。


比如上面的例子中,where ftime=‘2018-4-1’,根据分区规则 year 函数算出来的值是 2018,那么就会落在 p_2019 这个分区。但是,如果这个 where 条件改成 where ftime>=‘2018-4-1’,虽然查询结果相同,但是这时候根据 where 条件,就要访问 p_2019 和 p_others 这两个分区。如果查询语句的 where 条件中没有分区 key,那就只能访问所有分区了。

当然,这并不是分区表的问题。即使是使用业务分表的方式,where 条件中没有使用分表的 key,也必须访问所有的分表。

那么什么场景下适合使用分区表呢?


五、分区表的应用场景

分区表的一个显而易见的优势是对业务透明,相对于用户分表来说,使用分区表的业务代码更简洁。

还有,分区表可以很方便的清理历史数据。如果一项业务跑的时间足够长,往往就会有根据时间删除历史数据的需求。

这时候,按照时间分区的分区表,就可以直接通过 alter table t drop partition … 这个语法删掉分区,从而删掉过期的历史数据。

这个 alter table t drop partition … 操作是直接删除分区文件,效果跟 drop 普通表类似。

与使用 delete 语句删除数据相比,优势是速度快、对系统影响小。


六、总结

需要注意的是,我是以范围分区(range)为例和你介绍的。实际上,MySQL 还支持 hash 分区、list 分区等分区方法。可以在需要用到的时候,再翻翻手册。实际使用时,分区表跟用户分表比起来,有两个绕不开的问题:一个是第一次访问的时候需要访问所有分区,另一个是共用 MDL 锁。因此,如果要使用分区表,就不要创建太多的分区。见过一个用户做了按天分区策略,然后预先创建了 10 年的分区。

这种情况下,访问分区表的性能自然是不好的。这里有两个问题需要注意:

  1. 分区并不是越细越好。实际上,单表或者单分区的数据一千万行,只要没有特别大的索引,对于现在的硬件能力来说都已经是小表了。
  2. 分区也不要提前预留太多,在使用之前预先创建即可。比如,如果是按月分区,每年年底时再把下一年度的 12 个新分区创建上即可。对于没有数据的历史分区,要及时的 drop 掉。

至于分区表的其他问题,比如查询需要跨多个分区取数据,查询性能就会比较慢,基本上就不是分区表本身的问题,而是数据量的问题或者说是使用方式的问题了。

当然,如果你的团队已经维护了成熟的分库分表中间件,用业务分表,对业务开发同学没有额外的复杂性,对 DBA 也更直观,自然是更好的。


举例的表中没有用到自增主键,假设现在要创建一个自增字段 id。
MySQL 要求分区表中的主键必须包含分区字段。
如果要在表 t 的基础上做修改,会怎么定义这个表的主键呢?为什么这么定义呢?

如果不包含分区字段,那么对于主键和唯一键都可能冲突。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/387900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 数据库如何更新、删除、执行?这里实例告诉你

人生苦短 我用Python 这次继续给大家带来数据库的相关操作 Python 安装包其他数据:点击此处跳转文末名片获取 数据库更新操作 更新操作用于更新数据表的的数据, 以下实例将 EMPLOYEE 表中的 SEX 字段为 M的 AGE 字段递增 1: #!/usr/bin/python # -*- …

ubuntu-9-安装chrony时间同步

使用chrony搭建时间同步服务器 [Linux系列]Chrony时间同步服务器 配置chrony服务,实现服务器时间自动同步 linux上内网环境配置NTP时间同步详解 经验体会:解决Ubuntu 18.04Windows双系统时间不同步的问题 1 时间同步 我们知道一台电脑主机,…

将微信小程序页面转为图片

最近做项目遇到一个需求,那就是要将某个页面转为图片然后传给后端,我仔细找了一圈,发现官方那个Api也就是wx.canvasToTempFilePath生成的图片很有可能为空,太坑了,于是我放弃用它了,选择了用wxml2canvas。 安装wxml2canvas npm init npm install wxml2canvas --save --…

通用游戏地图解决方案设计解析

前言: 在软件开发过程中,我们都希望能设计出一个稳健的,可维护的系统,为了实现这个目的,人们总结出了很多相关的设计原则,比如SOLID原则, KISS原则等等。SOLID每个字母代表了一种设计原则&…

数据是如何在计算机中存储的

我们普通人对于数据存储的认识恐怕大多数都是从自己使用的电脑来的。现在几乎人手一台电脑,而我们的电脑存储着各种各样的文件,比如视频文件、音频文件和Word文档等。这些文件从计算机术语的角度都可以称为数据。 如图1-1所示是Windows 10 “我的电脑”的截图。通过该截图我…

202302读书笔记|《长安的荔枝》——只要肯努力,办法总比困难多

202302读书笔记|《长安的荔枝》——只要肯努力,办法总比困难多 《长安的荔枝》这本书真是酣畅淋漓啊,读起来一气呵成,以讲故事的口吻叙述,上林署九品小官员——李善德,兢兢业业工作多年,终于借贷买了房&…

mac M1 nvm安装教程,避坑

mac M1 nvm 安装问题 新款的mac搭载了苹果自研的芯片,放弃了intel的x86芯片,那之前的软件难免会存在兼容性问题。 鄙人有幸踩了第一个坑。 在通过nvm 安装不同版本的node 时,出现了问题。 问题一:先说一下 nvm的安装问题&#…

Python02 基础语法

Python02 基础语法 二、基础语法 2.1 注释 概念:对某一部分代码的解释说明,在程序执行的时候不会去进行执行。比如:产品的说明书、电影的旁白。 程序员最讨厌的两件事: 一件是自己写代码被要求加注释 另一件是接手别人代码&…

Cesium三维数据格式以及生产流程详解(glb,osgb,obj,bim,ifc)等

最近收到私信问我在cesium上展示的一些三维数据是如何生产和处理的,这篇文章就给大家一次性讲个透彻。 首先我们来做做分类。市面上能接触到的,常见的,cesium上支持展示的三维数据大致分为以下几种: 1.倾斜摄影(osgb,obj) 2.点云数据(las,pts) 3.手工模型(gltf,…

win10安装oracle

文件放到最后。我的电脑是win11的,因为老师让写下安装笔记,在11上安装的时候没有截屏,所以在虚拟机上重新安装下吧。室友说要把文件夹放到c盘才能打开。我试了下,具体的是要把Oracle11g文件夹放到c盘根目录下。如果解压后不是这个…

修正一些formdesigner的一些bug与操作

之前集成了formdesigner表单设计器,但还是有些问题,所以进行一些bug修复与功能修正 一、之前组件布局的图标不见了 在main.js里增加下面一行 import /components/formdesigner/assets/iconfont/iconfont.js 效果如下: 二、选择列表没有数…

react-draggable实现拖拽详解

react-draggable属性常用属性属性列表事件列表举例首先安装 react-draggable实现移动希望小编写的能够帮助到你😘属性 常用属性 属性默认值介绍axisxhandle拖动的方向,可选值 x ,y,bothhandle无指定拖动handle的classposition无handle的位置&#xff0…

习题30 if elif else 语句

people 30#变量people赋值30 cars 40#变量cars赋值40 buses 15#变量buses赋值 if cars > people:#如果出租车比人多print("We should take the cars")#我们坐出租车 elif cars < people:#elif后面必须跟条件&#xff0c;print("We should not take the…

MyBatis框架的入门案例

MyBatis框架的入门案例 资源地址&#xff1a;https://download.csdn.net/download/weixin_41957626/87531373 1.MyBatis的配置 环境&#xff1a;基于maven的结构 1.1目录结构 1.2依赖包 <dependencies><!--mybatis--><dependency><groupId>org.mybatis…

【Vue3 第二十二章】过渡动画

一、基本用法 <Transition> 是一个内置组件&#xff0c;这意味着它在任意别的组件中都可以被使用&#xff0c;无需注册。它可以将进入和离开动画应用到通过默认插槽传递给它的元素或组件上。进入或离开可以由以下的条件之一触发&#xff1a; 由 v-if 所触发的切换由 v-…

函数栈帧的创建和销毁(详解)

函数栈帧的创建和销毁&#x1f996;函数栈帧是什么&#xff1f;&#x1f996;函数栈帧的创建和销毁解析&#x1f40b;栈是什么&#xff1f;&#x1f40b;认识相关寄存器和汇编指令&#x1f40b;解析函数栈帧的创建和销毁&#x1f433;预备知识&#x1f433;函数的调用堆栈&…

SpringBoot入门 - 配置热部署devtools工具

在SpringBoot开发调试中&#xff0c;如果我每行代码的修改都需要重启启动再调试&#xff0c;可能比较费时间&#xff1b;SpringBoot团队针对此问题提供了spring-boot-devtools&#xff08;简称devtools&#xff09;插件&#xff0c;它试图提升开发调试的效率。准备知识点什么是…

汇编系列02-借助操作系统输出Hello World

说明:本节的程序使用的是x86_64指令集的。 汇编语言是可以编译成机器指令的&#xff0c;机器指令是可以直接在CPU上面执行的。我们编写的汇编程序既可以直接在操作系统的帮助下执行&#xff0c;也可以绕过操作系统&#xff0c;直接在硬件上执行。 如果你打算编写的汇编程序在…

Buuctf Younger-drive 题解

目录 一.查壳 二.运行缺少dll 三.主函数 四.hObject线程 五.Thread线程 六.judge函数 七.解题脚本 这题的关键在于了解一定的线程相关知识 一.查壳 32位带壳,用upx脱壳 二.运行缺少dll 后续尝试了各种方法修复dll但是还是运行不了 值得一提的是脱壳后的程序不能动态调试…

分布式事务概述

什么是分布式事务&#xff1f;和本地事务的区别 分布式事务是指会涉及到操作多个数据库的事务。其实就是将对同一库事务的概念扩大到了对多个库的事务。目的是为了保证分布式系统中的数据一致性。分布式事务处理的关键是必须有一种方法可以知道事务在任何地方所做的所有动作&a…