参数innodb_file_per_table
这个参数设置为ON,表示每个表数据单独存在一个文件中,这时如果执行drop命令,系统会直接删除表文件。
这个参数设置为off时,所有表的数据和索引都存在共享的.ibdata文件,即使表删掉了,磁盘空间也不会回收,而是将表数据标记为可复用。
系统表空间
所有表的数据和索引都存储在共享的.ibdata
文件
数据删除其实是标记为可复用
每个b+数的叶子节点是一个数据页,一个数据页包含很多条记录,一条记录被删除时,仅仅是这个记录的位置被标记为可复用,不管是主键索引还是二级索引,数据都是按照索引的顺序排好的,当一条记录被标记为可复用时,当有一条记录可以插在这个被复用的位置上时,就直接复用了这个位置,但是如果这条记录不能插在这个位置上就不能复用,比如一个主键索引树上,一个数据页有R1(100,数据),R2(200,数据),R3(300,数据),当R2被删除标记为可复用时,有个记录为100~300之间的都能复用这个位置,但是超过这个范围的就不行了。
当一个数据页都被标记为可复用时,就没有范围了,当需要新开辟一个数据页时,直接复用这个数据页。
所以,即使我们使用delete删除一个表的所有记录,磁盘文件也是不会减小的,而是都标记成可复用。
而这些可以复用但是没被复用的空间就是空洞。
删除数据会产生空洞,插入数据也会
在数据页内的数据很紧凑的情况下(不是100,200,300而是1,2,3,4,5这种)
假如一个数据页A已经满了,这时再插入一个比较靠后的位置,就会把这个新数据和数据页A后面的记录放在另一个新申请的数据页中。这就导致了A后面的记录变成空洞。
更新索引上 的值是先删除一个旧的值,再插入一个新的值
这也会产生空洞
通过重建表来收缩表空间,填补空洞
使用alter table A engine=InnoDB命令来重建表
(MYSQL5.5之前,默认的隐含意思是alter table t engine=innodb,ALGORITHM=copy)
MYSQL5.5之前,这条语句的执行逻辑是这样的:
首先创建一个临时表B,再将A中的数据一条一条按照顺序读写到表B中
这时B中数据是按照顺序插入的,没有空洞。
最后将A和B互换(重命名),删除表B。完成表A的重建
Online DDL
在上面这个流程中,最耗时的就是拷贝数据这个部分了,这个过程中如果写数据到A中,就会造成数据丢失。
Online DDL: 在对表结构进行更改时(执行DDL语句),同时支持对表数据进行增删改查。
此时重建表的逻辑就变成了
(MYSQL5.5之后,默认的隐含意思是alter table t engine=innodb,ALGORITHM=inplace)
首先创建临时文件B,在拷贝数据到临时文件B期间,将对表A的数据增删改查记录到一个日志文件row log中。
在拷贝完成后,将日志文件对表A数据的更改应用到临时文件B中,这样临时文件B中的数据旧和是一样的了
最后用临时文件B替换表A的数据文件
注意到这里和MYsql5.5之前有个不同的点是,之前的B是创建的表,是在server创建的,而这里是创建的临时文件,是在innodb内部创建的。对于server来说没有将表A的数据迁移到其他表,因为inno代办对其数据文件进行了更换。是在 原表上直接进行DDL操作,所以这种方式叫做inplace
DML写锁退化成读锁
这个流程中,在Alter 语句启动时会获取DML元数据写锁,防止其他线程来对这个进行结构的更改,同时DML写锁会阻塞数据的增删改查操作,但是读锁不会。所以在进行拷贝数据时,DML写锁会退化成DML读锁,DML读锁只会阻塞DML写锁,但是不会阻塞增删改查。
Online和inplace
DDL过程如果是Online的,就一定是inplace的
原因
因为Online DDl依赖于inplace算法,在重建表时,如果是使用的COPY模式,那就会创建新的表来作为临时中间表,这会阻塞增删改查,而inplace可以不创建新的表,仅仅是创建临时文件。
并且inplace在对DML写锁的持有上进行了优化,在进行拷贝数据到临时文件时就退化成读锁了。
但是一个DDL是inplace的,就不一定是Online的
原因
Online DDL指的是在进行DDL时不阻塞DML(数据的增删改查)。
即使是inplace地添加全文索引和添加空间索引时(在原表上创建索引而不创建临时表),仍然不能进行DML操作。