【问题标题】:MySQL Inndob Delete/Purge rows from very large databasesMySQL Innodb 从非常大的数据库中删除/清除行
【发布时间】:2013-07-29 11:33:26
【问题描述】:

我在从 innodb 表中删除数据时遇到了一些问题,从我读到的大多数人所说的释放空间的唯一方法是导出想要的数据创建一个新故事并导入它。这似乎是一个非常垃圾处理方式,尤其是在将近 3tbs 的数据上。

我遇到的问题是删除超过 3 个月的数据以尝试释放磁盘空间,一旦删除数据,磁盘空间似乎没有被释放。有没有办法清除或永久删除行/数据以释放磁盘空间?

有没有更可靠的方法,无需删除数据库并重新启动服务以释放磁盘空间。

请一些人建议我处理删除大型数据库的最佳方法。

非常感谢您的宝贵时间。

谢谢:)

【问题讨论】:

标签: php mysql database innodb lamp


【解决方案1】:

一种相对有效的方法是使用database partitions 并通过删除分区来删除旧数据。它当然需要更复杂的维护,但它确实有效。

首先,启用 innodb_file_per_table 以便每个表(和分区)转到自己的文件而不是单个巨大的 ibdata 文件。

然后,创建一个分区表,每个时间范围(日、月、周,您选择)有一个分区,这会为您的数据集生成一些合理大小的文件。

create table foo(     
        tid INT(7) UNSIGNED NOT NULL,
        yearmonth INT(6) UNSIGNED NOT NULL,
        data varbinary(255) NOT NULL,
        PRIMARY KEY (tid, yearmonth) 
) engine=InnoDB
PARTITION BY RANGE(yearmonth) (
        PARTITION p201304 VALUES LESS THAN (201304),
        PARTITION p201305 VALUES LESS THAN (201305),
        PARTITION p201306 VALUES LESS THAN (201306)
);

查看数据库数据目录,您会发现每个分区都有一个文件。在此示例中,分区“p201304”将包含 yearmonth

实际上,我实际上使用了一个包含 UNIX 时间戳的整数列作为分区键 - 这样随着时间的推移调整分区的大小会更容易。分区边缘不需要匹配任何日历边界,它们可以每 100000 秒发生一次,或者任何导致合理数量的分区(数十个分区)的结果,同时仍然有足够小的数据文件。

然后,设置维护过程,为新数据创建新分区:ALTER TABLE foo ADD PARTITION (PARTITION p201307 VALUES LESS THAN (201307)),并删除旧分区:ALTER TABLE foo DROP PARTITION p201304。删除大分区几乎与删除文件一样快,而且实际上会释放磁盘空间。此外,它不会通过在其中散布空白空间来分散其他分区。

如果可能,请确保您的频繁查询仅访问一个或几个分区,方法是在 WHERE 子句中指定分区键(上例中的年月)或其范围 - 这将使它们运行得更快因为数据库不需要查看所有分区来查找您的数据。

【讨论】:

  • 我会根据你的建议试一试,非常感谢你的帮助。
  • 如果我想使用表中的 id 一次从多个表中删除,或者我需要使用连接,这将如何工作?
  • 使用分区架构,您将执行“ALTER TABLE foo DROP PARTITION p201304”操作,这实际上会从磁盘中删除单个分区文件,立即清除一个月的数据(或任何您的分区时间步长)碰巧是)。你不能做一个JOIN。您仍然可以像以前一样使用 DELETE(使用 JOIN 和所有这些)删除少量数据,但这不会给您带来分区删除的速度,并且在删除分区之前它不会回收磁盘空间。 dev.mysql.com/doc/refman/5.5/en/…
【解决方案2】:

即使您使用file_per_table 选项,您仍然会遇到此问题。 “修复”它的唯一方法是重建单个表:

OPTIMIZE TABLE bloated_table

请注意,这将在重建操作期间锁定表,并且您必须有足够的可用空间来容纳新表。在某些系统上,这是不切实际的。

如果您经常删除数据,您可能需要定期轮换整个表格。使用file_per_table 删除 InnoDB 下的表将几乎立即释放磁盘空间。如果您每月有一张表,您可以简单地删除代表三个月前数据的表。

使用这些很难看吗?是的。有替代方案吗?并不真地。您可以尝试进入table partitioning 兔子洞,但这往往会带来更多的麻烦。

【讨论】:

  • 如果磁盘已满,优化会失败吗?我猜这对于定期维护来说可能是一个不错的解决方案。我想我可能因为 db 磁盘空间为 100%(19gb 空闲)而错过了这艘船,我可能不得不将数据库痛苦地移动到更大的磁盘驱动器并开始定期删除/优化维护计划。这听起来像是解决问题的最佳方法吗?谢谢:)
  • OPTIMIZE TABLE 将创建该表的新副本,然后删除旧副本。如果您的表很大,并且您没有设置 file_per_table,那么该操作可以使您的 ibdata 文件按照新数据集的大小增长 - 之后它不会缩小。即使使用 file_per_table,在单个大表的最坏情况设置中,常规的 OPTIMIZE 计划可能会增加您的磁盘空间需求,因为您在 OPTIMIZE 期间需要空间用于表副本。如果你有很多大桌子,这不是问题,但它仍然会锁定你的桌子很长一段时间。
  • “每月表”是一个可行的选项,但是您需要在所有 INSERT/DELETE/UPDATE 操作中添加额外的特殊代码来选择正确的表。使用分区将魔力转移到分区创建/删除维护代码中。
  • @oh7lzb 一切都是为了挑选你的毒药:你宁愿在数据库中维护一个分区映射,还是更愿意使用在执行操作时分配正确表名的应用程序代码?两者都可以,但除非您是经验丰富的 DBA,否则管理分区可能会很麻烦。
  • 我对分区很满意,因为我不必修改 SELECT 来使用自定义魔法对多个表执行 UNION - 感谢分区,应用程序中的旧查询无需修改即可工作,并且简单的 SELECT 将根据需要查看一个或多个分区。毕竟,维护代码也不是太难——只需一个查询来不时地添加一个新分区,然后另一个查询来删除一个旧分区。这是一个最简单的选择,到目前为止我对我的选择很满意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-19
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多