我有一个在 MySQL 上运行的查询(v5.5——我知道它很旧,但我现在必须使用它)。下面的表 A
有 ~1600 万行,B
有 ~700,000。查询如下所示:
SELECT A.id, A.x, A.y, A.z, B.foo FROM A STRAIGHT_JOIN B ON A.id = B.id
where A.x = 53 ORDER BY A.y desc LIMIT 0, 30;
A.id
和 B.id
上都有索引设置。
(A.x, A.y)
上还有一个索引设置(此键/索引称为 DocsByType
)。
到目前为止,这个查询效果很好,它的性能一直在亚秒级左右。不过最近,我需要偶尔检查 where 子句中 A.x
的其他可能值。以下查询现在执行得很差,平均需要大约 15 秒才能完成:
SELECT A.id, A.x, A.y, A.z, B.foo FROM A STRAIGHT_JOIN B ON A.id = B.id
where (A.x = 18 or A.x = 53) ORDER BY A.y desc LIMIT 0, 30;
只有一个比较的快速查询的 explain
如下所示:
+----+-------------+-------+------+-----------------------------------------------------+------------+---------+----------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------------------------------------------+------------+---------+----------------+---------+-------------+
| 1 | SIMPLE | A | ref | Documents1,Documents2,Documents3,DocsByType,KEY_AID | DocsByType | 4 | const | 1870603 | Using where |
| 1 | SIMPLE | B | ref | KEY_BID | KEY_BID | 4 | mydb.B.id | 1 | |
+----+-------------+-------+------+-----------------------------------------------------+------------+---------+----------------+---------+-------------+
多重比较查询的 explain
如下所示:
+----+-------------+-------+-------+-----------------------------------------------------+------------+---------+----------------+---------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-----------------------------------------------------+------------+---------+----------------+---------+-----------------------------+
| 1 | SIMPLE | A | range | Documents1,Documents2,Documents3,DocsByType,KEY_AID | DocsByType | 4 | NULL | 1878693 | Using where; Using filesort |
| 1 | SIMPLE | B | ref | KEY_BID | KEY_BID | 4 | mydb.B.id | 1 | |
+----+-------------+-------+-------+-----------------------------------------------------+------------+---------+----------------+---------+-----------------------------+
我可以看到有一个 filesort
操作不在第一个查询中。此外,type
是“range”而不是“ref”,ref
是“NULL”而不是“const”。删除 order by 子句可以完全修复它,使其在不到一秒的时间内完成,但对结果进行排序很重要。
查询优化不是我的强项。考虑到该列已经被编入索引,我会认为它的工作方式完全相同。任何人都可以解释为什么它的行为方式并建议优化查询的方法吗?另请注意,新查询可能需要为 where 子句使用 3、4 甚至 5 个可能的值(但始终针对同一列)。
我也尝试过使用 MySQL 5.8 运行查询,但结果是一样的。我的表使用的是 MyISAM 引擎。
答案 0 :(得分:1)
假设您有一个很大的人名列表。目标是找到前 30 个 Smiths(按名字排序)。第一个查询很快,因为它本质上是同时执行 WHERE
、ORDER BY
和 LIMIT
:
第二个更麻烦,因为它有效地完成了:
有两件事可以加快您的慢查询速度:
( SELECT A.id, A.x, A.y, A.z, B.foo FROM A JOIN B ON A.id = B.id
where (A.x = 18)
ORDER BY A.y desc LIMIT 30 )
UNION ALL
( SELECT A.id, A.x, A.y, A.z, B.foo FROM A JOIN B ON A.id = B.id
where (A.x = 53) -- Note
ORDER BY A.y desc LIMIT 30 )
ORDER BY A.y desc LIMIT 0, 30 -- Yes, repeated
评论:
STRAIGHT_JOIN
是不必要的,JOIN
会做同样的事情INDEX(x,y)
并使用 LIMIT。ALL
比默认值更快,适用于这种情况UNIONs
拼接在一起。然而,在某个时候,所有工会的成本将超过收益。 (尝试预测截止点是不切实际的。)在 LIMIT 30
到 JOINing
之前执行 B
会更快。这样,您将只在 B
中执行 30 次查找;我的方式需要60;您的原始查询需要更多。