MySQL语句处理速度非常慢,如何使其性能更好?

MySQL语句处理速度非常慢,如何使其性能更好?,mysql,performance,Mysql,Performance,我有下面的MySQL语句,当产品数量变大时,它的执行速度非常慢。当前声明如下所示: SELECT DISTINCT products.*, colors.value FROM products LEFT OUTER JOIN product_fields colors ON colors.product_id = products.id AND colors.name = 'color' GROUP BY products.id ORDER BY CASE WHEN merchant

我有下面的MySQL语句,当产品数量变大时,它的执行速度非常慢。当前声明如下所示:

SELECT DISTINCT products.*, colors.value FROM products
LEFT OUTER JOIN product_fields colors ON colors.product_id = products.id AND colors.name = 'color' 
GROUP BY products.id
ORDER BY
    CASE WHEN merchant IN ('Merchant 1') THEN -1 ELSE RAND(1617116433) END,
    CASE WHEN category IN ('Category 1', 'Category 2') THEN -1 ELSE RAND(1617116433) END 
LIMIT 0, 30
为了清楚起见:有一个
product
和一个
product\u字段
表。对于每个
产品
产品字段
表中有零条或多条记录。
product\u fields
表中的一个字段是一个字段:
name
,其值为
color
,这是我在结果中需要的唯一字段值

创建
orderby
语句是因为我想首先显示某个商家的所有产品,然后随机显示其他商家的其他产品。 其次,某一类别(类别1和类别2)的所有产品必须首先显示在结果中。之后,随机展示其他类别的其他产品

对于随机,我已经使用了一个固定的数字来随机化一切。它不一定每次都不同。我只想在显示商户1和类别1、类别2产品之后随机列出其余产品

目前,语句工作正常,虽然在大数据集上速度非常慢,但我认为
ORDER BY
使其速度变慢,但我不知道如何解决这个问题。希望有人能给我指出正确的方向

编辑>> 我现在运行了上述语句的
EXPLAIN
,结果如下:

+----+-------------+--------------+------+---------------+-------------+---------+----------------------------------------------+-------+---------------------------------+
| id | select_type | table        | type | possible_keys | key         | key_len | ref                                          | rows  | Extra                           |
+----+-------------+--------------+------+---------------+-------------+---------+----------------------------------------------+-------+---------------------------------+
|  1 | SIMPLE      | products     | ALL  | NULL          | NULL        | NULL    | NULL                                         | 10402 | Using temporary; Using filesort |
|  1 | SIMPLE      | colors       | ref  | product_key   | product_key | 767     | dbname.products.id                           |     1 |                                 |
+----+-------------+--------------+------+---------------+-------------+---------+----------------------------------------------+-------+---------------------------------+
2 rows in set (0.02 sec)
编辑2>>以进一步澄清问题: 之所以使用兰德,是因为我希望在显示特定“商家”和“类别”的所有产品后,随机显示每个产品。但是,下一次用户访问该站点时,顺序可以是相同的,我关心的是。我只希望所有其他产品不按某个商户或类别分类。这就是
RAND
的意义所在


感谢@spencer7593的精彩回答,我认为这一切都归结为使用
文件排序
选项对整个结果集进行排序(参见上面的解释)。那么,现在我如何解决这个问题,保持一种方法,按照上面段落中的解释对结果进行随机化。

尝试在
选择之前使用
explain
关键字运行查询。它将告诉您使用了什么索引(如果有的话)

索引是在MySQL中获得良好性能的关键。 在这种情况下,看起来需要对颜色(产品id、名称)进行索引

即使如此,这将始终在产品上运行完整的表扫描。您应该尝试在查询中添加一个限制
where
-语句。

表达式
RAND(1617116433)
将在每次计算时返回相同的常量值。将为每一行返回相同的精确值。也就是说,用大于-1的文字数字值替换该表达式将产生等效结果

如果确实希望为每一行指定伪随机值,则需要从函数中删除种子值。您需要使用
RAND()
为每一行获取不同的值

作为演示,比较以下结果:

SELECT RAND(1617116433), RAND(1617116433), RAND(1617116433) ;
SELECT RAND(1617116433), RAND(), RAND() ;
(请注意,第二条语句每次运行时都将返回相同的值序列。
RAND()
是一个伪随机数生成器,而不是真正的随机数。)

无论哪种方式,在查询中,都会对每一行计算函数,然后对整个结果集进行排序。(解释将显示“使用文件排序”)

最后应用
LIMIT
子句,对整个结果集进行排序,然后从排序集返回前30行。(这样可以避免返回大量行,但MySQL服务器仍在准备整个行集。)

这可能是您的查询“缓慢”的最大原因

DISTINCT
的用法有点奇怪,您已经有了一个
groupby
子句来确保产品的
id
是唯一的。标准模式是在
分组依据
子句中包含
颜色.value

另外,是否需要返回
产品
表中的每一列?我们更希望在选择列表中看到要返回的列列表,而不是依赖于
*

product_字段
表进行适当的索引可以提高联接操作的性能

... ON `product_fields` (`product_id`, `name`, `value`)
(我们希望解释输出应该显示该表的“使用索引”。)

但这并不能让您绕过需要访问
products
表中的每一行,并为
products.id
的每个不同值计算
RAND()
函数(两次)

(在
产品
表上的覆盖索引可能也会带来一些好处,但我认为这是可以忽略的。)


我会这样编写查询,但这无助于解决“大石头”性能问题:

SELECT p.id
     , p.???
     , p.???
     , c.value
  FROM (SELECT RAND(1617116433)) i 
 CROSS
  JOIN products p
  LEFT
  JOIN product_fields c
    ON c.product_id = p.id
   AND c.name = 'color'
 GROUP BY p.id, c.value
 ORDER
    BY CASE WHEN p.merchant IN ('Merchant 1') THEN -1 ELSE RAND() END
     , CASE WHEN p.category IN ('Category 1', 'Category 2') THEN -1 ELSE RAND() END 
 LIMIT 0, 30

解释了什么
返回?首先,没有办法使用索引来满足订单条件。可能还有其他性能问题,但在不了解表架构的情况下,我们无法判断。您正在按rand进行
排序,这在大型表上总是很慢。左侧联接比内部联接慢得多。如果你不需要没有颜色的产品(或者如果没有颜色的话),你当然应该改变。我已经添加了EXPLAIN关键字,并在上面以编辑的形式发布了结果…感谢@spencer7593对这个问题的伟大和非常彻底的解释。非常有帮助,我已经将我的整个声明更改为您提议的声明。我已经在我对原始问题的编辑2中添加了我对上述建议解决方案的评论。再次感谢@雨果:内联视图<