【发布时间】:2013-10-23 22:12:53
【问题描述】:
我有一个涉及两个表的查询:表A 有很多行,并包含一个名为b_id 的字段,它引用表B 中的一条记录,该表有大约30 个不同的行。表A 在b_id 上有一个索引,表B 在name 列上有一个索引。
我的查询如下所示:
SELECT COUNT(A.id) FROM A INNER JOIN B ON B.id = A.b_id WHERE (B.name != 'dummy') AND <condition>;
condition 是表 A 上的一些随机条件(我有很多,都表现出相同的行为)。
这个查询非常慢(耗时 2 秒以北),并且使用解释,显示查询优化器从表 B 开始,得出大约 29 行,然后扫描表 A。执行STRAIGHT_JOIN,将订单翻转,查询立即运行。
我不是黑魔法的粉丝,所以我决定尝试其他方法:为B 中名称为dummy 的记录找出id,假设为23,然后简化查询到:
SELECT COUNT(A.id) FROM A WHERE (b_id != 23) AND <condition>;
令我惊讶的是,这个查询实际上比直接连接慢,需要一秒钟。
关于为什么加入比简化查询更快的任何想法?
更新:根据 cmets 中的请求,解释的输出:
直接连接:
+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
| 1 | SIMPLE | A | ALL | b_id | NULL | NULL | NULL | 200707 | Using where |
| 1 | SIMPLE | B | eq_ref | PRIMARY,id_name | PRIMARY | 4 | schema.A.b_id | 1 | Using where |
+----+-------------+-------+--------+-----------------+---------+---------+---------------+--------+-------------+
没有加入:
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | A | ALL | b_id | NULL | NULL | NULL | 200707 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
更新 2: 尝试了另一种变体:
SELECT COUNT(A.id) FROM A WHERE b_id IN (<all the ids except for 23>) AND <condition>;
这比 no join 运行得快,但仍然比 join 慢,所以看起来不等式操作是造成部分性能损失的原因,但不是全部。
【问题讨论】:
-
b.id有索引吗? -
这两个中的一个
EXPLAIN可以告诉你很多,很可能是 MySQL 选择的索引在第二个中不是最优的,但被JOIN强制考虑一个b_id上的索引实际上更有益。我的建议是:查看EXPLAIN,查看可能的索引,在测试环境中使用FORCE INDEX来了解可能性(避免在生产中使用它们,除非您非常确定 i>),如果您当前的单个索引不是最优的,并且您通常需要在您的条件下组合超过 1 个,则考虑组合索引。 -
我能提供的唯一猜测是 Table
A根本没有索引或索引不佳,而 TableB索引完美。因此,在 JOIN 之后可以使用B的索引。 -
@EugenRieck 是的,确实如此
-
请原谅我的提问,但为了绝对肯定,您能否澄清一下您是如何排除查询缓存的?