58

I have an enormous MySQL (InnoDB) database with millions of rows in the sessions table that were created by an unrelated, malfunctioning crawler running on the same server as ours. Unfortunately, I have to fix the mess now.

If I try to truncate table sessions; it seems to take an inordinately long time (upwards of 30 minutes). I don't care about the data; I just want to have the table wiped out as quickly as possible. Is there a quicker way, or will I have to just stick it out overnight?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Kyle Kaitan
  • 1,761
  • 3
  • 19
  • 29

11 Answers11

160

(As this turned up high in Google's results, I thought a little more instruction might be handy.)

MySQL has a convenient way to create empty tables like existing tables, and an atomic table rename command. Together, this is a fast way to clear out data:

CREATE TABLE new_foo LIKE foo;

RENAME TABLE foo TO old_foo, new_foo TO foo;

DROP TABLE old_foo;

Done

searlea
  • 8,173
  • 4
  • 34
  • 37
  • 3
    Perfect decision, native, pure and logically excellent. Loooove this. – Arthur Kushman May 23 '12 at 11:08
  • I have a question, it could be obvious - why did you rename `foo`->`old_foo` instead of deleting it and renaming `new_foo`->`foo` after that? – Sergey Telshevsky Oct 17 '12 at 14:49
  • 1
    Vlakarados, this way there is always a table with the name foo... if you dropped foo and then renamed new_foo to foo.. especially with high traffic, clients would trigger SQL errors. – Caleb Gray Oct 18 '12 at 16:47
  • 17
    It's worth noting that this method will delete any foreign key associations between the table you're deleting and other tables. – Carlos P May 29 '13 at 19:53
  • The problem with this if you have two or more processes running this and you dont use some kind of queue mechanisme they will both probably fail – Waqleh Apr 04 '17 at 09:44
  • 1
    I get `errno: 150 - Foreign key constraint is incorrectly formed` – brandones Aug 20 '20 at 02:13
  • 1
    Oh, that's because I had done this once already with these same tables, leaving all the old foreign key constraints referring to the no-longer-existent `old_foo`. I wish I'd taken @CarlosP 's note more seriously. – brandones Aug 20 '20 at 22:52
52

The quickest way is to use DROP TABLE to drop the table completely and recreate it using the same definition. If you have no foreign key constraints on the table then you should do that.

If you're using MySQL version greater than 5.0.3, this will happen automatically with a TRUNCATE. You might get some useful information out of the manual as well, it describes how a TRUNCATE works with FK constraints. http://dev.mysql.com/doc/refman/5.0/en/truncate-table.html

EDIT: TRUNCATE is not the same as a drop or a DELETE FROM. For those that are confused about the differences, please check the manual link above. TRUNCATE will act the same as a drop if it can (if there are no FK's), otherwise it acts like a DELETE FROM with no where clause.

EDIT: If you have a large table, your MariaDB/MySQL is running with a binlog_format as ROW and you execute a DELETE without a predicate/WHERE clause, you are going to have issues to keep up the replication or even, to keep your Galera nodes running without hitting a flow control state. Also, binary logs can get your disk full. Be careful.

Bianchi
  • 18
  • 4
womp
  • 115,835
  • 26
  • 236
  • 269
10

The best way I have found of doing this with MySQL is:

DELETE from table_name LIMIT 1000;

Or 10,000 (depending on how fast it happens).

Put that in a loop until all the rows are deleted.

Please do try this as it will actually work. It will take some time, but it will work.

adnan.
  • 239
  • 1
  • 2
  • 8
  • 2
    I'm sorry, but if you are actually going to delete the rows, why not simply do "delete from table_name" ? – shylent May 19 '09 at 03:13
  • 8
    Have you done this before with a large table? "delete from ..." will usually just take up large amounts of CPU and take longer. The fewer rows there are, the faster the deletes become. Try it sometime. This is not an intellectual exercise, this actually works with MySQL. – adnan. May 19 '09 at 03:30
  • 1
    Also this way you can monitor the progress of the deletes. – adnan. May 19 '09 at 03:31
  • Well, yes. I have done 'delete from ...' on a really large dataset (think hundreds of millions) and yes it took a very long time, but that is to be expected (it also took extremely long time for the table to shrink after that). with LIMIT I just break it up into multiple deletes, but the total time will be just the same, right?.. – shylent May 19 '09 at 17:31
  • ... One thing I do NOT get though, is the part with WHERE 1=1. What purpose does it exactly serve? As far as I know, it doesn't get processed in any way, the optimizer just drops conditions like this (that can not influence the outcome of the query in any way), so the WHERE clause might've not even been here altogether. Care to clarify? Note, that I am not trying to find a mistake in your reasoning, I am just really curious, as I don't quite understand what you've meant. – shylent May 19 '09 at 17:35
  • This works really well for Percona in a master-master cluster when your deletes continually fail due to space constraints on the hard drive – Steve Buzonas Jun 17 '14 at 16:45
  • 2
    I tested this. I found my DROP query was taking enormous time and sometime failing also. number of records:717,36,563 I tried DELETE with LIMIT... superfast. +1 thanks – cjava Aug 27 '15 at 15:48
  • 2
    If using LIMIT with DELETE, you should also use ORDER BY. – w00t Oct 05 '15 at 12:20
  • @w00t unless you don't care about the order of deletion i.e. delete any x rows from the table or have a specific where clause that restricts the rows matched e.g. WHERE id < some_id – sactiw Oct 14 '16 at 13:14
  • 1
    this actually worked very well.. had a table with 1 mill and this is the only way to clean it, no idea why even "drop" wasn't working – Robert Sinclair Nov 14 '17 at 01:28
9

Couldn't you grab the schema drop the table and recreate it?

Nate
  • 30,286
  • 23
  • 113
  • 184
  • I thought "truncate" was a synonym for "delete from". But I've checked and in recent versions it actually drops and recreates the table, yes. (Unless it is referenced in one FK or another). – shylent May 18 '09 at 19:33
  • That is what I thought, apparently thats not the case; however, others are also saying to use drop -- there must be some difference in the way that truncate works. – Nate May 18 '09 at 20:02
  • Truncate will remove the rows from the table, leaving the schema intact. Drop will literally delete the table. If there's indices on sessions, that may be what's causing it to take a while. Cf. http://stackoverflow.com/questions/135653/difference-between-drop-table-and-truncate-table – J. Polfer May 18 '09 at 20:50
  • What would you do if there were indices on the table? – John Feminella May 18 '09 at 21:05
  • If the indexes are the problem, drop them first, truncate, then recreate the indexes. But dropping the table wholesale (including temporarily dropping any foreign key references first) is likely the best solution over all. – Will Hartung May 19 '09 at 03:41
3

drop table should be the fastest way to get rid of it.

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
1

Have you tried to use "drop"? I've used it on tables over 20GB and it always completes in seconds.

Zenshai
  • 10,307
  • 2
  • 19
  • 18
1

If you just want to get rid of the table altogether, why not simply drop it?

shylent
  • 10,076
  • 6
  • 38
  • 55
1

Truncate is fast, usually on the order of seconds or less. If it took 30 minutes, you probably had a case of some foreign keys referencing the table you were truncating. There may also be locking issues involved.

Truncate is effectively as efficient as one can empty a table, but you may have to remove the foreign key references unless you want those tables scrubbed as well.

Jeff Ferland
  • 17,832
  • 7
  • 46
  • 76
0

searlea's answer is nice, but as stated in the comments, you lose the foreign keys during the fight. this solution is similar: the truncate is executed within a second, but you keep the foreign keys.

The trick is that we disable/enable the FK checks.

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;

Extended answer - Delete all but some rows

My problem was: Because of a crazy script, my table was for with 7.000.000 junk rows. I needed to delete 99% of data in this table, this is why i needed to copy What I Want To Keep in a tmp table before deleteting.

These Foo Rows i needed to keep were depending on other tables, that have foreign keys, and indexes.

something like that:

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
)

but this query was always timing out after 1 hour. So i had to do it like this:

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);

I theory, because indexes are setup correctly, i think both ways of populating NewFoo should have been the same, but practicaly it didn't.

This is why in some cases, you could do like this:

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;
Community
  • 1
  • 1
Xavier
  • 440
  • 4
  • 11
0

We had these issues. We no longer use the database as a session store with Rails 2.x and the cookie store. However, dropping the table is a decent solution. You may want to consider stopping the mysql service, temporarily disable logging, start things up in safe mode and then do your drop/create. When done, turn on your logging again.

Brian Hogan
  • 3,033
  • 21
  • 18
0

I'm not sure why it's taking so long. But perhaps try a rename, and recreate a blank table. Then you can drop the "extra" table without worrying how long it takes.

Brent Baisley
  • 962
  • 1
  • 6
  • 4