56

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

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

4

11 回答 11

148

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

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

CREATE TABLE new_foo LIKE foo;

RENAME TABLE foo TO old_foo, new_foo TO foo;

DROP TABLE old_foo;

完毕

于 2011-06-14T21:00:51.780 回答
52

最快的方法是使用 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 将与删除相同(如果没有 FK),否则它就像没有 where 子句的 DELETE FROM。

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

于 2009-05-18T19:28:13.133 回答
9

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

DELETE from table_name LIMIT 1000;

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

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

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

于 2009-05-19T03:11:28.143 回答
8

你不能抓住模式删除表并重新创建它吗?

于 2009-05-18T19:24:58.163 回答
3

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

于 2009-05-18T19:25:34.263 回答
1

如果您只是想完全摆脱桌子,为什么不干脆放下它呢?

于 2009-05-18T19:27:17.177 回答
1

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

于 2009-05-18T19:25:05.743 回答
1

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

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

于 2009-05-19T20:28:54.617 回答
0

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

于 2009-05-19T20:39:11.787 回答
0

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

于 2009-05-19T02:08:09.457 回答
0

searlea 的回答很好,但正如评论中所述,您在战斗中丢失了外键。 此解决方案类似:截断在一秒钟内执行,但您保留外键。

诀窍是我们禁用/启用 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;
于 2017-03-02T14:26:17.170 回答