一张日志表删掉了八成数据,文件大小一点没变。df看磁盘还是满的。du看目录也没小。运维以为是删除没生效,又删了一遍。删之前表是120G,删完还是120G,一点都没有动。
InnoDB把数据存在表空间里,开了innodb_file_per_table之后每张表一个ibd文件,5.6.6起这是默认。DELETE做的事是给行打上删除标记,页从索引里摘掉,空间留在表空间内部,成了空闲页。这些页可以被同一张表的新数据复用,但不会还给操作系统,所以文件不会缩。ibd文件里除了数据页,还有索引页和空闲页,文件大小反映的是曾经用过的峰值。表的元数据里记着下一个可用页的位置,删除只把这个链断开,页本身还在原地。只有DROP或者TRUNCATE才会把文件整个删掉。
information_schema的TABLES表里有DATA_FREE字段,单位是字节,就是这张表内部的空闲空间。查出来几十个G,跟删掉的数据量对得上,基本可以确认。查的时候别用SELECT COUNT,它扫全表,估不出空闲空间。DATA_FREE是估算值,跟实际释放量会差一点,够用来判断量级。
OPTIMIZE TABLE对InnoDB来说等于ALTER TABLE FORCE,会新建一个表空间,把数据按顺序搬过去,再换名。搬的过程中原表还能读写,但要占额外的磁盘和IO。重建期间会持有一把元数据锁,时间很短,可长事务没结束的话,OPTIMIZE会一直等着,把后面的查询全堵住。
你先看磁盘够不够。新表空间的大小约等于当前数据量。剩余空间不到这个数,优化会中途失败。原表数据不受影响,只是留下#sql临时文件。先用DATA_FREE估算,再留两成余量。中途失败的话临时文件会留在磁盘上,要手动清掉。
几百G的表别直接跑OPTIMIZE。一次重建要几个小时,期间IO打满。用pt-online-schema-change或者gh-ost,分块拷贝,可以控速,出问题能停。两个工具都是建一张影子表、分块拷数据、最后原子换名。增量同步的做法不同,pt-online-schema-change用触发器,gh-ost改成解析binlog。拷的过程中原表照常读写,gh-ost那条路对原表的写入影响更小。
如果整张表都要清,TRUNCATE比DELETE快得多,文件直接重建,空间立刻释放。代价是不能回滚,也不能带条件。TRUNCATE会重置自增ID,有外键引用的话要先处理约束,否则直接报错。
还有一处容易被忽略,undo表空间。长事务跑过之后undo文件会涨,哪怕事务结束了也不马上缩。8.0的undo表空间可以单独设自动收缩。5.7的innodb_undo_log_truncate默认关闭,打开后同样会截断。undo留在ibdata1里的时候没法单独缩,只能靠重建整个实例。
重建完ibd文件从120G回到20多G,df的数字跟着降下来。这类问题不是删除没生效,是空间留在文件里没交回去。删完顺手看一眼DATA_FREE,就不会再被文件大小骗一次。
A5创业网 版权所有