获取按两列分组的最新值 - 更快

时间:2014-01-03 15:41:05

标签: mysql query-optimization

我正在开发一个需要从当前>表格中获取最新值的应用程序300万行和计数。最新值需要按两列/属性分组,因此它运行以下查询:

SELECT 
        m1.type,
        m1.cur,
        ROUND(m1.val, 2) AS val
    FROM minuteCharts m1
    JOIN
        (SELECT
            cur,
            type,
            MAX(id) id,
            ROUND(val) AS val
        FROM minuteCharts
        GROUP BY cur, type) m2
    ON m1.cur = m2.cur AND m1.id = m2.id;

数据库服务器非常重量级,但上面的查询需要3,500毫秒才能完成,而且这个数字正在上升。我怀疑当应用程序刚刚启动时这不是一个真正的问题(因为当时数据库几乎是空的),但它已成为一个问题,我还没有找到更好的解决方案。事实上,关于SO的类似问题实际上有类似于上面的答案(这可能是开发人员从中得到的)。

是否有人知道如何更有效地获得相同的结果?

更新:我太早提交了这个。

EXPLAIN minuteCharts;

Field   Type                              Null  Key     Default    Extra
id      int(255)                          NO    PRI         NULL   auto_increment
time    datetime                          NO    MUL         NULL    
cur     enum('EUR','USD')                 NO                NULL    
type    enum('GOLD','SILVER','PLATINUM')  NO                NULL    
val     varchar(80)                       NO                NULL

id是主要索引,time上有索引。

1 个答案:

答案 0 :(得分:1)

GROUP BY的子查询正在执行表扫描和临时表,因为没有索引支持它。

mysql> EXPLAIN SELECT m1.type, m1.cur, ROUND(m1.val, 2) AS val FROM minuteCharts m1 JOIN         (SELECT cur, type, MAX(id) id, ROUND(val) AS val FROM minuteCharts GROUP BY cur, type) m2     ON m1.cur = m2.cur AND m1.id = m2.id;
+----+-------------+--------------+------+---------------+-------------+---------+------------------------+------+---------------------------------+
| id | select_type | table        | type | possible_keys | key         | key_len | ref                    | rows | Extra                           |
+----+-------------+--------------+------+---------------+-------------+---------+------------------------+------+---------------------------------+
|  1 | PRIMARY     | m1           | ALL  | PRIMARY       | NULL        | NULL    | NULL                   |    1 | NULL                            |
|  1 | PRIMARY     | <derived2>   | ref  | <auto_key0>   | <auto_key0> | 6       | test.m1.cur,test.m1.id |    2 | NULL                            |
|  2 | DERIVED     | minuteCharts | ALL  | NULL          | NULL        | NULL    | NULL                   |    1 | Using temporary; Using filesort |
+----+-------------+--------------+------+---------------+-------------+---------+------------------------+------+---------------------------------+

您可以使用以下索引对此进行改进,首先按GROUP BY列排序,然后还包括子查询的其他列以使其成为覆盖索引:

mysql> ALTER TABLE minuteCharts ADD KEY (cur,type,id,val);

表扫描转变为索引扫描(仍然不是很好但更好),临时表消失了。

mysql> EXPLAIN ...
+----+-------------+--------------+-------+---------------+-------------+---------+------------------------+------+-------------+
| id | select_type | table        | type  | possible_keys | key         | key_len | ref                    | rows | Extra       |
+----+-------------+--------------+-------+---------------+-------------+---------+------------------------+------+-------------+
|  1 | PRIMARY     | m1           | index | PRIMARY,cur   | cur         | 88      | NULL                   |    1 | Using index |
|  1 | PRIMARY     | <derived2>   | ref   | <auto_key0>   | <auto_key0> | 6       | test.m1.cur,test.m1.id |    2 | NULL        |
|  2 | DERIVED     | minuteCharts | index | cur           | cur         | 88      | NULL                   |    1 | Using index |
+----+-------------+--------------+-------+---------------+-------------+---------+------------------------+------+-------------+

如果索引适合您的缓冲池,则效果最佳。如果它大于缓冲池,则查询将不得不在索引扫描期间反复推入和反对页面,这将大大降低性能。


重新评论:

添加索引需要多长时间的答案取决于您拥有的MySQL版本,此表的存储引擎,服务器硬件,表中的行数,并发加载的级别数据库等等。换句话说,我无从谈论。

我建议使用pt-online-schema-change,这样您就不会有停机时间。

另一个建议是在具有数据库克隆的登台服务器上进行尝试,这样你就可以粗略估计它需要多长时间(尽管在空闲服务器上进行测试通常比运行相同的程序要快得多在繁忙的服务器上更改。)