mysql为什么占用_小弟问我:为什么MySQL不建议使用delete删除数据?

前言

我负责的有几个系统随着业务量的增长,存储在MySQL中的数据日益剧增,我当时就想现在的业务方不讲武德,搞偷袭,趁我没反应过来把很多表,很快,很快啊都打到了亿级别,我大意了,没有闪,这就导致跟其Join的表的SQL变得很慢,对的应用接口的response time也变长了,影响了用户体验。

事后我找到业务方,我批评了他们跟他们说要讲武德,连忙跟我道歉,这个事情才就此作罢,走的时候我对他们说下次不要这样了,耗子尾汁,好好反思。

fc00ba685a3b056274b9d20bb4595c47.png

骂归骂,事情还是得解决,时候我分析原因发现,发现有些表的数据量增长很快,对应SQL扫描了很多无效数据,导致SQL慢了下来,通过确认之后,这些大表都是一些流水、记录、日志类型数据,只需要保留1到3个月,此时需要对表做数据清理实现瘦身,一般都会想到用insert + delete的方式去清理。

这篇文章我会从InnoDB存储空间分布,delete对性能的影响,以及优化建议方面解释为什么不建议delete删除数据。

InnoDB存储架构

c757c18c3e5df95770041fb1621d1c63.png

从这张图可以看到,InnoDB存储结构主要包括两部分:逻辑存储结构和物理存储结构。

逻辑上是由表空间tablespace —> 段segment或者inode —> 区Extent ——>数据页Page构成,Innodb逻辑管理单位是segment,空间分配的最小单位是extent,每个segment都会从表空间FREE_PAGE中分配32个page,当这32个page不够用时,会按照以下原则进行扩展:如果当前小于1个extent,则扩展到1个extent;当表空间小于32MB时,每次扩展一个extent;表空间大于32MB,每次扩展4个extent。

物理上主要由系统用户数据文件,日志文件组成,数据文件主要存储MySQL字典数据和用户数据,日志文件记录的是data page的变更记录,用于MySQL Crash时的恢复。

Innodb表空间

InnoDB存储包括三类表空间:系统表空间,用户表空间,Undo表空间。

系统表空间:主要存储MySQL内部的数据字典数据,如information_schema下的数据。

用户表空间:当开启innodb_file_per_table=1时,数据表从系统表空间独立出来存储在以table_name.ibd命令的数据文件中,结构信息存储在table_name.frm文件中。

Undo表空间:存储Undo信息,如快照一致读和flashback都是利用undo信息。

从MySQL 8.0开始允许用户自定义表空间,具体语法如下:

这样的好处是可以做到数据的冷热分离,分别用HDD和SSD来存储,既能实现数据的高效访问,又能节约成本,比如可以添加两块500G硬盘,经过创建卷组vg,划分逻辑卷lv,创建数据目录并mount相应的lv,假设划分的两个目录分别是/hot_data 和 /cold_data。

这样就可以将核心的业务表如用户表,订单表存储在高性能SSD盘上,一些日志,流水表存储在普通的HDD上,主要的操作步骤如下:

Inndob存储分布

创建空表查看空间变化

设置参数innodb_file_per_table=1时,创建表时会自动创建一个segment,同时分配一个extent,包含32个data page的来存储数据,这样创建的空表默认大小就是96KB,extent使用完之后会申请64个连接页,这样对于一些小表,或者undo segment,可以在开始时申请较少的空间,节省磁盘容量的开销。

插入数据后的空间变化

delete数据后的空间变化

MySQL内部不会真正删除空间,而且做标记删除,即将delflag:N修改为delflag:Y,commit之后会会被purge进入删除链表,如果下一次insert更大的记录,delete之后的空间不会被重用,如果插入的记录小于等于delete的记录空会被重用,这块内容可以通过知数堂的innblock工具进行分析。

Innodb中的碎片

碎片的产生

我们知道数据存储在文件系统上的,总是不能100%利用分配给它的物理空间,删除数据会在页面上留下一些”空洞”,或者随机写入(聚集索引非线性增加)会导致页分裂,页分裂导致页面的利用空间少于50%,另外对表进行增删改会引起对应的二级索引值的随机的增删改,也会导致索引结构中的数据页面上留下一些"空洞",虽然这些空洞有可能会被重复利用,但终究会导致部分物理空间未被使用,也就是碎片。

同时,即便是设置了填充因子为100%,Innodb也会主动留下page页面1/16的空间作为预留使用(An innodb_fill_factor setting of 100 leaves 1/16 of the space in clustered index pages free for future index growth)防止update带来的行溢出。

其中data_free是分配了未使用的字节数,并不能说明完全是碎片空间。

碎片的回收

对于InnoDB的表,可以通过以下命令来回收碎片,释放空间,这个是随机读IO操作,会比较耗时,也会阻塞表上正常的DML运行,同时需要占用额外更多的磁盘空间,对于RDS来说,可能会导致磁盘空间瞬间爆满,实例瞬间被锁定,应用无法做DML操作,所以禁止在线上环境去执行。

delete对SQL的影响

未删除前的SQL执行情况

删除后的SQL执行情况

结果统计分析

这也说明对普通的大表,想要通过delete数据来对表进行瘦身是不现实的,所以在任何时候不要用delete去删除数据,应该使用优雅的标记删除。

delete优化建议

控制业务账号权限

对于一个大的系统来说,需要根据业务特点去拆分子系统,每个子系统可以看做是一个service,例如美团APP,上面有很多服务,核心的服务有用户服务user-service,搜索服务search-service,商品product-service,位置服务location-service,价格服务price-service等。每个服务对应一个数据库,为该数据库创建单独账号,同时只授予DML权限且没有delete权限,同时禁止跨库访问。

delete改为标记删除

在MySQL数据库建模规范中有4个公共字段,基本上每个表必须有的,同时在create_time列要创建索引,有两方面的好处:

一些查询业务场景都会有一个默认的时间段,比如7天或者一个月,都是通过create_time去过滤,走索引扫描更快。

一些核心的业务表需要以T +1的方式抽取数据仓库中,比如每天晚上00:30抽取前一天的数据,都是通过create_time过滤的。

数据归档方式

通用数据归档方法

优化后的归档方式

这样原表和归档表都是按月的分区表,只需要创建一个中间普通表,在业务低峰期做两次分区交换,既可以删除无效数据,又能回收空,而且没有空间碎片,不会影响表上的索引及SQL的执行计划。

总结

通过从InnoDB存储空间分布,delete对性能的影响可以看到,delete物理删除既不能释放磁盘空间,而且会产生大量的碎片,导致索引频繁分裂,影响SQL执行计划的稳定性;

同时在碎片回收时,会耗用大量的CPU,磁盘空间,影响表上正常的DML操作。

在业务代码层面,应该做逻辑标记删除,避免物理删除;为了实现数据归档需求,可以用采用MySQL分区表特性来实现,都是DDL操作,没有碎片产生。

另外一个比较好的方案采用Clickhouse,对有生命周期的数据表可以使用Clickhouse存储,利用其TTL特性实现无效数据自动清理。

好啦,以上就是本期的全部内容啦,我是敖丙,你知道的越多,你不知道的越多,我们下期见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值