我们有一个相对简单的查询,它跨 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 子句更改为 only
SELECT a.id
而不是SELECT *
- 第二个查询也与上面相同,除了将 WHERE 子句更改为仅对
a.id IN
查询 1 的结果执行一次,而不是我们之前的结果
结果是截然不同的。第一个查询是 0.03 秒,第二个查询是 0.02 秒。
在代码中执行这个两步查询本质上使我们的性能提高了 20 倍。
所以这是我的问题:
这种类型的优化不应该已经在数据库引擎中完成了吗?为什么实际选择的字段的差异会对查询的整体性能产生影响?
归根结底,它只是选择完全相同的 25 行并返回这 25 行的完全相同的完整内容。那么,为什么性能差距如此之大呢?
添加于太平洋夏令时间 2012 年 8 月 24 日下午 13:02
感谢 eggyal 和invertedSpear 的反馈。首先,这不是缓存问题——我已经在每种方法之间交替运行了多次(大约 10 次)两个查询的测试。第一个(单个查询)方法的结果平均为 1.1 秒,第二个(2 个查询)方法的结果平均为 0.03+0.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 | |
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
奇怪的是,两者都列出了正在使用的正确索引......但我想它引出了问题:
为什么它们不同(考虑到所有其他条款都完全相同)?这是否表明它为什么更慢?
EXPLAIN
不幸的是,当“额外”列在结果中为空白/空时,MySQL 文档没有提供太多信息。