删除行会导致 MySQL 中数据库存储空间增加

Ric*_*ith 6 mysql innodb

我刚刚观察到一些非常奇怪的事情,我希望有人可以向我解释。我在 Linux 虚拟服务器上有一个 MySQL 5.5.58 数据库,其中包含 InnoDB 表。其中一张表被调用stats_archive,并且在普通使用中是只写的:它永远不会被读取或删除。其内容纯粹出于法律合规目的而保留一定时间,并且每月的 cronjob 应该删除旧条目。不幸的是,cronjob 默默地失败了,结果导致表变得过大。今天早上我尝试删除数据:

master:~# du -sh /var/lib/mysql
6.3G /var/lib/mysql

master:~# mysql -u root -p

mysql> select count(*) from stats_archive;
+-----------+
|  count(*) |
+-----------+
|  26339050 |
+-----------+
1 row in set (39.40 sec)

mysql> delete from stats_archive where archive_date < '2018-01-01';
Query OK, 24628026 rows affected (7 min 17.61 sec)

master:~# du -sh /var/lib/mysql
7.4G /var/lib/mysql
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,MySQL 使用的存储增长了 1GB 多一点。当我执行此操作时,没有其他明显的数据库活动。删除不是在未提交的事务中完成的,因此数据库不应该仍然保留它以防我回滚。

额外的 1GB 空间(毫不奇怪)已被使用/var/lib/mysql/ibdata1,据我所知,该文件永远不会缩小,所以我一直坚持使用它,直到我可以做一些重大的事情,例如删除所有数据库,从备份恢复和设置innodb_file_per_table=1(它是目前不是)。我会在适当的时候这样做。

但我真正想知道的是为什么会发生这种情况,每次我从数据库中删除行时都会发生同样的情况吗?

注意:不是这个问题的重复。这个问题是关于存储未释放的问题,这在 InnoDB 中是众所周知的,并且与我链接到的问题本质上相同。我的问题是关于删除导致存储使用量显着增长

Sha*_*dow 6

即使您从中删除记录,InnoDB 表的大小确实会增加。根本原因是已知的事实(或错误),即 InnoDB 不会释放或回收已删除记录使用的空间。

为了支持事务功能(回滚、一致读取等),InnoDB 采用多版本控制,这意味着它可以维护同一记录的多个版本。多个版本存储在InnoDB 表空间的撤消日志部分中。撤消日志部分可以无限增长。

由于删除数据也是一种修改,以前版本的记录存储在撤消日志部分,导致文件大小显着增长。


Ric*_*mes 6

(Shadow 很好地解释了原因;我将讨论现在该做什么。)

最好的做法是以PARTITION周或月为单位(无论什么合理并导致 20-60 个分区)。然后DROP PARTITION反而慢得多DELETE。并REORGANIZE PARTITION获得一个新的分区。更多详细信息请参见此处

在您刚刚遇到的情况下,更好的方法是复制要保留的行。这是因为您只保留 10% 的行;新桌子会小得多。有关如何执行创建复制重命名的更多讨论,请参阅此处。

同时,您可以(花费几分钟的时间来收拾桌子)通过执行以下操作来清理它OPTIMIZE TABLE。如果您不喜欢将其捆绑在一起,请执行上面的创建-复制-重命名操作。