为什么表数据删掉一半,表文件大小不变?

318 阅读3分钟

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

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

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

ps:

一个表单独存储为一个文件更容易管理,而且在你不需要这个表的时候,通过 drop table 命令,系统就会直接删除这个文件。而如果是放在共享表空间中,即使表删掉了,空间也是不会回收的。

如果我们用 delete 命令把整个表的数据删除呢?表文件有啥变化呢?为什么?

我们在删除一个语句的时候,只会将这个位置的数据删除,它的页和占用的东西还在,所以表文件的大小还是没变,我们可以对这个位置进行复用,如果我们删除了整个数据页,那么整个数据页都可以复用。

如果我们用 delete 命令把整个表的数据删除呢?结果就是,所有的数据页都会被标记为可复用。但是磁盘上,文件不会变小。你现在知道了,delete 命令其实只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。也就是说,通过 delete 命令是不能回收表空间的。这些可以复用,而没有被使用的空间,看起来就像是“空洞”。

数据页的复用跟记录的复用是一样的吗?

记录的复用,只限于符合范围条件的数据。(也就是说id为100删除,只能在id100这里操作)

而当整个页从 B+ 树里面摘掉以后,可以复用到任何位置。

怎么解决这个问题呢?(我有表a,想把a的表文件变小)

alter table A engine=InnoDB 重建表

你可以新建一个与表 A 结构相同的表 B,然后按照主键 ID 递增的顺序,把数据一行一行地从表 A 里读出来再插入到表 B 中。由于表 B 是新建的表,所以表 A 主键索引上的空洞,在表 B 中就都不存在了。

显然地,表 B 的主键索引更紧凑,数据页的利用率也更高。如果我们把表 B 作为临时表,数据从表 A 导入表 B 的操作完成后,用表 B 替换 A,从效果上看,就起到了收缩表 A 空间的作用。

以上流程是以前的mysql的版本,在MySQL 5.6 版本开始引入的 Online DDL,对这个操作流程做了优化。

新的流程为:

建立一个临时文件,扫描表 A 主键的所有数据页;

用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;

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

临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件,对应的就是图中 state3 的状态;

用临时文件替换表 A 的数据文件。

更新索引的值会造成空洞吗?

为什么说更新可能会导致位置改变?说再详细些应该是这样,其实更新操作,主键索引树的位置是不会变的,可能会变的是哪些二级索引树。