1. 业务场景(即为什么要重建表)

由于前期的存储设计不合理,表里有个字段,用于存储大段字符串,导致存储到磁盘的空间极大,严重影响表相关的增删改查速度。
所有需要对表存储设计进行重构,并对表进行重建。

2.实践记录

我们业务场景是由软删字段的(delete_flag = 1 :已删除),所以可以delete所有已软删数据(当然做好表的备份)。
delete from TableName where delete_flag=1;
alter table TableName engine = innodb;

3.对实践的技术总结

之前有重建表相关的知识储备,刚好再看一遍,并做记录。下次是‘Mysql实战45讲’的课程的抄录。

参数innodb_file_per_table

表数据既可以存在共享表空间里,也可以是单独的文件。这个行为是由参数innodb_file_per_table控制的:
1.这个参数设置为off表示的是,表的数据放在系统共享表空间,也就是跟数据字典放在一起;
2.这个参数设置为on表示的是,每个innobd表数据存储在一个以 .ibd为后缀的文件中。
从Mysql5.6.6版本开始,它的默认值就是on了。
开启的优势:
易于管理,在不需要表时,通过drop table命令,直接删除表文件。
如果存在共享空间,即便把表删除,所占用的空间也不会被回收。
所以,将 innodb_file_per_table 设置为 ON,是更好的选择。

数据删除的流程

InnoDB里的数据都是用B+树的结构组织的。
在这里插入图片描述
假设,我们要删掉 R4 这个记录,InnoDB 引擎只会把 R4 这个记录标记为删除。如果之后要再插入一个 ID 在 300 和 600 之间的记录时,可能会复用这个位置。但是,磁盘文件的大小并不会缩小。

删除一条记录,仅仅是把这个记录标记为删除(这里跟undo.log/数据恢复有关),如果符合条件可以被复用(比如上面的这个例子,R4 这条记录被删除后,如果插入一个 ID 是 400 的行,可以直接复用这个空间。但如果插入的是一个 ID 是 800 的行,就不能复用这个位置了),但一般主键是自增id,几乎不会被复用。
InnoDB 的数据是按页存储的,如果删除一个数据页上记录,那么一整个数据页就可以被复用,并且可以被复用到任何位置。
所以如果我们用 delete 命令把整个表的数据删除,仅仅是,把所有的数据页都标记为可复用,磁盘上的文件大小并不会改变。

重建表

1.建一张结构一样的临时表,把表内的数据导入到临时表,直接删除旧表,然后将临时表替换为旧表,完成重建。
2.通过alter table tableName engine=innodb 命令来重建表。在MySQL5.5版本之前,这个命令的执行流程跟1操作差不多,区别只是在于这个临时表不需要你直接创建,MySQL会自动完成转存数据、交换表名、删除旧表的操作。
在这里插入图片描述

显然,花时间最多的步骤是往临时表插入数据的过程,如果在这个过程中,有新的数据要写入到表 A 的话,就会造成数据丢失。因此,在整个 DDL 过程中,旧表中不能有更新。也就是说,这个 DDL 不是 Online 的。
而在MySQL5.6版本开始引入的Online DDL,对这个操作流程做了优化:
1.建立一个临时文件,扫描表A主键的所有数据页;
2.用数据页中表A的记录生成B+树,存储到临时文件中;
3.生成临时文件的过程中,将所有对A的操作记录在一个日志文件(row log)中,对应的是图中state2的状态;
4.临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表A相同的数据文件,对应的就是图中state3的状态;
5.用临时文件替换表A的数据文件。
在这里插入图片描述
执行alter语句时,需要获取MDL写锁,但是这个写锁在真正拷贝数据之前就退化成读锁。
为了实现Online,MDL读锁不会阻塞增删改操作。
那为什么不干脆直接解锁呢?为了保护自己,禁止其他线程对这个表同时做DDL。

插曲

我们的MySQL部署在云服务器上,查看表所占的空间,是通过information_schema库的统计信息查看到的(我们并没有直接去服务器磁盘查看表空间)。但统计信息是有更新频率的,所以在我们重建表之后,发现表空间并没有减小。
统计信息更新之后,表空间减小很明显,由原先的12G减少到700MB。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐