【问题标题】:Quickest way to delete enormous MySQL table删除巨大 MySQL 表的最快方法
【发布时间】:2010-10-27 03:09:36
【问题描述】:

我有一个巨大的 MySQL (InnoDB) 数据库,会话表中有数百万行,这些行是由与我们在同一台服务器上运行的不相关的故障爬虫创建的。不幸的是,我现在必须解决这个烂摊子。

如果我尝试truncate table sessions;,它似乎需要非常长的时间(超过 30 分钟)。我不关心数据;我只想让桌子尽快被消灭。有没有更快的方法,还是我必须一夜之间坚持下去?

【问题讨论】:

    标签: mysql innodb


    【解决方案1】:

    您不能抓住架构删除表并重新创建它吗?

    【讨论】:

    • 我认为“截断”是“删除”的同义词。但是我已经检查过,在最近的版本中,它实际上会删除并重新创建表,是的。 (除非它在一个 FK 或另一个 FK 中被引用)。
    • 我是这么想的,显然不是这样;但是,其他人也说要使用 drop —— truncate 的工作方式肯定有一些不同。
    • 截断将从表中删除行,保持架构不变。 Drop 将逐字删除该表。如果会话中有索引,那可能是导致它需要一段时间的原因。参照。 stackoverflow.com/questions/135653/…
    • 如果桌子上有索引,你会怎么做?
    • 如果索引是问题,请先删除它们,截断,然后重新创建索引。但整体删除表(包括暂时删除任何外键引用)可能是最好的解决方案。
    【解决方案2】:

    你试过用“drop”吗?我在超过 20GB 的表上使用过它,它总是在几秒钟内完成。

    【讨论】:

      【解决方案3】:

      drop table 应该是摆脱它的最快方法。

      【讨论】:

        【解决方案4】:

        如果你只是想彻底摆脱桌子,为什么不干脆drop它呢?

        【讨论】:

          【解决方案5】:

          最快的方法是使用 DROP TABLE 完全删除表并使用相同的定义重新创建它。如果您在表上没有外键约束,那么您应该这样做。

          如果您使用高于 5.0.3 的 MySQL 版本,这将通过 TRUNCATE 自动发生。您也可以从手册中获得一些有用的信息,它描述了 TRUNCATE 如何与 FK 约束一起工作。 http://dev.mysql.com/doc/refman/5.0/en/truncate-table.html

          编辑:TRUNCATE 与 drop 或 DELETE FROM 不同。对于那些对差异感到困惑的人,请查看上面的手动链接。如果可以的话,TRUNCATE 将与 drop 相同(如果没有 FK),否则它就像没有 where 子句的 DELETE FROM。

          编辑:如果您有一个大表,您的 MariaDB/MySQL 以 binlog_format 作为 ROW 运行,并且您在没有谓词/WHERE 子句的情况下执行 DELETE,您将遇到问题来保持复制甚至保持 Galera 节点运行而不会达到流控制状态。此外,二进制日志可以让您的磁盘满。小心点。

          【讨论】:

            【解决方案6】:

            我们遇到了这些问题。我们不再将数据库用作 Rails 2.x 和 cookie 存储的会话存储。但是,删除表是一个不错的解决方案。您可能需要考虑停止 mysql 服务,暂时禁用日志记录,以安全模式启动,然后进行删除/创建。完成后,再次打开您的日志记录。

            【讨论】:

              【解决方案7】:

              我发现使用 MySQL 执行此操作的最佳方法是:

              DELETE from table_name LIMIT 1000;
              

              或 10,000(取决于它发生的速度)。

              把它放在一个循环中,直到所有的行都被删除。

              请尝试一下,因为它确实有效。这需要一些时间,但它会起作用。

              【讨论】:

              • 很抱歉,如果您真的要删除行,为什么不简单地“从 table_name 删除”?
              • 你以前用大桌子做过这个吗? “从...中删除”通常只会占用大量 CPU 并且需要更长的时间。行数越少,删除速度就越快。有时间试试。这不是智力练习,这实际上适用于 MySQL。
              • 这样你也可以监控删除的进度。
              • 我对此进行了测试。我发现我的 DROP 查询花费了大量时间,有时也失败了。记录数:717,36,563 我尝试使用 LIMIT 删除...超快。 +1 谢谢
              • 如果使用 LIMIT 和 DELETE,你也应该使用 ORDER BY。
              【解决方案8】:

              截断速度很快,通常在几秒钟或更短的时间内。如果花费了 30 分钟,您可能会遇到一些外键引用您正在截断的表的情况。还可能涉及锁定问题。

              截断与清空表一样有效,但您可能必须删除外键引用,除非您也希望清理这些表。

              【讨论】:

                【解决方案9】:

                我不确定为什么要花这么长时间。但也许尝试重命名,并重新创建一个空白表。然后,您可以删除“额外”表,而不必担心需要多长时间。

                【讨论】:

                  【解决方案10】:

                  (由于这在 Google 的搜索结果中很高,我认为多一点说明可能会很方便。)

                  MySQL 有一个方便的方法来创建像现有表一样的空表,以及一个原子表重命名命令。总之,这是一种清除数据的快速方法:

                  CREATE TABLE new_foo LIKE foo;
                  
                  RENAME TABLE foo TO old_foo, new_foo TO foo;
                  
                  DROP TABLE old_foo;
                  

                  完成

                  【讨论】:

                  • 完美的决定,原生的,纯粹的,逻辑上的优秀。放开这个。
                  • 我有一个问题,这可能很明显 - 你为什么重命名 foo->old_foo 而不是删除它并重命名 new_foo->foo 之后?
                  • Vlakarados,这样总会有一个名为 foo 的表... >
                  • 值得注意的是,此方法将删除您要删除的表与其他表之间的任何外键关联。
                  • 如果你有两个或多个进程在运行它并且你不使用某种队列机制,那么问题可能会失败
                  【解决方案11】:

                  searlea 的answer 不错,但正如 cmets 中所述,您在战斗中会丢失外键。 此解决方案类似:截断在一秒钟内执行,但您保留外键。

                  诀窍在于我们禁用/启用 FK 检查。

                  SET FOREIGN_KEY_CHECKS=0;
                  CREATE TABLE NewFoo LIKE Foo;
                  insert into NewFoo SELECT * from Foo where What_You_Want_To_Keep  
                      
                  truncate table Foo;
                  insert into Foo SELECT * from NewFoo;
                  SET FOREIGN_KEY_CHECKS=1;
                  

                  扩展答案 - 删除除某些行之外的所有行

                  我的问题是:由于一个疯狂的脚本,我的表包含 7.000.000 行垃圾。我需要删除此表中 99% 的数据,这就是为什么我需要在删除之前将 我想要保留的内容复制到 tmp 表中。

                  我需要保留的这些 Foo 行取决于其他具有外键和索引的表。

                  类似的东西:

                  insert into NewFoo SELECT * from Foo where ID in (
                   SELECT distinct FooID from TableA 
                   union SELECT distinct FooID from TableB 
                   union SELECT distinct FooID from TableC
                  )
                  

                  但此查询总是在 1 小时后超时。 所以我不得不这样做:

                  CREATE TEMPORARY TABLE tmpFooIDS  ENGINE=MEMORY  AS (SELECT distinct FooID from TableA);
                  insert into tmpFooIDS SELECT distinct FooID from TableB
                  insert into tmpFooIDS SELECT distinct FooID from TableC
                  insert into NewFoo SELECT * from Foo where ID in (select ID from tmpFooIDS);
                  

                  我的理论,因为索引设置正确,我认为填充 NewFoo 的两种方式应该是相同的,但实际上它没有。

                  这就是为什么在某些情况下,您可以这样做:

                  SET FOREIGN_KEY_CHECKS=0;
                  CREATE TABLE NewFoo LIKE Foo;
                  
                  -- Alternative way of keeping some data.
                  CREATE TEMPORARY TABLE tmpFooIDS  ENGINE=MEMORY  AS (SELECT * from Foo where What_You_Want_To_Keep);
                  insert into tmpFooIDS SELECT ID from Foo left join Bar where OtherStuff_You_Want_To_Keep_Using_Bar
                  insert into NewFoo SELECT * from Foo where ID in (select ID from tmpFooIDS);
                  
                  truncate table Foo;
                  insert into Foo SELECT * from NewFoo;
                  SET FOREIGN_KEY_CHECKS=1;
                  

                  【讨论】:

                    猜你喜欢
                    • 2019-01-27
                    • 2018-12-11
                    • 1970-01-01
                    • 2016-11-26
                    • 2010-09-09
                    • 1970-01-01
                    • 2021-01-09
                    • 2015-06-10
                    • 2010-11-23
                    相关资源
                    最近更新 更多