MySQL的表空间释放
概述
最近为了对 MySQL 数据库磁盘占用瘦身,对一张近100GB表的历史数据进行了 delete 删除,删除了约2/3的数据,删除后发现该表占用的空间并未减少。通过下面语句查看该表的磁盘占用情况:
SELECT
TABLE_NAME AS `表名`,
ROUND(DATA_LENGTH / 1024 / 1024 / 1024, 2) AS `数据大小(GB)`,
ROUND(INDEX_LENGTH / 1024 / 1024 / 1024, 2) AS `索引大小(GB)`,
ROUND(DATA_FREE / 1024 / 1024 / 1024, 2) AS `碎片空间(GB)`,
ROUND((DATA_LENGTH + INDEX_LENGTH + DATA_FREE) / 1024 / 1024 / 1024, 2) AS `预估总占用(GB)`
FROM
information_schema.TABLES
WHERE
TABLE_SCHEMA = 'mp_limitation_global'
ORDER BY
(DATA_LENGTH + INDEX_LENGTH)
DESC;
发现总的空间并没有发生变化,只是表数据空间减小,而碎片空间大幅上升:

所以,为什么数据删除了,表占用的磁盘空间却没有释放?
这里先交代一下全文 MySQL 相关的论述的基础:MySQL 版本为5.7.36,存储引擎为 InnoDB,参数innodb_file_per_table=ON。
为什么删除数据表空间未释放?

为了解释这个问题,先探究一下 MySQL 数据删除的过程。
如上图,现在要删掉 R7 这个记录,InnoDB 引擎只会把 R7 这个记录标记为删除,以待后续复用,但磁盘文件的大小并不会缩小。由于 InnoDB 的数据是按页存储的,那么如果删掉了一个数据页上的所有记录,整个数据页就也是可以被复用的。
然而,数据页的复用跟记录的复用是不同的。
记录的复用,只限于符合范围条件的数据:比如上面的假设,R7 这条记录被删除后,如果插入一个 ID 是 008 的行,那么可以直接复用这个空间;但如果插入的是一个 ID 是 012 的行,就不可以复用这个位置了。
数据页的复用,是当整个页从 B+ 树里面删掉以后,可以复用任何位置:以上图为例,如果将数据页 page A 上的所有记录被删除以后,page A 就会被标记为可复用,但是磁盘上的文件不会变小。
所以,delete 命令其实只是把记录的位置或数据页标记为了「可复用」,但不会改变磁盘文件的大小,即只通过 delete 是不能释放表空间的。这些可以复用,而又没有归还给操作系统的空间,看起来就像是“干净的地板上散落的碎片”。
如果能把这些碎片清理干净,那么就达到释放表空间的目的。
如何释放碎片占用的空间?
查阅 MySQL 官方文档-Defragmenting a Table,发现可以使用如下语句整理碎片:
ALTER TABLE tbl_name ENGINE=INNODB;

当然也可以使用下面的的语句整理碎片:
ALTER TABLE tbl_name FORCE;
这两种操作都使用到了在线 DDL 来重建表。那在线 DDL 又是什么?
在线 DDL 重建表的流程是什么?
使用ALTER 语句重新表的在线 DDL 机制大致如下:
ALTER语句在初始化启动的时候需要获取 MDL 写锁;
MDL写锁在真正拷贝数据之前就退化成读锁,MDL 读锁不会阻塞增删改等 DML 操作,InnoDB把这些操作记入 ROW_LOG;
最后完成存量数据相关拷贝,MDL 升级为独占锁,重放 ROW_LOG以写入增量数据。
而整个 DDL 最耗时的过程就是拷贝目标表数据到中间表文件的过程,这个过程中又可以增删改数据,而相对于整个 DDL 过程来说,锁的时间非常短,所以对业务来说,就可以认为是没有中断且在线的,这就是称为在线 DDL 的缘由,官方称之为 Online DDL。

在 Online DDL 过程中哪些操作可以触发重建表,哪些操作是“原地”或称“就地”操作,官方给出了详细说明,这里罗列一些开发中常见的一些操作:



Online DDL 过程中会产生临时文件,而这些临时文件也需要占用磁盘空间,对应的磁盘空间要求,官方也出了明确描述:

上面的 ALTER 语句在 MySQL 5.7下隐含的意思其实是:
ALTER TABLE tbl_name engine=innodb,ALGORITHM=INPLACE;
同时整个过程不需要经过服务层搬运数据,整个过程是在 InnoDB 内部完成的,是一个“原地”、“就地”操作,这就是称为“inplace”的来源。
跟 INPLACE 相对应的就是拷贝表:
ALTER TABLE tbl_name engine=innodb,ALGORITHM=COPY;
当使用 ALGORITHM=COPY 的时候,表示的是强制拷贝表,这是 MySQL5.6版本以前的重建表流程:
MySQL服务层触发创建临时表,对源表加MDL锁,阻塞DML写而不阻塞DML读;
MySQL服务层从目标表中逐行读取数据,写入到临时表;
数据拷贝完成后,禁止读写,删除目标表,把临时表重命名为目标表。
上面的流程大致如下图:

如何理解 INPLACE 和 COPY
想象一个堆着货物又杂乱无章的仓库,现在要进行整理,以充分利用仓库内的空间:
INPLACE 就是在仓库内部整理,货物不用搬出仓库,货物按照编号摆放到货架。在这个过程中,可能要从货架临时取出一些货物,把它们放到仓库内指定的某处,以腾出空间排放正确编号的货物,所以仓库要有足够的空间,来临时堆放那些从货架临时取出的货物;
COPY 就是把仓库里的货物从货架取出,然后全部搬到仓库外面指定的某个场地(这个场地的空间需要足够大,以容纳所有货物),然后再按照编号顺序一件一件的搬回仓库,放到货架。
操作注意事项
由于通过 Online DDL 方式整理碎片的过程中会产生和原数据表空间大小几乎规模相当的临时文件,所以执行前一定要确认 MySQL 磁盘剩余可用空间是否足够;
Online DDL在ALTER表过程中,会通过过 ROW_LOG 临时文件记录当时的并发 DML 操作,这个临时文件的大小上限由参数innodb_online_alter_log_max_size决定(默认大小128MB),所以要在业务低峰期操作碎片整理,保证没有大量 DML 操作,或者调整innodb_online_alter_log_max_size大小;
在碎片整理过程中,可以通过以下方式来监控整理过程:
-- 监控磁盘临时文件
SHOW STATUS LIKE 'Created_tmp_disk_tables';
SHOW STATUS LIKE 'Created_tmp_files';
-- 查看正在进行的 DDL 操作
SELECT * FROM information_schema.INNODB_TRX;
附录:
Online DDL 参考文档
欢迎关注同名公众号,内容同步发布,移动端体验更好,你的关注是我持续创作的动力:

MySQL的表空间释放的更多相关文章
- [记录]一则清理MySQL大表以释放磁盘空间的案例
一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...
- MySQL的表空间管理
表空间: MySQL没有真正意义上的表空间管理. MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间. 一般来说,当数据量很小的时候建议使用共享表空间的管理方式. ...
- mysql通过表空间来恢复或者传递数据
mysql的备份工具通常有 mysqldump ,mysqlpump(5.7后新特性)等备份工具,这里我们可以尝试使用表空间进行传递 方式是:拷贝数据文件+拷贝表空间 对应innodb引擎就是 i ...
- 谈谈什么是MySQL的表空间?
今天我要跟你分享的话题是:"大家常说的表空间到底是什么?究竟什么又是数据表?" 这其实是一个概念性的知识点,当作拓展知识.涉及到的概念大家了解一下就好,涉及的参数,留个印象就好. ...
- 使用MySQL传输表空间迁移数据
对于大表的迁移,如果使用mysqldump进行导出,然后重新导入到其它环境,速度是非常缓慢的.如果使用传输表空间,则可以解决这个问题. 测试使用传输表空间迁移表,基础环境如下: 源库 目标库 IP ...
- mysql共享表空间转独立表空间
使用innodb_export_import.py脚本: https://github.com/thecpaneladmin/innodb-tools 安装MySQL-python模块: shell ...
- 浅析mysql 共享表空间与独享表空间以及他们之间的转化
innodb这种引擎,与MYISAM引擎的区别很大.特别是它的数据存储格式等.对于innodb的数据结构,首先要解决两个概念性的问题: 共享表空间以及独占表空间.什么是共享表空间和独占表空间共 ...
- mysql 案例 ~ 表空间迁移数据与数据导入
一 简介:mysql5.6+的表空间传输二 目的:复制数据到另一个表三 步骤 1 create table b like a ->创建一个空表 2 alter table b disc ...
- mysql之 表空间传输
说明:MySQL(5.6.6及以上),innodb_file_per_table开启. 1.1. 操作步骤: 0. 目标服务器创建相同表结构1. 目的服务器: ALTER TABLE t DISCAR ...
- MySQL 独立表空间恢复案例
创建表的时候就会得到元数据.可以通过定义的方式对表的元数据进行生成 这个地方要注意的是 独立表空间当中 ibd & frm分别存储的是什么数据? 表空间:文件系统,为了更好的扩容数据库的存 ...
随机推荐
- php全文搜索代码
在PHP中实现全文搜索,你可以使用多种方法,具体取决于你的数据存储方式和需求.如果你的数据存储在MySQL数据库中,你可以利用MySQL的全文搜索功能(FULLTEXT).如果你需要更复杂的搜索功能, ...
- 在ubuntu系统下,安装opencv各个版本
要在Linux系统上安装OpenCV库,你可以通过包管理器(如apt)来安装.以下是详细的步骤,包括如何在/usr/local/lib或/usr/lib/x86_64-linux-gnu目录下安装Op ...
- golang 使用goto进行多错误处理
goto 语句介绍 在 Go 语言中,可以通过goto语句跳转到标签,进行代码间的无条件跳转.另外,goto语句在快速跳出循环.避免重复退出方面可以简化代码实现过程,但在结构化程序设计中一般不主张使用 ...
- dify 1.0.1无法在ollama下新增LLM模型
原来在0.15很正常,升到1.0.0之后就不行 了,再后来1.0.1出来后,以为问题都解决了,没想到还是有问题. 具体是:添加ollama是容易了,但是添加模型(比如deepsek)还是不行.表现为点 ...
- RabbitMQ 消息实现过程+事务+消息确认
服务端(生产者) 1.引用 rabbitmq 包 2.建立连接工厂 connectionfactory 3.创建 频道 createchannel 4.在频道中 绑定消息队列 5.发布basicpub ...
- 阅读IDEA生成的equals方法--java进阶day05
1.IDEA生成的equals方法 虽然我们之前写了equals方法,但IDEA中可以快速生成equals方法,因此,我们要能看懂IDEA生成的equals方法 1.if(this==o) 2.if( ...
- 小白必看的java完整下载攻略!(在Typora中有图片参考)
Java下载 在浏览器上搜索JDK(2024年最新版是22,本人下载的是21) 点击官网下载,会跳到Oracle官网,需要注册账号才可下载 根据自己的电脑型号选择下载(本人下载的是64的) 正常情况下 ...
- 学习unigui【27】像pg的jsonb一样编辑json。
var I: Integer; CurrentObject: TJSONObject; FieldName: string; Pair: TJSONPair;function CreateJS ...
- 搞笑的ini文件读取ReadSectionValues
用fdconnection自动生成连接文本,将其写入ini文件,用于程序的读写.ini文件见后面. Lfini.ReadSection('LocalConnection',lstrings); Sho ...
- 一个Bug让人类科技倒退几十年?
大家好,我是良许. 前几天在直播的时候,问了直播间的小伙伴有没人知道「千年虫」这种神奇的「生物」的,居然没有一人能够答得上来的. 所以,今天就跟大家科普一下这个人类历史上最大的 Bug . 1. 全世 ...