MySQL InnoDB 从表中删除数据行后未释放磁盘空间
Posted
技术标签:
【中文标题】MySQL InnoDB 从表中删除数据行后未释放磁盘空间【英文标题】:MySQL InnoDB not releasing disk space after deleting data rows from table 【发布时间】:2010-11-19 05:57:02 【问题描述】:我有一张使用 InnoDB 存储引擎的 mysql 表;它包含大约 2M 数据行。当我从表中删除数据行时,它没有释放分配的磁盘空间。运行optimize table
命令后,ibdata1 文件的大小也没有减小。
有没有办法从 MySQL 中回收磁盘空间?
我的处境很糟糕;该应用程序在大约 50 个不同的位置运行,现在几乎所有位置都出现磁盘空间不足的问题。
【问题讨论】:
在运行优化命令后,ibdata1 文件的大小并没有减小。 我认为最好将该评论编辑到您的答案中,然后删除 ***.com/q/11751792/82114 的可能副本(但这个是第一个) “运行优化表命令后,ibdata1 文件的大小也没有减小”,这是因为您的innodb_file_per_table
已关闭。好消息是,在最新版本的 MySQL 中,此选项默认为 on
。
我运行“优化表 xxxx”并收到消息“表不支持优化,而是重新创建 + 分析。”之后在 shell 上运行“du -h /var/lib/mysql”,我可以看到数据库已经缩小了。
【参考方案1】:
我有同样的问题。我从logging_event
表中删除了所有数据。但是,logging_event.ipd
仍然消耗超过15GB
的数据。
所以,我已经按照以下步骤解决了。
从现有表创建新表。CREATE TABLE logging_event_new like logging_event;
删除实际表格。DROP TABLE logging_event;
将新表重命名为旧表RENAME TABLE logging_event_new to logging_event;
而且,它对我有用。
现在,我有超过15GB
可用空间。
【讨论】:
【参考方案2】:我找到的最短路径是:
ALTER TABLE YOURTABLE ENGINE=InnoDB
在截断或删除不需要的记录后,我运行了这个并且表空间缩小了。
Here is a nice article by Shlomi Noach, where I found (this and more information about) this.
感谢 Shlomi Noach。希望能帮助到你。以防万一我也将其粘贴在这里:
使用 InnoDB 时,您有两种管理表空间存储的方法:
将所有内容放入一个大文件中(可选择拆分)。 每张桌子有一个文件。 我将讨论这两个选项的优缺点,并努力说服 innodb_file_per_table 更可取。
单个表空间
将所有内容都放在一个大文件中意味着所有方案中的所有表和索引都在该文件中“混合”在一起。
这允许以下很好的属性:可用空间可以在不同的表和不同的方案之间共享。因此,如果我从日志表中清除许多行,那么现在未使用的空间可能会被任何其他表的新行占用。
同样好的属性也转化为一个不太好的属性:数据可能在表空间中非常分散。
InnoDB 表空间的一个恼人属性是它们从不收缩。所以在从日志表中清除这些行之后,表空间文件(通常是 ibdata1)仍然保持相同的存储。它不会向文件系统释放存储空间。
我不止一次地看到某些表是如何无人看管的,直到磁盘空间达到 90% 并且 SMS 通知开始四处响起。
在这种情况下几乎没有什么可做的。好吧,人们总是可以清除行。当然,这个空间会被 InnoDB 重用。但是拥有一个占用大约 80-90% 磁盘空间的文件是性能灾难。这意味着圆盘针需要移动很远的距离。整体磁盘性能非常低。
解决此问题的最佳方法是设置一个新的从属设备(在清除行之后),并将数据转储到该从属设备中。
InnoDB 热备份
有趣的是,ibbackup 实用程序会照原样复制表空间文件。如果是 120GB,其中仅使用了 30GB,您仍然会得到 120GB 的备份和恢复。
mysqldump,mk-parallel-dump
如果您只有原始机器可以使用,mysqldump 将是您的最佳选择。假设您只使用 InnoDB,使用 --single-transaction 的转储将完成这项工作。或者您可以使用 mk-parallel-dump 来加快速度(取决于您的转储方法和可访问性需求,注意锁定)。
innodb_file_per_table
使用此参数集,将为每个表创建一个 .ibd 文件。我们得到的是这样的:
表空间在不同的表之间不共享,当然也不在不同的方案之间。 每个文件都被视为自己的一个表空间。 同样,表空间的大小永远不会减少。 可以重新获得每个表空间的空间。 等待。最后两个似乎矛盾,不是吗?让我们解释一下。
在我们的日志表示例中,我们清除了许多行(最多删除了 90GB 的数据)。 .ibd 文件不会缩小。但我们可以这样做:
ALTER TABLE 日志引擎=InnoDB
将发生的情况是创建一个新的临时文件,并在其中重建表。只有现有数据会添加到新表中。完成后,原表被删除,新表重命名为原表。
当然,这需要很长时间,在此期间表被完全锁定:不允许写入和读取。但仍然 - 它允许我们重新获得磁盘空间。
使用新的 InnoDB 插件,在执行 TRUNCATE TABLE 日志语句时也会重新获得磁盘空间。
碎片并不像在单个表空间中那样糟糕:数据被限制在较小文件的边界内。
监控
关于 innodb_file_per_table 的另一个好处是可以在文件系统级别监控表大小。您不需要访问 MySQL、使用 SHOW TABLE STATUS 或查询 INFORMATION_SCHEMA。您可以在 MySQL 数据目录(和子目录)下查找前 10 个最大的文件,并监控它们的大小。您可以查看哪个表增长最快。
备份
最后,目前还不能通过复制 .ibd 文件来备份单个 InnoDB 表。但希望能朝着这个方向努力。
【讨论】:
【参考方案3】:如果 OPTIMIZE 不能解决您的问题,请尝试:
ALTER TABLE tbl_name ENGINE=INNODB, ALGORITHM=INPLACE, LOCK=NONE;
或
ALTER TABLE tbl_name FORCE, ALGORITHM=INPLACE, LOCK=NONE;
【讨论】:
【参考方案4】:MySQL 不会减小 ibdata1 的大小。曾经。即使您使用optimize table
来释放已删除记录中使用的空间,它也会在以后重复使用。
另一种方法是将服务器配置为使用innodb_file_per_table
,但这需要备份、删除数据库和还原。积极的一面是表的 .ibd 文件在optimize table
之后减少了。
【讨论】:
MySQL 5.5 docs about the InnoDB file-per-table mode state "要利用现有表的 [InnoDB file-per-table] 功能,您可以打开 file-per-table 设置并在现有表上运行ALTER TABLE t ENGINE=INNODB
。”这意味着您可以打开此功能,使用 ALTER TABLE 命令“转换”现有表以使用单独的 InnoDB 文件,然后优化表以缩小其大小。但是,一旦完成,您就必须弄清楚如何删除(巨大的)源 InnoDB 文件...
我想这在技术上回答了这个问题,但我希望大多数搜索这个主题的人都在寻找缩小/回收空间的实际过程,而这个答案没有提供。
@Manachi 该过程是“配置服务器以使用innodb_file_per_table”,“备份”服务器,“删除数据库”,停止mysql,删除.ibd,启动服务器并恢复数据库。使用 MySQL 5.5+,您可以使用 Josh 所说的,更改所有表后,停止服务器,删除巨大的 .ibd 并重新启动它。【参考方案5】:
今天(在最初提出该问题 11 年后)遇到了这个问题,并且能够通过删除表格并重新创建它来解决它。我不必重新安装数据库或转储和恢复、修改存储、更改表空间等 - 没有这些。
我使用的是 InnoDB 但不是 innodb_file_per_table,所以即使在我从表中删除了 900K 行之后,数据库大小也没有变化。所以我删除了表并重新创建了它。
在我的情况下,我的表被清理为零行,所以我很容易删除表,但保留我运行的结构
create table mynewtable as select * from myoldtable where 1=2;
紧随其后
drop table myoldtable;
这将我的数据库大小从 5G 降至 400MB
【讨论】:
欢迎来到 Stack Overflow。数据库的本质是数据不会因为记录被删除而释放回系统。根据我的研究,INNODB 没有像某些数据库那样的回收资源命令或类似命令。不过,在您的情况下,我很想尝试 TRUNCATE TABLE myoldtable; ...我希望这将是一个更简单的解决方案。 谢谢 - 忘记了 TRUNCATE。已在 Oracle 中广泛使用它,但没想到在这里尝试。将归档以供将来参考:) TRUNCATE TABLE 在我的情况下也不会释放磁盘空间。【参考方案6】:一年前,我在 mysql5.7 版本上也遇到了同样的问题,并且 ibdata1 占用了 150 Gb。 所以我添加了撤消表空间
进行 Mysqldump 备份 停止 mysql 服务 从数据目录中删除所有数据 在当前 my.cnf 中添加下面的 undo 表空间参数
#undo tablespace
innodb_undo_directory = /var/lib/mysql/
innodb_rollback_segments = 128
innodb_undo_tablespaces = 3
innodb_undo_logs = 128
innodb_max_undo_log_size=1G
innodb_undo_log_truncate = ON
启动mysql服务 存储mysqldump备份
问题解决了!!
【讨论】:
【参考方案7】:十年后,我也遇到了同样的问题。我通过以下方式解决了它:
我优化了剩下的所有数据库。 我重新启动了我的计算机和 MySQL on services (Windows+r --> services.msc)就是这样:)
【讨论】:
是的,只是“优化表 tbl_name;”和mysql重启 我不知道为什么这个投票不够。显然这应该是最佳答案。【参考方案8】:MySQL Inodb 引擎从表中删除数据后有几种方法可以回收磁盘空间
如果你一开始不使用innodb_file_per_table,转储所有数据,删除所有文件,重新创建数据库并再次导入数据是唯一的方法(检查上面FlipMcF的答案)
如果你使用的是innodb_file_per_table,你可以试试
-
如果您可以删除所有数据,则截断命令将删除数据并为您回收磁盘空间。
Alter table 命令将删除并重新创建表,以便它可以回收磁盘空间。因此,删除数据后,运行不改变任何内容的更改表以释放硬盘(即:表 TBL_A 具有字符集 uf8,删除数据后运行 ALTER TABLE TBL_A 字符集 utf8 -> 此命令不会从您的表中更改任何内容,但它会使 mysql 重新创建您的表并重新获得磁盘空间
像 TBL_A 一样创建 TBL_B 。将要保留的选择数据从 TBL_A 插入到 TBL_B。删除 TBL_A,并将 TBL_B 重命名为 TBL_A。如果 TBL_A 和需要删除的数据很大,这种方式非常有效(MySQL innodb 中的删除命令性能很差)
【讨论】:
请注意,选项 3 将丢弃基于 TBL_A 构建的视图/索引,从而损害性能并可能破坏应用程序。此外,如果 TBL_A 是外键源,您将无法删除它。同样,选项 2 应谨慎执行,如在封面之下。它与选项 3 相同。不确定索引/视图是否会在 ALTER 之后重新构建(怀疑)。一般来说,除了 TRUNCATE 之外,以上都不是业务质量实践,并且至少应该只在规定的维护时间内执行,并且要特别注意(超级和子)依赖关系。 嗨 @Dennis 选项 3 是我从 Percona 那里学到的,在这个答案中我只谈论想法,实际上需要通过代码逻辑处理所有约束和业务,所以它不能适合所有情况.还有一件事,这个问题是关于如何删除数据和释放磁盘。我认为我所有的解决方案都可以做到这一点,但在每个案例研究中,我们都需要选择合适的解决方案。有时你想要的只是截断未使用的大日志表【参考方案9】:解决空间回收问题的其他方法是,在表中创建多个分区 - 基于范围、基于值的分区,然后删除/截断分区以回收空间,这将释放存储在特定的分区。
当您为表引入分区时,表架构中需要进行一些更改,例如 - 唯一键、包含分区列的索引等。
【讨论】:
【参考方案10】:如果您不使用innodb_file_per_table,则可以回收磁盘空间,但非常繁琐,并且需要大量停机时间。
How To 非常深入 - 但我在下面粘贴了相关部分。
请务必在转储中保留架构的副本。
目前,您无法从系统表空间中删除数据文件。 要减小系统表空间大小,请使用以下过程:
使用 mysqldump 转储所有 InnoDB 表。
停止服务器。
删除所有现有的表空间文件,包括 ibdata 和 ib_log 文件。如果您想保留信息的备份副本, 然后在删除之前将所有 ib* 文件复制到另一个位置 MySQL 安装中的文件。
删除 InnoDB 表的所有 .frm 文件。
配置一个新的表空间。
重启服务器。
导入转储文件。
【讨论】:
感谢您提供这些步骤 - 因为“操作方法”链接不再包含此信息【参考方案11】:我自己也遇到了同样的问题。
发生的情况是,即使你删除了数据库,innodb 仍然不会释放磁盘空间。我必须导出,停止 mysql,手动删除文件,启动 mysql,创建数据库和用户,然后导入。感谢上帝,我只有 200MB 的行,但它节省了 250GB 的 innodb 文件。
设计失败。
【讨论】:
是的,这绝对是失败的。 MySql 5.5 有同样的问题:我运行“优化表”以减少 28GB 表的磁盘使用量。该操作可能试图对原始克隆进行优化克隆,这样做会耗尽分区上的所有空间。现在“优化表”失败了,即使我删除了整个数据库,分区上也没有空间......非常令人失望。 4 年多之后,我遇到了与 MySQL 相同的问题。 MS SQL 类似:dba.stackexchange.com/questions/47310/…以上是关于MySQL InnoDB 从表中删除数据行后未释放磁盘空间的主要内容,如果未能解决你的问题,请参考以下文章