最快的方法来删除巨大的MySQL表

我有一个巨大的MySQL(InnoDB)数据库在会话表中有数百万行,这是由与我们在同一台服务器上运行的无关的,发生故障的爬行程序创build的。 不幸的是,我现在必须解决这个混乱。

如果我尝试truncate table sessions; 似乎花了很长的时间(30分钟以上)。 我不关心数据; 我只是想尽快把桌子擦掉。 有没有更快的方法,还是我不得不坚持一夜之间?

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

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

编辑:TRUNCATE是不一样的一滴或删除从。 对于那些对这些差异感到困惑的人,请查看上面的手册链接。 如果TRUNCATE可以(如果没有FK的话)将TRUNCATE作为一个drop来执行,否则就像DELETE FROM一样,没有where子句。

(当谷歌的结果出现这种情况时,我认为多一点指导可能会得心应手。)

MySQL有一个方便的方法来创build像现有表一样的空表和一个atomic table rename命令。 总之,这是清除数据的一种快速方法:

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

完成

难道你不能抓住架构删除表并重新创build它吗?

我发现用MySQL做这件事的最好方法是:

 DELETE from table_name LIMIT 1000; 

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

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

请尝试这个,因为它会实际工作。 这将需要一些时间,但它会工作。

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

你有没有尝试使用“下降”? 我已经在超过20GB的表上使用它,它总是在几秒钟内完成。

如果你只是想摆脱桌子,为什么不简单地扔掉呢?

截断速度很快,通常在几秒或更less。 如果花了30分钟,你可能有一些外键引用你正在截取的表。 也可能涉及locking问题。

截断的效率和清空表的效率一样高,但是除非你想清除这些表,否则你可能不得不移除外键引用。

我们有这些问题。 我们不再使用数据库作为会话存储与Rails 2.x和cookie存储。 但是,放弃桌子是一个体面的解决scheme。 你可能要考虑停止mysql服务,暂时禁用日志logging,在安全模式下启动,然后进行删除/创build。 完成后,再次开启日志logging。

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

Searlea的回答很好,但正如评论中所述,你在战斗中失去了外键。 这个解决scheme是类似的:截断是在一秒钟内执行的,但你保持外键。

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

扩展答案 – 删除除一些行外的所有行

我的问题是: 由于一个疯狂的脚本,我的桌子上有7000.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;