在30,000,000行表上的DELETE命令未完成


22

我继承了一个数据库,并希望清理并加快它的速度。我有一个表,其中包含30,000,000行,由于代表程序员的错误,其中许多行都是垃圾数据。在添加任何新的,更优化的索引之前,我已将表从MyISAM转换为InnoDB,并希望删除很多包含垃圾数据的行。

数据库是MySQL 5.0,我具有对该服务器的root访问权限。我首先通过Adminer运行这些命令,然后通过phpMyAdmin运行,结果均相同。

我正在运行的命令是,

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'

本质上,删除此列中以破折号开头的所有内容-

它运行大约3-5分钟,然后当我查看进程列表时,它就消失了。

然后我跑

SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'

它返回数百万行。

为什么我的删除语句没有完成?

PS,我知道MySQL 5.0是过时的。我正在努力将数据库移至MySQL 5.6 w InnoDB(也许是MariaDB 10 w XtraDB),但是直到发生这种情况之前,我一直希望使用DB来解决此问题。

-

编辑已删除,请参阅我的答案。

Answers:


24

请看一下InnoDB的体系结构(图片来自Percona CTO Vadim Tkachenko)

InnoDB管道

您要删除的行将被写入撤消日志。在删除期间,文件ibdata1应该现在正在增长。根据mysqlperformanceblog.com的Reasons for run-away main Innodb Tablespace

  • 大量交易变更
  • 非常长的交易
  • 吹扫螺纹滞后

在您的情况下,原因1将会占用一个回滚段以及一些撤消空间,因为您要删除行。这些行必须位于ibdata1中,直到删除完成。该空间在逻辑上被丢弃,但磁盘空间不会缩小。

您需要立即删除该删除。终止删除查询后,它将回滚已删除的行。

您可以这样做:

CREATE TABLE tablename_new LIKE tablename;
INSERT INTO tablename_new SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%';
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

您可以首先针对表的MyISAM版本执行此操作。然后,将其转换为InnoDB。


21

我想我们可能已经过于复杂的是在需要的答案在我的情况。我毫不怀疑,Roland和Rick James都正确地创建了一个临时表,只注入了通过筛选器的行,NOT LIKE '-%'但是对我来说解决方案“更轻松”,因为直到现在和现在我都没有意识到一个重要的错误。我深表歉意。

我在mysql交互式提示中运行查询,并注意到错误消息,

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
ERROR 1206 (HY000): The total number of locks exceeds the lock table size

通过Googleing错误,我发现解决方案innodb_buffer_pool_size通过/etc/my.cnf文件增加并重新启动mysql守护程序。对于我的服务器,它设置为默认值,8M然后增加到默认值1G(服务器有32GB,这是当前唯一的InnoDB表)。

mysql> DELETE FROM `slugs` WHERE `slug` LIKE '-%';
Query OK, 23517226 rows affected (27 min 33.23 sec)

然后,我能够运行命令并在大约27分钟内删除2300万条记录。

对于那些好奇innodb_buffer_pool_size应该设置什么的人,请注意您有多少RAM,然后查看该线程该线程以GB为单位给出建议的估计值。


12

通过同时执行以下两项操作,可以加快Roland的建议:

CREATE TABLE tablename_new LIKE tablename;
ALTER TABLE tablename_new ENGINE = InnoDB;
INSERT INTO tablename_new 
    SELECT * FROM tablename WHERE `columnname` NOT LIKE '-%' ORDER BY primary_key;
RENAME TABLE
    tablename TO tablename_old,
    tablename_new TO tablename
;
DROP TABLE tablename_old;

但是这里有一个博客,该博客解释了如何分块进行大的DELETE,而不是看起来像是永久地做:http : //mysql.rjweb.org/doc.php/deletebig 要点是通过PK遍历表格,进行1K行一次。(当然,还有更多细节需要注意。)

这个博客解决了向InnoDB转换时的潜在陷阱:http : //mysql.rjweb.org/doc.php/myisam2innodb


5

我的本能是通过限制查询结果的数量并多次运行查询来进行多个较小的删除:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%' LIMIT 1000000

这种方法的缺点:每次删除将花费越来越长的时间。这是因为它需要跳过越来越多与不匹配的行WHERE
瑞克·詹姆斯

的确如此,但是如果此过程不是很经常发生,那么多次完整的表扫描就不会像要解决的原始问题那样糟糕,这是由于撤消日志的大小,查询从未完成。
kristianp

有效点。(我会LIMIT降低;例如10000。)
瑞克·詹姆斯

4

最简单的解决方案是根本不这样做-进行较小的删除,这样可以更轻松地进行处理。

在这种情况下,我建议尝试按顺序删除表单:

DELETE
FROM `tablename`
WHERE `columnname` LIKE '-a%'

2

也许您可以执行以下操作:

  • 添加一个名为的新字段deleted
  • 进行类似的更新UPDATE tablename SET deleted=1 WHERE `columnname` LIKE '-a%'
  • 设置cron为在夜间将其删除。

更新时间可能与删除时间一样长。
瑞克·詹姆斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.