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 中应用最广泛的 InnoDB 引擎展开讨论. - 一个 InnoDB 表包含两部分,即:表结构定义和数据. - 在 MySQL 8.0 版本以前,表结构是 ...
- MySQL 笔记整理(13) --为什么数据表删掉一半,表文件大小不变?
笔记记录自林晓斌(丁奇)老师的<MySQL实战45讲> (本篇内图片均来自丁奇老师的讲解,如有侵权,请联系我删除) 13) --为什么数据表删掉一半,表文件大小不变? 我们还是以MySQL ...
- 取A表数据,关联B表任意一条数据
表A=================== AID, AName 1 jack 2 mary 3 lily 表B================== BID, AID, BName 1 1 aaa ...
- 三十一.MySQL存储引擎 、 数据导入导出 管理表记录 匹配条件
1.MySQL存储引擎的配置 查看服务支持的存储引擎 查看默认存储类型 更改表的存储引擎 设置数据库服务默认使用的存储引擎 1.1 查看存储引擎信息 mysql> SHOW ENGINES\G ...
- merge源表数据移植到目标表新表数据中
merge into dbo.ak_SloteCardTimes a using(select RecordID,CardNO,SloteCardTime from dbo.Tb_CardDate b ...
- SQL Server 的表数据简单操作(表数据查询)
--表数据查询----数据的基本查询-- --数据简单的查询--select * | 字段名[,字段名2, ...] from 数据表名 [where 条件表达式] 例: use 商品管理数据库 go ...
- 设计一个A表数据抽取到B表的抽取过程
原题如下: 解题代码如下: table1类: @Data @NoArgsConstructor @AllArgsConstructor public class table1{ private Str ...
- 【database】复制表数据到相同备份表
目的及由来,因为数据库表都采取逻辑删除isDeleted=true/flase,但是之前有些报表或者其他的sql并没有在sql中指明此条件.为了不影响之前代码,所以: 1.数据库中创建一张相同的表,把 ...
- sqlserver复制表数据到另一个表
SQL Server中,如果目标表存在: insert into 目标表 select * from 原表; SQL Server中,,如果目标表不存在: select * into 目标表 from ...
- mysql把A表数据插入到B表数据的几种方法
web开发中,我们经常需要将一个表的数据插入到另外一个表,有时还需要指定导入字段,设置只需要导入目标表中不存在的记录,虽然这些都可以在程序中拆分成简单sql来实现,但是用一个sql的话,会节省大量代码 ...
随机推荐
- 使用傅里叶级数和Python表示方波
引言 在信号处理和数字通信中,方波是非常常见的一种波形.方波是一种周期性波形,信号在两个固定的幅度之间跳跃,通常是"高"与"低"的状态.你可能会问,如何通过数学 ...
- 如何使用Git命令将代码上传到GitHub
1. 首先在我们的计算机上创建文件夹:例如取名:test 2. 进入test文件夹后点击鼠标右键,选择打开Git Bash. 3. 将我们创建的文件夹初始化为Git仓库:git init 4. 将要上 ...
- 解决Linux中网络重启后ip变化的问题
一.在Linux中输入命令, 编辑 ens33 网卡的网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 二.在配置中作如下修改 将 BOOTP ...
- Avalonia跨平台实战(二),Avalonia相比WPF的便利合集(一)
本话讲的是Avalonia中相比于WPF更方便的一些特性 布局 布局方面没什么好说的,和WPF没什么区别,Grid,StckPanel...这些,不熟悉的话可以B站上找一下教程 xml树 在WPF中我 ...
- MySQL InnoDB 引擎中的聚簇索引和非聚簇索引有什么区别?
MySQL InnoDB 引擎中的聚簇索引和非聚簇索引的区别 在 MySQL 的 InnoDB 存储引擎中,聚簇索引和非聚簇索引是两种常见的索引类型,它们在数据存储结构和使用场景上有显著区别. 1. ...
- Harmony 动态路由框架:TheRouter 开源
TheRouter 是一个用于移动端APP,包括 Android.iOS.Harmony 三端的模块化.组件化开发的一整套解决方案框架.提供了三端高一致性,对移动端开发者更友好,让开发人员更适应,使用 ...
- 信息资源管理综合题之“SPD属于知识管理工具那一类 与 管理工具与知识库的区别 以及 使用知识地图是否可以用SynchroFLOW替代”
一.案例:1995年10月,微软开发了一项"技能规划与开发(SPD)"的计划,他们把每个系统开发人员的工作能力和这些特定工作需要的知识制作成地图,让那个员工与团队间的配合更加默契, ...
- WPF中Resx文件中添加Byte[]
参考:https://learn.microsoft.com/zh-tw/dotnet/api/system.resources.resxresourcewriter.generate?view=wi ...
- C# 之静态构造器与静态字段初始化器
public class Test { /// <summary> /// 静态字段初始化器会在调用静态构造器前运行. /// 如果类型没有静态构造器,字段会在类型被使用前或运行时中更早的 ...
- 制作一个炫酷的多小球碰碰的 JS 网页,入门物理碰撞模拟和类的应用
目录 前言 先画一个圆 完善我们的类 小球动起来 最简单的碰撞计算,接触墙壁反弹 向量类的完善 检测两小球之间的碰撞 完善碰撞的效果 重复计算的问题 撞击墙壁定格问题 内存问题 随机数生成多个小球 参 ...