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

标签 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 的类似问题实际上有类似上面的答案(这可能是开发人员从中得到的)。

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

更新:我太早提交了。

解释分钟图表

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/

相关文章:

Mysql IN 关键字

MySQL查询优化: how can I speed up this query?

php - Mysql条件MAX或MIN依赖于另一个列值

Python - MySQL 连接器语法错误

mysql - 为什么 Id 只保存在数据库中而没有其他数据?

php - 加速 php/mysql 查询/连接的正确使用?

sql - 优化 postgis 索引查询。注意 : gserialized_gist_joinsel: jointype 1 not supported

Python mysql - cursor.close() db.close() 函数

MySQL在引用上创建表错误

php - 如何使用 Laravel 4 框架在 ANGULARJS 中执行所有 MySql 查询