【问题标题】:Optimizing MySQL query with SUM, date range and Group By使用 SUM、日期范围和 Group By 优化 MySQL 查询
【发布时间】:2012-02-18 03:52:57
【问题描述】:

我有以下表格:

CREATE TABLE IF NOT EXISTS stats (
    date date NOT NULL DEFAULT '0000-00-00',
    cid int(8) NOT NULL DEFAULT '0',
    v bigint(15) NOT NULL DEFAULT '0',
    c bigint(15) NOT NULL DEFAULT '0',
    a bigint(15) NOT NULL DEFAULT '0',
PRIMARY KEY (date,cid),
KEY date (date),
KEY cid (cid),
KEY date_cid_vca (date,cid,v,c,a)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

此表有 30,842,712 行

和

CREATE TABLE IF NOT EXISTS camp (
id int(8) NOT NULL AUTO_INCREMENT,
name varchar(80) NOT NULL DEFAULT '',
PRIMARY KEY (id,name)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

此表有 1985 行

我有以下疑问:

SELECT
    c.id,
    c.name,
    SUM(s.v) AS sumv,
    SUM(s.c) AS sumc,
    GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
    SUM(s.a) AS suma,
    GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio
FROM
    stats s, camp c
WHERE
    s.date >= '2012-02-01' AND
    s.date <= '2012-02-29' AND
    c.id=s.cid
GROUP BY s.cid;

EXPLAIN 显示:

+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+
| id | select_type | table | type  | possible_keys                 | key          | key_len | ref                 | rows    | Extra                                                     |
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+
|  1 | SIMPLE      | s     | range | PRIMARY,date,cid,date_cid_vca | date_cid_vca | 3       | NULL                | 1010265 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | c     | ref   | PRIMARY                       | PRIMARY      | 4       | db.s.cid            |       1 | Using index                                               |
+----+-------------+-------+-------+-------------------------------+--------------+---------+---------------------+---------+-----------------------------------------------------------+

问题是查询需要大约 50 秒才能完成,即使它使用索引。有没有其他方法可以优化查询?

谢谢!

【问题讨论】:

    标签: mysql


    【解决方案1】:

    您在使用索引优化此查询方面做得很好。我想在那个日期范围内,stats 中确实有超过 100 万行。不幸的是,连接(然后分组)100 万行,即使有一个覆盖索引,对数据库的要求也很多。为了获得更好的性能,您需要增强硬件,开始去规范化(将 camp 放在 stats 内以避免连接),或者继续计算每个阵营的总数,而不是动态计算。

    编辑

    由于删除超过 100 万个连接似乎产生了很大的影响,您可以尝试以下操作:

    SELECT c.*, a.* FROM
    (SELECT
        SUM(s.v) AS sumv,
        SUM(s.c) AS sumc,
        GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
        SUM(s.a) AS suma,
        GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio,
        s.cid
    FROM
        stats s
    WHERE
        s.date >= '2012-02-01'
       AND s.date <= '2012-02-29'
    GROUP BY s.cid) a
    JOIN
      camp c
      ON c.id = a.cid
    

    此查询对较小的结果集进行连接。

    【讨论】:

    • 我认为硬件不是问题。服务器有 4 个 CPU,8 核(32 核,64 线程)和 64GB RAM。它在 RAID 10 中有 6 个 SSD 磁盘。my.cnf 得到了最大程度的优化(基于 tune-primer.sh)。您能否提供有关非规范化的更多信息。谢谢!
    • 我尝试删除连接,然后从主查询的数据循环中使用单独的查询从营地中提取数据。我想这是要走的路,因为现在总执行时间大大减少了。完成大约需要 10 秒。
    • 听起来不错,很强大,硬件。 :)
    • 这个查询很棒。您只是忘记在子查询中添加 GROUP BY。它与没有 JOIN 的查询所需的时间相同。谢谢!
    • @Paxxil,我添加了缺少的GROUP BY。谢谢。
    【解决方案2】:

    以下查询应该允许它更有效地使用索引

    SELECT
        c.id,
        c.name,
        SUM(s.v) AS sumv,
        SUM(s.c) AS sumc,
        GREATEST(((SUM(s.c)/SUM(s.v))*100.00), 0.00) AS cratio,
        SUM(s.a) AS suma,
        GREATEST(((SUM(s.a)/SUM(s.c))*100.00), 0.00) AS aratio
    FROM
        camp c
    INNER JOIN
        stats s
    ON
        s.cid = c.id
        AND s.date BETWEEN '2012-02-01' AND '2012-02-29'
    
    GROUP BY c.id;
    

    另外我会考虑删除 date_cid_vca 键,因为它只包含整个表,所以不是特别有用。上面的查询应该使用 PK 根据日期和 cid 来匹配从 stats 到 camp 的行,所以虽然在不访问您的数据库的情况下很难 100% 确定,但我相当有信心上述将改善您的响应时间

    【讨论】:

    • 我已经尝试了您的建议,但不幸的是,执行您的查询版本需要大约 5 秒的时间。没有索引 date_cid_vca 需要两倍的时间。
    【解决方案3】:

    您可以使用您的 C 表创建一个内连接,并且在连接中使用日期条件,它应该会减少您的查询时间。

    你可能可以做更多的优化,但这是我能看到的第一个。

    【讨论】:

      猜你喜欢
      • 2020-05-16
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多