什么?还在用delete删除数据《死磕MySQL系列 九》
系列文章
六、五分钟,让你明白MySQL是怎么选择索引《死磕MySQL系列 六》
七、字符串可以这样加索引,你知吗?《死磕MySQL系列 七》
参与了好几个项目开发,每个项目随着业务量的增大,MySQL数据日益剧增,例如其中一个项目中得用户足迹表,那是非常的疯狂,只怪我大意了,没有闪。
这篇文章我会从delete对性能的影响,以及如何以正确的姿势来删除数据。
在MySQL中Innodb存储引擎的表存在两部分,一部分是表结构,另一部分是表数据。
在MySQL8.0之前/var/lib/mysql
下都会存在.frm文件,在MySQL8.0之后就不存在了。这是因为MySQL8.0中已经允许把表结构定义放到数据字典中了,是用参数innodb_file_per_table来决定的。
一、表空间
表空间分为几种,系统表空间、用户表空间、undo空间。
系统表空间:MySQL内部的数据字典,如information_schema库下的数据。
用户表空间:自己建立的表结构数据
undo空间:存储Undo信息,用于快速回滚。
MySQL8.0之前表结构是在系统表空间存储的,在MySQL5.6.6后可以使用参数innodb_file_per_table来控制。
设置为off时,表数据是放在系统表空间中,也就是MySQL的数据字典放在一起。
设置为on时,innodb存储引擎的表数据存储在.idb文件中。
你知道表定义存储在哪里吗?
来到死磕MySQL系列的专用数据库kaka,新建一张表evt_sms。

猜一下创建的evt_sms表结构定义存储在哪里呢?
在information_schema库里边的TABLES中,执行查询SELECT TABLE_NAME,TABLE_COMMENT FROM TABLES WHERE TABLE_TYPE='BASE TABLE';
我们自定义的表类型是TABLE_TYPE
。
说了这么是为了解释如果把innodb_file_per_table
设置为off,则表数据也会存放在这里。
问题:如果数据存在放共享表空间中,表删除了,空间会删除吗?
答案是不会的。
参数innodb_file_per_table设置为on数据存储在哪里呢?
一般情况下是在var/lib/mysql
中,会看到你创建的数据库,进入到数据库中就能看到一张表对应一个ibd文件。
数据就是存储在这里。

结论
在项目开始阶段,切记将innodb_file_per_table设置为on,这是正确的做法。
二、数据删除流程
现在你应该知道Innodb存储引擎用的是B+树数据结构,如下图。

如果现在删了主键ID为4的这条记录,Innodb引擎会把ID为4的这条记录标记为删除,如果之后再插入ID为4的记录,可能会复用这个位置,但磁盘文件大小并不会缩小。
隐式字段
这里就牵扯到了mvcc中的一个知识点,MVCC实现原理是由俩个隐式字段、undo日志、Read view来实现的。
上文说的标记删除就是隐式字段中的delete flag,即记录被更新或删除,这里的删除并不代表真的删除,而是将这条记录的delete flag改为true。
在MVCC:听说有人好奇我的底层实现这篇文章中也给大家留下了一个伏笔,数据库的删除是真的删除吗?
问题:删了一个数据页的所有数据会怎么样
跟单条数据是一样的,整个数据页都是可以复用的。
记录的复用是仅限于符合范围条件的数据,例如上文删除的ID为4这条记录,如果在插入ID为4就会复用。
这里需要给大家再聊一个新的知识点页合并
,若相邻的两个数据页利用率都很低,系统就会把这两个数据页合并到一个页上,另一个数据页就会标记为可复用。
问题:使用delete把整个表的数据都删除了会怎么样
答案是,所有的数据页都会标记为可复用,但是磁盘文件大小是不会改变的。
三、实践全表删除表文件大小不改变

经过添加数据后表数据已经达到近100W了,文件大小已经达到108M。
扩展
这里大家应该能看见stopped
,就是执行命令ctrl + z
来的,作用是开始我们在MySQL窗口里边,但不想退出MySQL窗口查看MySQL表文件大小,然后就可以执行这个命令结束任务。
查看完后可以在执行fg
返回到MySQL窗口。

问题:Linux如何把文件单位显示为M
假设刚刚直接执行ll命令查看文件,那么就需要手动计算文件大小,很不方便。
执行ll -h命令则可以直观的看到文件大小。

删除数据查看磁盘文件是否缩小
为了直观看大文件大小变化,咔咔直接把表里边的数据全部删了,再看文件大小,还是108M。文件大小是没有变化的。
四、如何正确的减少磁盘文件
在第三小节中,我们演示了删除了100W数据后文件大小是没有改变的,也就是空洞问题影响的,接下来就解决这种问题。
问题:空洞是如何产生的?
到了这里都应该知道空洞是因为大量的增删改造成的。
解决思路
你可以新建一个evt_sms_copy表,然后根据主键ID递增的顺序,把数据从evt_sms读入evt_sms1中。
这样就可以达到因为空洞造成的磁盘文件大小无法收缩问题。
问题:为什么能解决呢?
因为evt_sms_copy是一张新的表,并且数据是以主键ID递增的,索引是紧促的,数据页利用率已经达到了最高峰状态,这样就起到了磁盘文件无法收缩问题。
上干货
直接执行alter table evt_sms engine = Innodb 命令来达到磁盘文件收缩。
这里需要跟大家聊一下不同版本处理不同。
在MySQL5.5之前,这个命令做的事情跟我们解决思路是一样的,不同的是evt_sms_copy是不用自己创建的。
在执行命令期间如有新增数据的话,会造成数据丢失,因为在MySQL5.5之前版本的DDL不是Online的。因此不能有数据的改动。
现在MySQL都已经更新到8版本了,如果你是新项目就直接用8版本,不要在用5.6以前的老版本了,咔咔在18年开始就已经在使用MySQL8.0版本了。
在锁那一期文章中跟大家聊了MySQL5.6在DDL操作做了优化,引入了Online DDL。
优化后的执行流程
建立临时文件tmp_file,把表的B+树存储到临时文件中。若此时有对表的操作,则会记录在row log文件中。 把数据从原表全部刷到临时文件后,此时临时文件的数据就跟原表的数据一致。 最后用临时文件替换表A的数据文件。
Online DDL的由来
可以看到在收缩磁盘文件时有数据更新会记录在row log中,意思就是在收缩磁盘空间时是可以对表进行增删改查的。
注意点
在进行磁盘文件收缩的过程中,都会全表扫描原数据和新增临时文件,如果你的表非常大,会非常消耗IO和CPU。
因此,你要安全的做这个操作,可以使用开源的gh-ost来进行。
结论
当你想收缩因为大量增删改查而导致表磁盘文件非常大时就可以执行alter table evt_sms engine=Innodb
命令来达到收缩表空间的目的。
五、实践是检验认识是否具有真理性的唯一标准
都应该知道实践是检验认识是否具有真理性的唯一标准
,那么接下里就对本文提出的结论进行实际操作一下。
先执行 ctrl + z
结束MySQL任务窗口执行 ll -h
查看此时表evt_sms磁盘文件大小为108M执行 fg
返回到MySQL任务窗口执行命令 alter table evt_sms engine=Innodb
再执行 ctrl + z
,执行ll -h
查看磁盘文件大小已经到了128k。

上图即是咔咔操作的全过程,得到的结论就是执行命令alter table ect_sms engine = Innodb
可以收缩由于大量增删改查的表引发的空洞问题。最终达到收缩表空间目的。
六、开发建议
删除数据不要使用delete,而是使用软删除,做一个标记删除即可。
这样既不会出现空洞问题,也方便数据溯源。
每张表必备三个字段create_time、update_time、delete_time。
七、总结
通过本期文章我们需要知道以下几点。
通过大量增删改查的表会出现空洞 干掉空洞需要执行alter table evt_sms engine=Innodb来解决 使用delete删除数据只会做一个标记处理,并不会真正删除空间 本文所有的结论都基于innodb_file_per_table = on
“
坚持学习、坚持写作、坚持分享是咔咔从业以来所秉持的信念。愿文章在偌大的互联网上能给你带来一点帮助,我是咔咔,下期见。
”
什么?还在用delete删除数据《死磕MySQL系列 九》的更多相关文章
- 重重封锁,让你一条数据都拿不到《死磕MySQL系列 十三》
在开发中有遇到很简单的SQL却执行的非常慢,甚至只查询一行数据. 咔咔遇到的只有两种情况,一种是MySQL服务器CPU占用率很高,所有的SQL都执行的很慢直到超时,程序也直接502,另一种情况是行锁造 ...
- MySQL统计总数就用count(*),别花里胡哨的《死磕MySQL系列 十》
有一个问题是这样的统计数据总数用count(*).count(主键ID).count(字段).count(1)那个效率高. 先说结论,不用那么花里胡哨遇到统计总数全部使用count(*). 但是有很多 ...
- 为什么MySQL字符串不加引号索引失效?《死磕MySQL系列 十一》
群里一个小伙伴在问为什么MySQL字符串不加单引号会导致索引失效,这个问题估计很多人都知道答案.没错,是因为MySQL内部进行了隐式转换. 本期文章就聊聊什么是隐式转换,为什么会发生隐式转换. 系列文 ...
- 打开order by的大门,一探究竟《死磕MySQL系列 十二》
在日常开发工作中,你一定会经常遇到要根据指定字段进行排序的需求. 这时,你的SQL语句类似这样. select id,phone,code from evt_sms where phone like ...
- 闯祸了,生成环境执行了DDL操作《死磕MySQL系列 十四》
由于业务随着时间不停的改变,起初的表结构设计已经满足不了如今的需求,这时你是不是想那就加字段呗!加字段也是个艺术活,接下来由本文的主人咔咔给你吹. 试想一下这个场景 事务A在执行一个非常大的查询 事务 ...
- 一生挚友redo log、binlog《死磕MySQL系列 二》
系列文章 原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 一生挚友redo log.binlog<死磕MySQL系列 二> 前言 咔咔闲谈 上期根据 ...
- MySQL强人“锁”难《死磕MySQL系列 三》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 前言 最近数据库 ...
- S 锁与 X 锁的爱恨情仇《死磕MySQL系列 四》
系列文章 一.原来一条select语句在MySQL是这样执行的<死磕MySQL系列 一> 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强 ...
- 五分钟,让你明白MySQL是怎么选择索引《死磕MySQL系列 六》
系列文章 二.一生挚友redo log.binlog<死磕MySQL系列 二> 三.MySQL强人"锁"难<死磕MySQL系列 三> 四.S 锁与 X 锁的 ...
随机推荐
- 定要过python二级 真题 第四套
第一模块 基本操作 1.print(" { } " . format(s)) 记住 " { }" ...
- VmWare装Linux&Centos步骤
昨晚一次偶然的机会进入飞哥的直播间,他正在将用虚拟机搭建Linux环境的步骤,自己之前也确实安装过一次,不过没什么系统性总结,过程中有些步骤还需百度查找.于是乎今天决定从零基础在过一遍流程,便是这篇博 ...
- Bootstrap的模态框无法弹出的问题
今天在使用Bootstrap官网所提供的模态框插件时候发现其中的 可选尺寸模态框 无法弹出 在模态框前使用过其他 Bootstrap的js插件,可以正常使用,说明所需依赖js文件已经正常引用 注意:j ...
- Idea生成JavaDoc文档
什么是JavaDoc javadoc是Sun公司提供的一个技术 它从程序源代码中抽取类.方法.成员等注释形成一个和源代码配套的API帮助文档 实现方式 命令行方式 javadoc -encoding ...
- Scala trait特质 深入理解
Scala trait特质 深入理解 初探Scala 特质trait 在Scala中,trait(特质)关键字有着举足轻重的作用.就像在Java中一样,我们只能在Scala中通过extends进行单一 ...
- CF193D Two Segments (线段树+dp)(外加两个扩展题)
大概算是个系列整理 (最强版是模拟赛原题)) 首先,我们先来看这个题目. QWQ一开始是毫无头绪,除了枚举就是枚举 首先,我们可以枚举一个右端点,然后算一下当前右端点的答案 我们令\(f[l,r]\) ...
- Jenkins 进阶篇 - 单元测试覆盖率
我们做项目开发,肯定免不了要写单元测试,不管是 Java 项目.Python 项目.PHP 项目,甚至是 nodejs 项目,都应该要写单元测试,本小节就来介绍单元测试的覆盖率报告输出和展示,在后面的 ...
- Spring Boot 整合 Apache Ignite
关于Ignite的介绍,这边推荐三个链接进行学习了解. https://ignite.apache.org/,首选还是官网,不过是英文版,如果阅读比较吃力可以选择下方两个链接. https://www ...
- JSP(java server pages)安装开发和执行环境
JSP是一种动态网页技术标准. 它是在传统的网页HTML文件中插入Java程序段(Scriptlet)和JSP标记(tag)的.jsp文件: java程序段:操纵数据库,重新定向网页,发送email等 ...
- Unity——自动化代码生成
自动化代码生成 一.前言 由于之前写过关于UI框架的文章,这篇基于之前的基础,添加了自动生成代码的功能: 如果学习过程有困惑可以跳转到之前的文章<Unity--基于UGUI的UI框架>: ...