删除庞大的MySQL表的最快方法

时间:2009-05-18 19:22:51

标签: mysql innodb

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

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

11 个答案:

答案 0 :(得分:124)

(由于Google的搜索结果有所提高,我认为更多的指示可能会很方便。)

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

CREATE TABLE new_foo LIKE foo;

RENAME TABLE foo TO old_foo, new_foo TO foo;

DROP TABLE old_foo;

完成

答案 1 :(得分:47)

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

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

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

答案 2 :(得分:7)

难道你不能抓住架构下拉表并重新创建吗?

答案 3 :(得分:7)

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

DELETE from table_name LIMIT 1000;

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

将其放入循环中,直到删除所有行。

请尝试这样做,因为它实际上会起作用。这需要一些时间,但它会奏效。

答案 4 :(得分:3)

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

答案 5 :(得分:1)

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

答案 6 :(得分:1)

如果您只是想完全摆脱桌面,为什么不简单地放弃呢?

答案 7 :(得分:1)

截断很快,通常在几秒或更短的时间。如果花了30分钟,你可能有一些外键引用你正在截断的表。可能还存在锁定问题。

Truncate实际上和人们可以清空表一样有效,但是你可能必须删除外键引用,除非你想要擦除这些表。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

searlea的answer很不错,但正如评论中所述,你在战斗中丢失了外键。 此解决方案类似:truncate在一秒钟内执行,但保留外键。

诀窍是我们禁用/启用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;