大家好,我是啊粥,昨天我们介绍了 MySQL 索引的模型。
文末,我留了一个问题,在我们执行 delete 命令删除数据的时候,为什么表数据文件大小没变化。
这也是我以前面试遇到过的一个问题,今天就给大家分享一下具体原因。
一般来说,我们如果想删除整个变的情况下,我们会直接使用 drop table 来回收表空间,但这是建立在我们以后不再用这张表时。
但是我们在业务开发中,更长碰到的是使用 delete 来删除具体的业务数据行,InnoDB 在处理删除的时候会将空间地址标记为删除,但是并不会真正删除。因为它有可能会复用这个位置,所以磁盘文件的大小并不会缩小。
比如昨天文章提到的那个个例子,我把那张图再重新贴过来给大家看一下
T4 这条记录被删除后,如果插入一个 ID 是 5 的行,可以直接复用这个空间,不需要再重新分配存储空间。但如果插入的是一个 ID 是 8 的行,就不能复用这个位置了。
删除单条记录,则这个单条记录的空间可以被复用,进一步地,如果我们用 delete 命令把整个表的数据删除呢?
结果就是,所有的数据页都会被标记为可复用。但是磁盘上,数据文件并不会对应变小,因为它所申请的表空间只不过是被标记为复用,并没有真正回收。
由此我们得出,delete 命令其实只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。也就是说,通过 delete 命令是不能回收表空间的。
这些可以复用,而没有被使用的空间,看起来就像是“空洞”。
实际上,不止是删除数据会造成空洞,插入数据也会。如果数据是按照索引递增顺序插入的,那么索引是紧凑的。但如果数据是随机插入的,就可能造成索引的数据页分裂。
一般来说,经过大量增删改的表,都是可能是存在空洞的。
所以,如果能够把这些空洞去掉,就能达到收缩表空间的目的。而重建表,就可以达到这样的目的。
这里,你可以使用 alter table test engine=InnoDB
命令来重建表。在 MySQL 5.5 版本之前,MySQL 会自动完成转存数据、交换表名、删除旧表的操作。
显然,花时间最多的步骤是往临时表插入数据的过程,如果在这个过程中,有新的数据要写入到表 A 的话,就会造成数据丢失。
因此,在整个 DDL 过程中,表 test 中不能有更新。也就是说,这个 DDL 不是 Online 的。
而在 MySQL 5.6 版本开始引入的 Online DDL,对这个操作流程做了优化。我
引入了 Online DDL 之后,重建表的流程
建立一个临时文件,扫描表 test 主键的所有数据页;
用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;
生成临时文件的过程中,将所有对 test 的操作记录在一个日志文件(row log)中;
临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 test 相同的数据文件;
用临时文件替换表 test 的数据文件。
DDL Online 由于日志文件记录和重放操作这个功能的存在,这个方案在重建表的过程中,允许对表 test 做增删改操作,这也就是 Online DDL 名字的来源。
通过重建表,我们就可以真正的回收表空间。
总结一下,我们今天主要分享了为什么在 delete 删除数据的时候,表的数据文件大小并不会产生变化的原因,主要是因为 InnoDB 引擎在处理 delete 删除数据时,会把删除行的位置标记为可复用,而不是真正删除,所以才会出现这种数据删了但是表文件大小没变的情况。
好了,我们今天的内容到此结束了,最近两天我们分享了索引模型是什么,明天开始我会分享 MySQL 中索引具体怎么用。
我是程序员啊粥,关注我,我们一起在技术海洋中向上生长。