baf*_*mca 22 mysql innodb myisam delete mysql-5.0
我继承了一个数据库,并希望对其进行清理和加速。我有一个包含 30,000,000 行的表,其中许多是由于我们程序员的错误而插入的垃圾数据。在添加任何新的、更优化的索引之前,我将表从 MyISAM 转换为 InnoDB,并希望删除大量包含垃圾数据的行。
数据库是 MySQL 5.0,我有服务器的 root 访问权限。我首先通过 Adminer 运行这些命令,然后通过 phpMyAdmin 运行,结果相同。
我正在运行的命令是,
DELETE
FROM `tablename`
WHERE `columnname` LIKE '-%'
Run Code Online (Sandbox Code Playgroud)
本质上,删除此列中以 dash 开头的任何内容-。
它运行了大约 3-5 分钟,然后当我查看进程列表时,它消失了。
然后我跑,
SELECT *
FROM `tablename`
WHERE `columnname` LIKE '-%'
Run Code Online (Sandbox Code Playgroud)
它返回数百万行。
为什么我的删除语句没有完成?
PS,我知道 MySQL 5.0 已经过时了。我正在将数据库移动到 MySQL 5.6 w InnoDB(可能是 MariaDB 10 w XtraDB),但在此之前,我希望按原样使用 DB 来回答这个问题。
——
编辑已删除,请参阅我的答案。
Rol*_*DBA 24
请看InnoDB的架构(图片来自Percona CTO Vadim Tkachenko)

您正在删除的行正在写入撤消日志中。在删除期间,文件 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;
Run Code Online (Sandbox Code Playgroud)
您可以先针对表的 MyISAM 版本执行此操作。然后,将其转换为 InnoDB。
baf*_*mca 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
Run Code Online (Sandbox Code Playgroud)
通过谷歌搜索错误,我发现解决方案是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)
Run Code Online (Sandbox Code Playgroud)
然后我能够运行命令并在大约 27 分钟内删除 2300 万条记录。
对于那些好奇innodb_buffer_pool_size应该设置什么的人,请注意您拥有多少 RAM,然后查看此线程,该线程给出了以 GB 为单位的建议估计值。
Ric*_*mes 12
罗兰的建议可以通过同时做这两件事来加快一些速度:
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;
Run Code Online (Sandbox Code Playgroud)
但这里有一个博客,解释了如何分块进行大 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
Run Code Online (Sandbox Code Playgroud)