概述

最近为了对 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 机制大致如下:

  1. ALTER语句在初始化启动的时候需要获取 MDL 写锁;

  2. MDL写锁在真正拷贝数据之前就退化成读锁,MDL 读锁不会阻塞增删改等 DML 操作,InnoDB把这些操作记入 ROW_LOG;

  3. 最后完成存量数据相关拷贝,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版本以前的重建表流程:

  1. MySQL服务层触发创建临时表,对源表加MDL锁,阻塞DML写而不阻塞DML读;

  2. MySQL服务层从目标表中逐行读取数据,写入到临时表;

  3. 数据拷贝完成后,禁止读写,删除目标表,把临时表重命名为目标表。

上面的流程大致如下图:

如何理解 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的表空间释放的更多相关文章

  1. [记录]一则清理MySQL大表以释放磁盘空间的案例

    一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...

  2. MySQL的表空间管理

    表空间: MySQL没有真正意义上的表空间管理. MySQL的Innodb包含两种表空间文件模式,默认的共享表空间和每个表分离的独立表空间. 一般来说,当数据量很小的时候建议使用共享表空间的管理方式. ...

  3. mysql通过表空间来恢复或者传递数据

    mysql的备份工具通常有 mysqldump ,mysqlpump(5.7后新特性)等备份工具,这里我们可以尝试使用表空间进行传递 方式是:拷贝数据文件+拷贝表空间   对应innodb引擎就是 i ...

  4. 谈谈什么是MySQL的表空间?

    今天我要跟你分享的话题是:"大家常说的表空间到底是什么?究竟什么又是数据表?" 这其实是一个概念性的知识点,当作拓展知识.涉及到的概念大家了解一下就好,涉及的参数,留个印象就好. ...

  5. 使用MySQL传输表空间迁移数据

    对于大表的迁移,如果使用mysqldump进行导出,然后重新导入到其它环境,速度是非常缓慢的.如果使用传输表空间,则可以解决这个问题. 测试使用传输表空间迁移表,基础环境如下:   源库 目标库 IP ...

  6. mysql共享表空间转独立表空间

    使用innodb_export_import.py脚本: https://github.com/thecpaneladmin/innodb-tools 安装MySQL-python模块: shell ...

  7. 浅析mysql 共享表空间与独享表空间以及他们之间的转化

        innodb这种引擎,与MYISAM引擎的区别很大.特别是它的数据存储格式等.对于innodb的数据结构,首先要解决两个概念性的问题: 共享表空间以及独占表空间.什么是共享表空间和独占表空间共 ...

  8. mysql 案例 ~ 表空间迁移数据与数据导入

    一  简介:mysql5.6+的表空间传输二 目的:复制数据到另一个表三 步骤   1 create table b like a ->创建一个空表   2 alter table b disc ...

  9. mysql之 表空间传输

    说明:MySQL(5.6.6及以上),innodb_file_per_table开启. 1.1. 操作步骤: 0. 目标服务器创建相同表结构1. 目的服务器: ALTER TABLE t DISCAR ...

  10. MySQL 独立表空间恢复案例

    创建表的时候就会得到元数据.可以通过定义的方式对表的元数据进行生成 这个地方要注意的是 独立表空间当中   ibd & frm分别存储的是什么数据? 表空间:文件系统,为了更好的扩容数据库的存 ...

随机推荐

  1. Processing模拟控制多台舵机-以距离为参数 程序参考

    又是一次课程学习的结束,辅导学生的过程也很受益,温故而知新.该组同学需要Arduino控制多达6个舵机,而且基于距离这一参数,在不同距离值之间会有不同的触发事件,也就是6个舵机转的角度都有所不同,而且 ...

  2. C# .net 压缩文件解压上传及文件压缩下载

    using ICSharpCode.SharpZipLib.Zip; using System; using System.Collections.Generic; using System.IO; ...

  3. mysql - 视图的操作 创建,修改,删除,查看

    只保存sql逻辑,不保存查询结果 视图可以看作是封装了多条sql语句,之后使用的时候就像普通表一样,而这个表上的字段则是创建视图时,select 后边跟的字段,支持列的别名. 创建 语法: creat ...

  4. offsetTop && offsetParent

    在迄今为止的一年里,做滚动动画的时候其实对一个概念比较模糊,就是一个元素在此文档中距离文档顶部的距离,一开始的想法是一个元素距离顶部的距离就是此元素同级的previous兄弟节点的高度和加上此元素的父 ...

  5. Linux性能分析-CPU上下文切换

    前言 在Linux性能分析-平均负载中,提到过一种导致平均负载升高的情况,就是有大量进程或线程等待cpu调度. 为什么大量进程或者线程等待CPU调度会导致负载升高呢? 当大量进程或者线程等待调度时,c ...

  6. 【网络】Windows在局域网配置DNS服务器

    [网络]Windows在局域网配置DNS服务器 零.需求 最近因为要搭建一个局域网视频聊天系统,需要用到HTTPS协议,HTTPS协议需要证书,证书需要用到域名,而且IP地址不太好记,就想着直接在聊天 ...

  7. SQLite 爬坑记录之无法设置自增约束

    场景重现 SQLite在设置自增约束(Autoincrement)的时候出现无法设置的情况: 问题原因 ... 解决办法 ...

  8. unigui显示uniTreeVview使用TUniTreeNode内存泄漏的问题【14】

    uniTreeVviewc创建一个tree,显示患者姓名(PatientName).因为需要用到患者ID(PatientID),所以使用help: TPatientTreeNode = class(T ...

  9. zk源码—4.会话的实现原理

    大纲 1.创建会话 (1)客户端的会话状态 (2)服务端的会话创建 (3)会话ID的初始化实现 (4)设置的会话超时时间没生效的原因 2.分桶策略和会话管理 (1)分桶策略和过期队列 (2)会话激活 ...

  10. JMeter用例数据分离

    1.编写接口用例文件 新建csv文件,以查询用户财富值和时长接口为例 参数说明: ${caseSeq}:用例编号 ${apiType}:api类型 ${apiSeq}:api版本号 ${apiName ...