155

我有一张使用 InnoDB 存储引擎的 MySQL 表;它包含大约 2M 数据行。当我从表中删除数据行时,它没有释放分配的磁盘空间。optimize table运行该命令后,ibdata1 文件的大小也没有减小。

有没有办法从 MySQL 回收磁盘空间?

我的处境很糟糕;该应用程序在大约 50 个不同的位置运行,现在几乎所有位置都出现了磁盘空间不足的问题。

4

11 回答 11

153

MySQL 不会减小 ibdata1 的大小。曾经。即使您使用optimize table从已删除记录中释放使用的空间,它也会在以后重用它。

另一种方法是配置要使用的服务器innodb_file_per_table,但这需要备份、删除数据库和恢复。积极的一面是表的 .ibd 文件在optimize table.

于 2009-08-14T23:13:03.787 回答
50

我自己也遇到了同样的问题。

发生的情况是,即使您删除数据库,innodb 仍然不会释放磁盘空间。我必须导出,停止 mysql,手动删除文件,启动 mysql,创建数据库和用户,然后导入。感谢上帝,我只有 200MB 的行,但它节省了 250GB 的 innodb 文件。

设计失败。

于 2009-12-25T20:55:02.897 回答
27

如果您不使用innodb_file_per_table,则可以回收磁盘空间,但非常乏味,并且需要大量停机时间。

How To非常深入 - 但我在下面粘贴了相关部分。

请务必在转储中保留架构的副本。

目前,您无法从系统表空间中删除数据文件。要减小系统表空间大小,请使用以下过程:

使用 mysqldump 转储所有 InnoDB 表。

停止服务器。

删除所有现有的表空间文件,包括 ibdata 和 ib_log 文件。如果要保留信息的备份副本,请在删除 MySQL 安装中的文件之前将所有 ib* 文件复制到另一个位置。

删除 InnoDB 表的任何 .frm 文件。

配置一个新的表空间。

重新启动服务器。

导入转储文件。

于 2012-01-19T19:45:16.680 回答
11

十年后,我也遇到了同样的问题。我通过以下方式解决了它:

  • 我优化了所有剩下的数据库。
  • 我在服务上重新启动了我的计算机和 MySQL (Windows+r --> services.msc)

就这些 :)

于 2019-08-13T08:49:10.590 回答
3

今天遇到了这个问题(最初提出这个问题 11 年后),并且能够通过删除表并再次创建它来解决它。我不必重新安装数据库或转储和恢复、修改存储、更改表空间等 - 没有这些。

我正在使用 InnoDB 但不是 innodb_file_per_table 所以即使在我从表中删除了 900K 行之后,数据库大小也没有改变。所以我删除了表并再次创建它。

在我的情况下,我的表被清理为零行,所以我很容易删除表,但保留我运行的结构

create table mynewtable as select * from myoldtable where 1=2;

其次是

drop table myoldtable;

这将我的数据库大小从 5G 降至 400MB

于 2021-03-12T13:07:35.343 回答
3

我找到的最短方法是:

ALTER TABLE YOURTABLE ENGINE=InnoDB

在截断或删除不需要的记录后,我运行了这个并且表空间缩小了。

这是 Shlomi Noach 的一篇不错的文章,我在其中找到了这个(以及更多相关信息)。

归功于 Shlomi Noach。希望能帮助到你。以防万一我也将其粘贴在这里:

使用 InnoDB 时,您有两种方式来管理表空间存储:

将所有内容放入一个大文件中(可选拆分)。每张桌子有一个文件。我将讨论这两个选项的优缺点,并努力说服 innodb_file_per_table 更可取。

单个表空间

将所有内容放在一个大文件中意味着所有方案中的所有表和索引都在该文件中“混合”在一起。

这允许以下很好的属性:可用空间可以在不同的表和不同的方案之间共享。因此,如果我从日志表中清除许多行,那么现在未使用的空间可能会被任何其他表的新行占用。

这个同样好的属性也转化为一个不太好的属性:数据可以在表空间中大大分散。

InnoDB 表空间的一个令人讨厌的特性是它们从不收缩。所以在从日志表中清除这些行之后,表空间文件(通常是 ibdata1)仍然保持相同的存储。它不会将存储释放到文件系统。

我不止一次地看到某些表是如何无人看管的,直到磁盘空间达到 90% 并且 SMS 通知开始四处响起。

在这种情况下几乎没有什么可做的。好吧,人们总是可以清除行。当然,这个空间会被 InnoDB 重用。但是拥有一个占用大约 80-90% 磁盘空间的文件是性能灾难。这意味着圆盘针需要移动很远的距离。整体磁盘性能非常低。

解决此问题的最佳方法是设置一个新的从属设备(在清除行之后),并将数据转储到该从属设备中。

InnoDB 热备份

有趣的是,ibbackup 实用程序会照原样复制表空间文件。如果是 120GB,其中只有 30GB 被使用,您仍然会得到 120GB 的备份和恢复。

mysqldump,mk 并行转储

如果您只有原始机器可以使用,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 表。但希望工作将朝着这个方向进行。

于 2021-11-09T09:51:43.260 回答
1

解决空间回收问题的其他方法是,在表中创建多个分区 - 基于范围、基于值的分区,然后删除/截断分区以回收空间,这将释放存储在特定分区中的整个数据使用的空间。

当您为表引入分区时,表架构中需要进行一些更改,例如 - 唯一键、包含分区列的索引等。

于 2015-02-10T07:22:00.640 回答
0

一年前,我在 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备份

问题解决!!

于 2020-05-21T14:51:22.077 回答
0

如果 OPTIMIZE 不能解决您的问题,请尝试:

ALTER TABLE tbl_name ENGINE=INNODB, ALGORITHM=INPLACE, LOCK=NONE;

或者

ALTER TABLE tbl_name FORCE, ALGORITHM=INPLACE, LOCK=NONE;
于 2021-06-23T15:36:46.367 回答
0

我有同样的问题。logging_event我从表中删除了所有数据。但是,logging_event.ipd仍然消耗更多15GB的数据。
所以,我已经通过以下步骤解决了它。

  1. 从现有表创建新表。
    CREATE TABLE logging_event_new like logging_event;

  2. 删除实际表。
    DROP TABLE logging_event;

  3. 将新表重命名为旧表
    RENAME TABLE logging_event_new to logging_event;

而且,它对我有用。
现在,我有更多15GB可用空间。

于 2021-12-16T14:25:51.413 回答
-2

从 MySQL Inodb 引擎的表中删除数据后,有几种方法可以回收磁盘空间

如果您从一开始就没有使用 innodb_file_per_table,则转储所有数据,删除所有文件,重新创建数据库并再次导入数据是唯一的方法(检查上面 FlipMcF 的答案)

如果您使用的是 innodb_file_per_table,您可以尝试

  1. 如果您可以删除所有数据,则截断命令将删除数据并为您回收磁盘空间。
  2. Alter table 命令将删除并重新创建表,以便它可以回收磁盘空间。因此,删除数据后,运行不改变任何内容的更改表以释放硬盘(即:表 TBL_A 具有字符集 uf8,删除数据后运行 ALTER TABLE TBL_A 字符集 utf8 -> 此命令不会从您的表中更改任何内容,但它会使 mysql 重新创建您的表并重新获得磁盘空间
  3. 像 TBL_A 一样创建 TBL_B 。将要保留的选择数据从 TBL_A 插入到 TBL_B。删除 TBL_A,并将 TBL_B 重命名为 TBL_A。如果 TBL_A 和需要删除的数据很大,这种方式非常有效(MySQL innodb 中的删除命令性能很差)
于 2016-04-25T17:07:27.273 回答