MySQL 13表数据删掉一半表文件大小不变的原因分析

 更新时间:2025年07月14日 09:31:19   作者:san-mu  
这篇文章主要介绍了MySQL 13表数据删掉一半表文件大小不变的原因分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

一个InnoDB表包含两部分:表结构定义和数据。在MySQL 8.0版本前,表结构存在以.frm为后缀的文件里。之后的版本允许把表结构定义放在系统数据表中。由于表结构定义占用空间很小,所以主要讨论表数据。

接下来,先说明为什么简单删除表数据达不到表空间回收的效果,再介绍正确回收空间的方法。

参数innodb_file_per_table

表数据既可以存在共享表空间里,也可以是单独的文件,这由参数innodb_file_per_table控制:

  • 设为OFF,表示表数据放在系统共享表空间,也就是跟数据字典放在一起;

  • 设为ON,表示每个InnoDB表数据存储在一个以.ibd为后缀的文件中。

从MySQL 5.6.6版本开始,默认值为ON。建议也是使用ON,因为一个表单独存储为一个文件更容易管理,而且在不需要该表时通过drop table命令,系统就会直接删除文件;如果是放在共享表空间中,即使表删除,空间也是不会回收的。

接下来的讨论也是基于innodb_file_per_table=ON的设置。

在删除整张表的时候,可以使用drop table命令回收表空间。但是,平时更多的场景是删除某些行。

数据删除流程

为了搞懂删除部分行的场景,需要先从数据删除流程开始说。

看一下InnoDB中一个索引的示意图:

假设要删除R4这个记录,InnoDB只会把R4这个记录标记为删除。如果之后插入一个ID在300-600间的记录,可能会复用这个位置,但磁盘文件的大小不会缩小。

那么如果将一个数据页上的所有记录都删除,会怎么样呢?答案是整个数据页可以复用。

但是数据页的复用和记录的复用还是不一样的。记录的复用只限于符合范围条件的数据,而一旦一个数据页可以复用,所有范围的数据都可以使用。比如在上面的索引中,若page A是可复用的,ID=50这样的记录也能使用该页。

如果相邻两个数据页利用率都很小,系统会把这两个页上的数据合到其中一个页上,另一个页就会被标记为可以复用。

进一步地,如果用delete命令删除整个表的数据,那么所有数据页都会被标记为可复用,而磁盘上的文件并不会变小。也就是说,delete命令不能回收表空间,这些可以复用却没被使用的空间,看起来就像“空洞”。

实际上不止删除数据会造成空洞,插入数据也会。如果数据的插入是随机的,可能造成索引的数据页分裂。比如在上面的索引中,假设page A已满,这时若要再插入一行数据ID=550:

当page A已满的情况下进行插入,就必须再申请一个新的页面page B来保存数据。由于页分裂导致部分数据移动,page A就出现了空洞。

除了插入,由于更新可以看为删除+插入,也可能造成空洞。即,增删改都可能出现空洞。所以,如果能把这些空洞去掉,就能达到收缩表空间的目的。

重建表就可以达到这样的目的。

重建表

假设现在有一个表A,需要去除其中的空洞,有什么办法呢?

可以新建一个与表A结构相同的表B,然后按照主键ID递增的顺序,把数据逐行从表A读取出来再插入到表B中。由于表B是新建的表,所以没有表A上的空洞。把表B作为临时表,数据从表A导入表B后,再用表B替换表A,从效果上就是表A没有空洞了。

可以使用alter table A engine=InnoDB的命令重建表。在MySQL 5.5版本前,这个命令的执行流程和上面描述的差不多,区别只是不需要自己创建临时表,MySQL会自动完成转存数据、交换表名、删除旧表的操作。

在往临时表插入数据的过程中,如果有新的数据要写入表A,会造成数据损失,因此整个DDL的过程中,表A不能有更新,即DDL不是Online的。

而MySQL 5.6开始的版本引入了Online DDL,对这个操作流程做了优化。新的流程为:

  • 建立一个临时文件;

  • 扫描表A主键的所有数据页,用里面的记录生成B+树,存储到临时文件中;

  • 生成临时文件的过程中,将所有对A的操作记录在一个日志文件(row log)中,对应下图中state 2的状态;

  • 临时文件生成以后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表A相同的临时文件;

  • 用临时文件替换表A。

该操作流程由于日志文件和重放操作的功能,在重建表的过程中允许对表A做增删改操作。

当然,由于对表做改动,会有MDL锁的存在。alter语句在启动时会获取MDL写锁,但这个锁在真正拷贝数据之前就会退化成读锁,目的是禁止其他线程对这个表同时做DDL,又不会阻塞增删改操作。

对于一个大表来说,Online DDL最耗时的过程就是拷贝数据到临时表的过程,所以相对整个DDL过程来说,写锁锁住的时间非常短,可以认为是Online的。

需要说明的是,上述这些重建方法都会扫描原表数据和构建临时文件,对于很大的表来说,该操作很消耗IO和CPU资源。因此,如果是线上服务需要控制操作时间,推荐使用开源的gh-ost来做。

Online和inplace

说到Online,再讲一个容易混淆的概念inplace。

在早版本的重建表过程中,表A数据导出来的存放位置叫做tmp_table,这个临时表是在Server层创建的。

而在后面的版本,表A重建出来的数据是放在tmp_file里的(见前面的图),这个临时文件是InnoDB在内部创建出来的。由于整个DDL过程在InnoDB内部完成,对于Server层来说,没有把数据挪动到临时表,是一个“原地”操作,因此叫inplace。

那么假如表大小为1TB,磁盘空间为1.2TB,是否能做inplace的DDL呢?答案是不行的,因为tmp_file会占用临时空间。

重建表的完整语句其实是下面这样:

alter table t engine=innodb,ALGORITHM=inplace;
alter table t engine=innodb,ALGORITHM=copy;

其中,copy表示强制拷贝表,即使用临时表;inplace表示使用临时文件。

那是否表示,inplace就是Online?也不是,只是在重建表这个逻辑中刚好是这样。

如果说这两个逻辑之间的关系是什么,可以概括为:

  • DDL过程如果是Online的,就一定是inplace的;

  • 反之不正确,inplace的DDL,不一定是Online的。截止到 MySQL 8.0,添加全文索引(FULLTEXT index)和空间索引 (SPATIAL index) 就属于这种情况。比如要给InnoDB表的一个字段加全文索引,过程是inplace的,但会阻塞增删改。

到此这篇关于MySQL 13 为什么表数据删掉一半,表文件大小不变?的文章就介绍到这了,更多相关mysql表数据删掉一半表文件大小不变内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决Mysql主从错误:could not find first log file name in binary

    解决Mysql主从错误:could not find first log&nbs

    这篇文章主要介绍了解决Mysql主从错误:could not find first log file name in binary问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 快速修复损坏的MySQL数据库

    快速修复损坏的MySQL数据库

    在长时间使用 MySQL 后,数据库可能会出现一些问题。大多数问题可以通过简单的操作进行快速修复。下面介绍两种快速检修 MySQL 数据库的方法。
    2011-06-06
  • Mysql树形结构的数据库表设计方案

    Mysql树形结构的数据库表设计方案

    树形结构对大家来说应该都不陌生,在日常开发中经常会遇到,下面这篇文章主要给大家介绍了关于Mysql树形结构的数据库表设计的相关资料,文中通过示例代码的非常详细,需要的朋友可以参考下
    2021-09-09
  • MySQL请求处理全流程之如何从SQL语句到数据返回

    MySQL请求处理全流程之如何从SQL语句到数据返回

    这篇文章主要介绍了MySQL请求处理全流程之如何从SQL语句到数据返回,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-03-03
  • mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录

    mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录

    mysql not in、left join、IS NULL、NOT EXISTS 效率问题记录,需要的朋友可以参考下。
    2011-12-12
  • mysql查看死锁与去除死锁示例详解

    mysql查看死锁与去除死锁示例详解

    这篇文章主要给大家介绍了关于mysql查看死锁与去除死锁的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-12-12
  • ubuntu kylin 14.10下多个mysql 5.7.14安装教程

    ubuntu kylin 14.10下多个mysql 5.7.14安装教程

    这篇文章主要为大家分享了ubuntu kylin 14.10下多个mysql 5.7.14安装教程,感兴趣的朋友可以参考一下
    2016-08-08
  • 关于MySQL存取图片的三种方式(含源码示例)

    关于MySQL存取图片的三种方式(含源码示例)

    最近在做小程序的后台,要求将小程序用户以upload方法上传的图片保存到mysql数据库中,然后机缘巧合三种方式都试了,所以专门整理出来分享给大家,可能有的地方说的不太对,欢迎大家帮纠正,需要的朋友可以参考下
    2024-04-04
  • MySQL5.7更改密码时出现ERROR 1054 (42S22)的解决方法

    MySQL5.7更改密码时出现ERROR 1054 (42S22)的解决方法

    这篇文章主要为大家详细介绍了MySQL5.7更改密码时出现ERROR 1054 (42S22)的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • mysql批量插入BulkCopy的实现

    mysql批量插入BulkCopy的实现

    本文主要介绍了mysql批量插入BulkCopy的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03

最新评论