以下内容出自 《MySQL 实战 45 讲》
13 | 为什么表数据删掉一半,表文件大小不变?
参数 innodb_file_per_table
表数据既可以存在共享表空间里,也可以是单独的文件。这个行为是由参数 innodb_file_per_table 控制的:
- ON 表示的是,每个 InnoDB 表数据存储在一个以 .ibd 为后缀的文件中
- OFF 表示的是,表的数据放在系统共享表空间,也就是跟数据字典放在一起
默认值为 ON。
数据删除流程
InnoDB 里的数据都是用 B+ 树的结构组织的。
当删除数据记录时,InnoDB 引擎会把对应的数据记录标记为删除,之后这个被标记为删除的位置,会被复用。
如果删除了一个数据页上的所有记录,整个数据页就会被复用。
记录的复用:只限于符合范围条件的数据。
数据页的复用:可复用到任何位置。
delete 命令其实只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。这些可以复用的没被使用的空间,也称为 “空洞”。
空洞的原因:
- 删除表记录,被删除的记录只是被标记删除,索引值所在的空间能被复用,但是没有真正的删除。
- 新增表记录,如果索引的值是随机分散的,那么会造成数据页的分裂,也会造成空洞
- 更新索引上的值,实际上是把旧值标记为删除,然后新增一个新值,旧值虽然能被复用,但是还是造成了空洞
只有把这些空洞去掉,就能达到收缩表空间的目的。而重建表,就可以达到这样的目的。
重建表
使用 alter table A engine=InnoDB
命令来重建表。
在重建表的过程中,允许对表 A 做增删改操作。即 Online DDL。
Online DDL ,重建表的流程:
1、建立一个临时文件,扫描表 A 主键的所有数据页;
2、用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;
3、生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中,对应的是图中 state2 的状态;
4、临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件,对应的就是图中 state3 的状态;
5、用临时文件替换表 A 的数据文件。
DDL 之前是要拿 MDL 写锁的,这样还能叫 Online DDL 吗?
alter 语句在启动的时候需要获取 MDL 写锁,但是这个写锁在真正拷贝数据之前就退化成读锁了。
为什么要退化呢?为了实现 Online,MDL 读锁不会阻塞增删改操作。
那为什么不干脆直接解锁呢?为了保护自己,禁止其他线程对这个表同时做 DDL。
Online 和 inplace
Online的意义是代表在操作表时不会阻塞其增删改的过程
inplace的意义是修改表时直接操作其原表,操作在引擎层内部完成.
与之相对的是copy方式,操作方式是新建一个修改后的表,再将原表的内容一条一条的拷贝到新表上,其效率更慢
- DDL 过程如果是 Online 的,就一定是 inplace 的;
- 反过来未必,也就是说 inplace 的 DDL,有可能不是 Online 的。截止到 MySQL 8.0,添加全文索引(FULLTEXT index)和空间索引 (SPATIAL index) 就属于这种情况。
optimize table、analyze table 和 alter table 这三种方式重建表的区别
- alter table 重建表,减少页空洞
- analyze table 重新统计索引信息
- optimize table 重建+重新统计