我们有一个相对简单的查询,可以在4个表中执行LEFT JOIN。 A是“主”表或层次结构中的最顶层表。 B链接到A,C链接到B.此外,X链接到A.所以层次结构基本上是
A
C => B => A
X => A
查询基本上是:
SELECT
a.*, b.*, c.*, x.*
FROM
a
LEFT JOIN b ON b.a_id = a.id
LEFT JOIN c ON c.b_id = b.id
LEFT JOIN x ON x.a_id = a.id
WHERE
b.flag = true
ORDER BY
x.date DESC
LIMIT 25
通过EXPLAIN
,我已确认正确的索引已到位,并且内置的MySQL查询优化器正确且正确地使用这些索引
所以这是奇怪的部分......
当我们按原样运行查询时,运行大约需要1.1秒。
然而,经过一些检查后,似乎如果我删除了大部分SELECT字段,我会得到显着速度提升。
因此,如果我们将其分为两步查询过程:
SELECT a.id
而不是SELECT *
a.id IN
再次执行查询1的结果而不是之前的结果结果截然不同。第一个查询为.03秒,第二个查询为.02。
在代码中执行这两步查询实际上为我们提供了20倍的性能提升。
所以这是我的问题:
这种类型的优化不应该在数据库引擎中完成吗?为什么实际SELECT的哪些字段的差异会对查询的整体性能产生影响?
在一天结束时,它只是选择完全相同的25行并返回这25行完全相同的全部内容。那么,为什么性能差异很大?
ADDED 2012-08-24 13:02 PM PDT
感谢eggyal和invertedSpear的反馈意见。首先,它不是一个缓存问题 - 我在每个方法之间交替执行多次运行两次查询(大约10次)的测试。第一个(单个查询)方法的结果平均为1.1秒,第二个(2个查询)方法的平均结果为.03 + .02秒。
就索引而言,我认为我已经做了一个EXPLAIN来确保我们通过密钥,而且大多数情况下我们都是。但是,我刚刚再次进行了快速检查,并注意到一件有趣的事情:
较慢的“单一查询”方法未显示第三行的“使用索引”的额外注释:
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | t1 | index | PRIMARY | shop_group_id_idx | 5 | NULL | 102 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | dbmodl_v18.t1.organization_id | 1 | Using where |
| 1 | SIMPLE | t0 | ref | bundle_idx,shop_id_idx | shop_id_idx | 4 | dbmodl_v18.t1.organization_id | 309 | |
| 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | dbmodl_v18.t0.id | 1 | |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
当 显示“使用索引”时,我们只查询ID:
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | t1 | index | PRIMARY | shop_group_id_idx | 5 | NULL | 102 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | dbmodl_v18.t1.organization_id | 1 | Using where |
| 1 | SIMPLE | t0 | ref | bundle_idx,shop_id_idx | shop_id_idx | 4 | dbmodl_v18.t1.organization_id | 309 | Using index |
| 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | dbmodl_v18.t0.id | 1 | |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
奇怪的是,两者都列出了正在使用的正确索引......但我想它会引发问题:
为什么它们不同(考虑到所有其他条款完全相同)?这是否表明为什么它变慢?
不幸的是,MySQL文档没有提供有关EXPLAIN
结果中“额外”列为空/空的详细信息。
答案 0 :(得分:1)
比速度更重要的是,您的查询逻辑存在缺陷。在WHERE子句中测试LEFT JOINed列时(除了测试NULL),您强制该连接的行为就像它是INNER JOIN一样。相反,你想要:
SELECT
a.*, b.*, c.*, x.*
FROM
a
LEFT JOIN b ON b.a_id = a.id
AND b.flag = true
LEFT JOIN c ON c.b_id = b.id
LEFT JOIN x ON x.a_id = a.id
ORDER BY
x.date DESC
LIMIT 25
我的下一个建议是检查SELECT中的所有.*
。你真的需要所有 所有表中的列吗?