一个 InnoDB 表包含两部分,
-
表结构定义
在 MySQL 8.0 版本以前,存在以.frm 为后缀的文件里。
而 MySQL 8.0 版本,则已经允许把 放在 系统数据表中了。 -
数据
可以存在共享表空间里,也可以是单独的文件。
5.6.6 版本开始,它的默认值就单独的文件
一个表 单独存储为一个文件更容易管理,需要这个表的时候,通过 drop table 命令,系统就会直接删除这个文件。
而如果是放在共享表空间中,即使表删掉了,空间也是不会回收的。
遇到的更多的是删除某些行,
数据被删除了,但是空间却没有被回收
空洞的形成
删除:
要删掉 R4 这个记录,InnoDB 引擎只会把 R4 这个记录标记为删除。如果之后要再插入一个 ID 在 300 和 600 之间的记录时,可能会复用这个位置。但是,磁盘文件的大小并不会缩小。
如果删掉了一个数据页上的所有记录,页就 标记为可复用
相邻的两个数据页利用率都很小,系统就会把这两个页上的数据合到其中一个页上,另外一个数据页就被标记为可复用。
如果整个表的数据删除, 所有的数据页都会被标记为可复用。
磁盘上,文件不会变小。
插入 :
如果数据是按照索引递增顺序插入的,那么索引是紧凑的。
如果数据是随机插入的,就可能造成索引的数据页分裂。 分裂 会造成空洞
重建表
空间收缩,为了把表中存在的空洞去掉,
可以
新建一个与表 A 结构相同的表 B,然后按照主键 ID 递增的顺序,把数据一行一行地从表 A 里读出来再插入到表 B 中。
由于表 B 是新建的表,所以表 A 主键索引上的空洞,在表 B 中就都不存在了。
用表 B 替换 A,从效果上看,就起到了收缩表 A 空间的作用。
命令是
alter table A engine=InnoDB
命令来重建表。MySQL 会自动完成转存数据、交换表名、删除旧表的操作。
MySQL 5.6 版本开始引入的 Online DDL 就是这个复制的过程中 原表可以改
流程:
-
建立一个临时文件,扫描表 A 主键的所有数据页,用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;
生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中,对应的是图中 state2 的状态; -
临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件,对应的就是图中 state3 的状态;
-
用临时文件替换表 A 的数据文件。
在启动的时候需要获取 MDL 写锁,但是这个写锁在真正拷贝数据之前就退化成读锁了。
MDL 读锁不会阻塞增删改操作。
那为什么不干脆直接解锁呢?为了保护自己,禁止其他线程对这个表同时做 DDL。
网友评论