mysql - 删除匹配行的更快方法?

标签 mysql performance sql-delete sql-execution-plan

在数据库方面,我是一个相对新手。我们正在使用 MySQL,我目前正在尝试加速似乎需要一段时间才能运行的 SQL 语句。我环顾四周寻找类似的问题,但没有找到。
目标是删除表 A 中在表 B 中具有匹配 id 的所有行。
我目前正在做以下事情:

DELETE FROM a WHERE EXISTS (SELECT b.id FROM b WHERE b.id = a.id);
表 a 中大约有 100K 行,表 b 中大约有 22K 行。 'id' 列是两个表的 PK。
这个语句在我的测试机器上运行大约需要 3 分钟 - Pentium D、XP SP3、2GB ram、MySQL 5.0.67。这对我来说似乎很慢。也许不是,但我希望加快速度。有没有更好/更快的方法来实现这一目标?

编辑:
一些可能有用的附加信息。表 A 和 B 的结构与我为创建表 B 所做的以下操作相同:
CREATE TABLE b LIKE a;
表 a(以及表 b)有一些索引来帮助加快对其进行的查询。同样,我是 DB 工作中的相对新手,仍在学习中。我不知道这对事物有多大影响,如果有的话。我认为它确实有影响,因为索引也必须清理,对吗?我还想知道是否还有其他可能影响速度的数据库设置。
另外,我正在使用 INNO DB。

以下是一些可能对您有所帮助的其他信息。
表 A 具有与此类似的结构(我对此进行了一些清理):
DROP TABLE IF EXISTS `frobozz`.`a`;
CREATE TABLE  `frobozz`.`a` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `fk_g` varchar(30) NOT NULL,
  `h` int(10) unsigned default NULL,
  `i` longtext,
  `j` bigint(20) NOT NULL,
  `k` bigint(20) default NULL,
  `l` varchar(45) NOT NULL,
  `m` int(10) unsigned default NULL,
  `n` varchar(20) default NULL,
  `o` bigint(20) NOT NULL,
  `p` tinyint(1) NOT NULL,
  PRIMARY KEY  USING BTREE (`id`),
  KEY `idx_l` (`l`),
  KEY `idx_h` USING BTREE (`h`),
  KEY `idx_m` USING BTREE (`m`),
  KEY `idx_fk_g` USING BTREE (`fk_g`),
  KEY `fk_g_frobozz` (`id`,`fk_g`),
  CONSTRAINT `fk_g_frobozz` FOREIGN KEY (`fk_g`) REFERENCES `frotz` (`g`)
) ENGINE=InnoDB AUTO_INCREMENT=179369 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
我怀疑问题的一部分是该表有许多索引。
表 B 看起来与表 B 相似,但它只包含列 idh .
此外,分析结果如下:
starting 0.000018
checking query cache for query 0.000044
checking permissions 0.000005
Opening tables 0.000009
init 0.000019
optimizing 0.000004
executing 0.000043
end 0.000005
end 0.000002
query end 0.000003
freeing items 0.000007
logging slow query 0.000002
cleaning up 0.000002

已解决
感谢所有的回复和评论。他们当然让我思考这个问题。感谢 多乔通过问一个简单的问题“是否有其他表引用 a.id?”,让我摆脱这个问题。
问题是表 A 上有一个 DELETE TRIGGER,它调用一个存储过程来更新另外两个表 C 和 D。表 C 有一个 FK 回到 a.id 并且在存储过程中做了一些与该 id 相关的事情之后,它有这样的声明,
DELETE FROM c WHERE c.id = theId;
我查看了 EXPLAIN 语句并将其重写为,
EXPLAIN SELECT * FROM c WHERE c.other_id = 12345;
所以,我可以看到这是在做什么,它给了我以下信息:
id            1
select_type   SIMPLE
table         c
type          ALL
possible_keys NULL
key           NULL
key_len       NULL
ref           NULL
rows          2633
Extra         using where
这告诉我,这是一个痛苦的操作,因为它将被调用 22500 次(对于被删除的给定数据集),这就是问题所在。一旦我在 other_id 列上创建了一个 INDEX 并重新运行 EXPLAIN,我得到:
id            1
select_type   SIMPLE
table         c
type          ref
possible_keys Index_1
key           Index_1
key_len       8
ref           const
rows          1
Extra         
好多了,事实上真的很棒。
我补充说,Index_1 和我的删除时间与 报告的时间一致。马特肯普 .这对我来说是一个非常微妙的错误,因为在最后一分钟硬塞了一些额外的功能。事实证明,大多数建议的替代 DELETE/SELECT 语句,如 丹尼尔声明,最终花费的时间与 基本相同灵魂合并提到,该声明几乎是我能够根据我需要做的事情构建的最好的声明。一旦我为另一个表 C 提供了索引,我的 DELETE 就很快了。
验尸 :
从这个练习中吸取了两个教训。首先,很明显我没有利用 EXPLAIN 语句的强大功能来更好地了解我的 SQL 查询的影响。这是一个新手错误,所以我不会因为那个而自责。我会从那个错误中吸取教训。其次,有问题的代码是“快速完成”心态的结果,设计/测试不充分导致这个问题没有早点出现。如果我生成了几个相当大的测试数据集作为这个新功能的测试输入,我不会浪费我的时间也不会浪费你的时间。我在数据库方面的测试缺乏应用程序方面的深度。现在我有机会改进它。
Reference: EXPLAIN Statement

最佳答案

从 InnoDB 中删除数据是您可以请求的最昂贵的操作。正如您已经发现查询本身不是问题 - 无论如何,它们中的大多数都会优化为相同的执行计划。

虽然可能很难理解为什么所有情况下的 DELETE 都是最慢的,但有一个相当简单的解释。 InnoDB 是一个事务存储引擎。这意味着,如果您的查询在中途中止,所有记录仍将保留,就好像什么也没发生一样。一旦完成,一切都会在同一瞬间消失。在 DELETE 期间,连接到服务器的其他客户端将看到记录,直到您的 DELETE 完成。

为了实现这一点,InnoDB 使用了一种称为 MVCC(多版本并发控制)的技术。它的主要作用是为每个连接提供整个数据库的快照 View ,就像事务的第一个语句开始时一样。为了实现这一点,InnoDB 内部的每条记录都可以有多个值——每个快照一个。这也是为什么在 InnoDB 上进行计数需要一些时间 - 这取决于您当时看到的快照状态。

对于您的 DELETE 事务,根据您的查询条件标识的每条记录都会被标记为删除。由于其他客户端可能同时访问数据,因此无法立即将它们从表中删除,因为它们必须查看各自的快照以保证删除的原子性。

一旦所有记录都被标记为删除,事务就成功提交。即便如此,在 DELETE 事务之前使用快照值的所有其他事务也结束之前,它们也无法立即从实际数据页面中删除。

因此,实际上您的 3 分钟并没有那么慢,考虑到必须修改所有记录才能以事务安全的方式将它们删除。当语句运行时,您可能会“听到”硬盘在工作。这是由访问所有行引起的。
为了提高性能,您可以尝试增加服务器的 InnoDB 缓冲池大小,并尝试在 DELETE 时限制对数据库的其他访问,从而减少 InnoDB 每条记录必须维护的历史版本数。
使用额外的内存 InnoDB 可能能够将您的表(大部分)读入内存并避免一些磁盘搜索时间。

关于mysql - 删除匹配行的更快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/812512/

相关文章:

MySQL 连接包含价格、房间和季节的表

java - Java 中的 FPS 低

android - 如何删除 SQLite、Android 中表中的所有项目?

mysql - 为什么 MySQL DELETE 不能在子查询上使用索引?

sql - 递归 SELECT 帮助

php - 选项国家html与mysql

php - 如何使用 MVC 使用 PHP 表单更新 MySQL 数据库

mysql - 比 NOT IN(嵌套选择)更有效的查询

python:涉及幂级数的问题的效率

mysql - 从 wp_post 表中删除 ID