处理MySQL语句非常慢,如何让它表现更好?

时间:2015-02-04 14:58:21

标签: 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 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_fields表。对于每个productproduct_fields表中都有零个或多个记录。 product_fields表格中的一个字段是一个字段:name,其值为color,这是我在结果中需要的唯一字段值。

创建ORDER BY语句是因为我想先显示某个商家的所有商品,然后随机显示其他商家的其他商品。 接下来,必须首先在结果中显示来自特定类别(类别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)

EDIT 2>>为了澄清一点事情: 仅使用RAND是因为我希望在显示特定“商家”和“类别”的所有产品后随机显示每个产品。但是下次用户访问网站时,订单可以是相同的,对于我所关心的一切。我只是希望所有其他产品不按特定商家或类别进行排序。这就是RAND的意思。

感谢@ spencer7593的精彩回答,我认为这一切归结为使用Using filesort选项对整个结果集进行排序(请参阅上面的EXPLAIN)。那么现在我如何解决这个问题,并保持一种方法来随机化结果,如上段所述。

2 个答案:

答案 0 :(得分:2)

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

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

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

SELECT RAND(1617116433), RAND(1617116433), RAND(1617116433) ;
SELECT RAND(1617116433), RAND(), RAND() ;

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

无论哪种方式,在您的查询中,将对每个行评估函数,然后对整个结果集进行排序。 (EXPLAIN将显示“使用filesort”)。

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

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

DISTINCT的用法有点奇怪,您已经有一个GROUP BY子句,可确保产品中的id是唯一的。规范模式是在colors.value子句中包含GROUP BY

此外,您是否需要从products表中返回每个列?我们希望在SELECT列表中看到要枚举的列列表,而不是依赖*

product_fields表上的适当索引可以提高连接操作的性能。

... ON `product_fields` (`product_id`, `name`, `value`)

(我们希望EXPLAIN输出应该为该表显示“使用索引”。)

但这并不能满足您在products表中访问每一行的需要,并为RAND()的每个不同值评估products.id函数(两次)。

products表上的覆盖索引也可能带来一点好处,但我希望这是可以忽略不计的。)


我会像这样编写查询,但这并没有解决“大摇滚”性能问题:

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

答案 1 :(得分:0)

尝试在explain前面使用select关键字运行您的查询。它会告诉你使用了什么索引(如果有的话)。

索引是在MySQL中获得良好性能的关键。 在这种情况下,您看起来需要索引颜色(product_id,name)。

即便如此,这也将始终对产品进行全表扫描。您应该尝试在查询中添加限制where - 语句。