【发布时间】:2010-10-23 04:30:19
【问题描述】:
在数据库方面,我是一个相对新手。我们正在使用 MySQL,我目前正在尝试加速似乎需要一段时间才能运行的 SQL 语句。我在 SO 上四处寻找类似的问题,但没有找到。
目标是删除表 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 相似,但它只包含 id 和 h 列。
另外,分析结果如下:
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
已解决
感谢所有回复和 cmets。他们当然让我思考这个问题。感谢 dotjoe 通过提出简单的问题“是否有其他表引用 a.id?”让我摆脱了这个问题
问题是表 A 上有一个 DELETE TRIGGER,它调用存储过程来更新另外两个表 C 和 D。表 C 有一个返回到 a.id 的 FK,并且在做了一些与该 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 和我的删除时间与 mattkemp 报告的时间一致。这对我来说是一个非常微妙的错误,因为在最后一分钟增加了一些额外的功能。事实证明,正如 Daniel 所说,大多数建议的替代 DELETE/SELECT 语句最终花费的时间基本相同,并且正如 soulmerge 所提到的,该语句很漂亮根据我需要做的事情,我将能够构建的最好的东西。一旦我为另一个表 C 提供了索引,我的 DELETE 速度很快。
事后分析:
从这个练习中学到了两个教训。首先,很明显,我没有利用 EXPLAIN 语句的强大功能来更好地了解我的 SQL 查询的影响。这是一个菜鸟的错误,所以我不会为此而自责。我会从那个错误中吸取教训。其次,有问题的代码是“快速完成”心态的结果,而设计/测试不充分导致这个问题没有尽快出现。如果我生成了几个相当大的测试数据集作为这个新功能的测试输入,我就不会浪费我和你的时间了。我在数据库方面的测试缺乏我的应用程序方面的深度。现在我有机会改进它。
【问题讨论】:
-
+1 礼貌的回答和良好的跟进数据
-
以下查询需要多长时间:SELECT b.id FROM b JOIN a ON b.id = a.id
-
这是来自 mbeckish 的一个好问题。请同时执行“SET profiling = 1;”,运行“SELECT b.id FROM b JOIN a ON b.id = a.id”,然后执行“SHOW PROFILE;”。您可以在问题中发布输出...
-
我的表已满(见我的回答)“SELECT count(b.id) FROM b JOIN a ON b.id = a.id;”耗时 0.07 秒。
-
我假设您是运行删除时唯一连接的人?是否有任何其他表引用 a.id?
标签: mysql performance sql-delete sql-execution-plan