什么?还在用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 锁的 ...
随机推荐
- P4980-[模板]Pólya定理
正题 题目链接:https://www.luogu.com.cn/problem/P4980 题目大意 \(n\)个物品图上\(m\)种颜色,求在可以旋转的情况下本质不同的涂色方案. 解题思路 既然是 ...
- Java——this关键字总结
一.this关键字的使用 1.当同类型的对象a和b,调用相同的方法method()时,为了区分该方法是被a调用还是被b调用 如下代码中,: class Banana { void method(int ...
- 记一次centos挂载ceph存储的坑
起因 生产有两台服务器,准备用来跑工作流,执行的资源的是放在ceph存储集群中,第一步挂载ceph 执行命令:mount -t ceph xxx:xxx -o name=admin,secret=AQ ...
- 【C++ Primer Plus】编程练习答案——第3章
1 void ch3_1() { 2 using namespace std; 3 unsigned int factor = 12; 4 unsigned int inch, feet; 5 cou ...
- centos7有关于防火墙的命令
查看防火墙状态 firewall-cmd --state 开启防火墙 systemctl start firewalld.service 关闭防火墙 systemctl stop firewalld. ...
- 10.7 URI
URI: Uniform Resource Identifier 统一资源标识符 URL: Uniform Resource Locator 统一资源定位符 URN: Uniform R ...
- C# 类拓展方法
C#类拓展方法 要求: 扩展方法类必须为静态类: 拓展方法必须为静态方法,参数为this+需拓展类对象: 多个类拓展方法可以写在一个拓展类中: public class TestExtension { ...
- 阿里 Midway 正式发布 Serverless v1.0,研发提效 50%
Github:https://github.com/midwayjs/midway 开源为了前端和 Node.js 的发展,点 Star! 去年阿里提出 Serverless 架构,并利用其新一代研发 ...
- golang引用第三方包的报错:no required module provides package [完美解决]
关于golang第三方包的引用报错:no required module provides package : go.mod file not found in current directory o ...
- 洛谷3703 SDOI2017树点涂色(LCT+线段树+dfs序)
又一道好题啊qwqqqq 一开始看这个题,还以为是一个树剖的什么毒瘤题目 (不过的确貌似可以用树剖啊) qwq这真是一道\(LCT\)维护颜色的好题 首先,我们来一个一个操作的考虑. 对于操作\(1\ ...