我正在开发一个需要从表中获取最新值的应用程序,当前表中的行数超过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的类似问题实际上有与上述类似的答案(这可能是开发人员从中获得的)。

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

更新:我提交得太早了。

解释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上有一个索引。

最佳答案

使用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,这样您就不会停机。

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

关于mysql - 获取按两列分组的最新值-更快,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20907333/

10-16 00:37