我一直在尝试在MySQL中创建索引,但每当我对查询运行解释时,都会不断获取临时文件。
我的表格的简化版本如下:
ordered_products
op_id INT UNSIGNED NOT NULL AUTO_INCREMENT
op_orderid INT UNSIGNED NOT NULL
op_orderdate TIMESTAMP NOT NULL
op_productid INT UNSIGNED NOT NULL
products
p_id INT UNSIGNED NOT NULL AUTO_INCREMENT
p_productname VARCHAR(128) NOT NULL
p_enabled TINYINT NOT NULL
'ordered_products'表目前有超过1,000,000行,是所有已订购产品的记录,以及它们所属的订单。这张桌子快速增长。
“产品”表目前有大约3,000行,其中包含待售产品列表。
该网站显示特定时期(通常是过去3天)的顶级产品列表,我的查询如下:
SELECT COUNT(op.op_productid) AS ProductCount, op.op_productid
FROM ordered_products op
LEFT JOIN products p ON op.op_productid=p.p_id
WHERE op.op_orderdate>='2014-03-08 00:00:00'
AND p.p_enabled=1
GROUP BY op.op_productid
ORDER BY ProductCount DESC, p.p_productname ASC
当我运行该查询时,通常需要大约800毫秒(0.8秒)才能执行,这太荒谬了。我们已经通过缓存来解决这个问题,但是每当缓存过期时,我们都会放慢速度。我需要解决这个问题。
我试图对表进行索引,但无论我尝试什么,我都无法避免临时和文件排序。 EXPLAIN的输出是:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE p index PRIMARY,idx_enabled_id_name idx_enabled_id_name 782 \N 1477 Using where; Using index; Using temporary; Using filesort
1 SIMPLE op ref idx_pid_oid_date idx_pid_oid_date 4 test_store.p.p_id 9 Using where; Using index
如果我删除了GROUP BY,则文件存档会消失,但是我需要它以确保ProductCount值显示每个产品计数而不是所有产品的总和。
如果我删除GROUP BY和ORDER BY ProductCount,临时和文件都会消失,但现在我的结果集非常糟糕。
任何人都可以帮我解决这个问题吗?我尝试了很多不同的索引,并尝试过多次重写SQL,但永远不会成功。
非常感谢任何帮助。
答案 0 :(得分:1)
在计算列ORDER BY
上使用ProductCount
时,无法删除临时表和文件排序。计算列没有索引,因此必须在查询时进行排序。
我尝试通过实验重现您的结果。我可以在op_productid
上放置一个索引,然后优化器可以使用它来执行GROUP BY
。
mysql> EXPLAIN SELECT COUNT(op.op_productid) AS ProductCount, op.op_productid
FROM ordered_products op FORCE INDEX (op_productid) STRAIGHT_JOIN products p
ON op.op_productid=p.p_id
WHERE op.op_orderdate>='2014-03-08 00:00:00' AND p.p_enabled=1
GROUP BY op.op_productid ORDER BY null;
在我的情况下,我不得不使用STRAIGHT_JOIN和FORCE INDEX来覆盖优化器。但这可能是由于我的测试环境,我每个表只有1或2行进行测试,它会抛弃优化器的选择。在您的真实数据中,它可能是一个更明智的选择。
另外,如果WHERE子句中有条件使隐式连接成为内连接,则不要使用LEFT JOIN。了解联接的类型及其工作原理 - 默认情况下不要总是使用LEFT JOIN。
+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+
| 1 | SIMPLE | op | index | op_productid | op_productid | 4 | NULL | 5 | Using where |
| 1 | SIMPLE | p | ALL | PRIMARY | NULL | NULL | NULL | 1 | Using where |
+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+
您唯一的选择是存储非规范化表,其中计数保持不变。然后,如果您的缓存失败,则刷新缓存不是一个昂贵的查询。