我有以下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
表。对于每个product
,product_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)。那么现在我如何解决这个问题,并保持一种方法来随机化结果,如上段所述。
答案 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
- 语句。