【问题标题】:MySQL query takes ages to return on a huge tableMySQL 查询需要很长时间才能返回一个巨大的表
【发布时间】:2015-04-22 19:13:48
【问题描述】:

我有一张很大的桌子,下面的代码需要 990 秒。去完成。 bdateitype 已编入索引。我还需要优化/更改什么?

SELECT s, count(*) as total
FROM  `mt_ex_15` 
WHERE bdate > '2014-10-01' and bdate < '2014-11-01'
and itype = '3'
group by s
order by total desc

编辑:这是EXPLAIN

id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  mt_ex_15    ref itype,bdate,s   itype   2   const   44157686    Using where; Using temporary; Using filesort    

编辑:我认为我需要优化我的 DB 或 my.cnf,因为即使是以下查询也需要 40 秒。

SELECT count(*) as total
FROM  `mt_ex_15` 
WHERE bdate > '2015-02-01' and bdate < '2015-03-01'

这里是解释:

 id     select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   SIMPLE  mt_ex_15    range   bdate   bdate   3   NULL    4494019     Using where; Using index

【问题讨论】:

  • 表定义会很好。编辑:因为您的查询最好由index(bdate,itype,s)提供。您的日期是日期还是日期时间?
  • 你有s的索引吗? s 有多少个不同的值?由于您正在对计算值进行排序,因此如果 s 有大量不同的值,则排序可能仍需要一些时间。
  • s 有 8 个不同的值,是的,它已编入索引
  • 考虑在 (itype, s, bdate) 上建立一个多列索引。假设 InnoDB,如果针对和索引的简单 COUNT 查询需要很长时间,则需要增加 InnoDB 缓冲池的大小。

标签: mysql database performance indexing


【解决方案1】:

对于这个查询:

SELECT s, count(*) as total
FROM  `mt_ex_15` 
WHERE bdate > '2014-10-01' and bdate < '2014-11-01' and itype = '3'
group by s
order by total desc

最好的索引是mt_ex_15(itype, bdate, s)。引擎应该能够充分利用where 子句的索引。此外,这是一个覆盖索引,因此该查询不需要触及原始数据。

如果您有所有可用“s”值的列表,则可以将其作为相关子查询:

select s.*,
       (select count(*)
        from mt_ex_15 m
        where m.s = s.s and m.itype = 3 and m.bdate > '2014-10-01' and m.bdate < '2014-11-01'
       ) total
from s
having total > 0 -- using a convenient MySQL extension
order by total desc;

这个查询的最佳索引是mt_ex_15(s, itype, bdate)

注意:如果itype 确实是一个整数,则应该删除常量周围的引号。它们具有误导性。

【讨论】:

  • +1。对于有限数量的s 值,使用相关子查询返回s 的每个值的计数是一种可行的方法。但是,正如 Gordon 指出的那样,为了获得最佳性能,我们确实需要有一个合适的可用索引(根据 Gordon 建议的索引,独立 s 上的索引将是多余的,可以删除。)@Gordon:我们可以得到一个来自内联视图的 s 的不同值列表,例如FROM (SELECT DISTINCT s FROM mt_ex_15) s 。此外,来自 OP 的查询不包括“零”计数,因此我们还需要添加 HAVING total &gt; 0 以复制该行为。
【解决方案2】:

使用EXPLAIN查看执行计划。

由于缺少有关表的任何信息,我们真的只是在猜测。

我会尝试达到这样的指定结果:

CREATE INDEX `mt_ex_15_IX1` ON `mt_ex_15` (`itype`,`s`,`bdate`);

SELECT t.s
     , SUM(t.bdate > '2014-10-01' AND t.bdate < '2014-11-01') AS `total`
  FROM `mt_ex_15` t
 WHERE t.itype = '3'
 GROUP BY t.s
HAVING `total` > 0
 ORDER BY t.s DESC

比较这个和原始的EXPLAIN 输出将(可能)表明这两个查询使用不同的执行计划。

跟进

有了合适的索引,MySQL 可以避免昂贵的“使用文件排序”操作。我在上面推荐的索引将使itype 列上的索引变得多余,并且可以删除该索引。 (任何使用该索引的查询都可以使用新索引,因为itype 是前导列。

新索引的建议基于查询...itype 上的相等谓词(使该列成为前导列),然后是 s,因为该列上有一个 GROUP BY。在索引中包含bdate 列意味着可以从索引中满足查询,而无需查找底层数据页。

我们希望EXPLAIN 输出“Extra”列显示“使用索引”,而不是显示“使用文件排序”。

如果添加索引是不可能的,那么避免“使用文件排序”的最佳方法是使用以列 s 作为前导列的现有索引。但这意味着查询将需要检查表中的每一行;如果bdateitype 列未包含在索引中,则这意味着对表中的每一行进行索引查找。但是,这可能会执行得更快。检查此查询的 EXPLAIN 输出:

EXPLAIN
SELECT t.s
     , SUM(t.itype = '3' AND t.bdate > '2014-10-01' AND t.bdate < '2014-11-01') 
       AS `total`
  FROM `mt_ex_15` t
 GROUP BY t.s
HAVING `total` > 0
 ORDER BY t.s DESC

【讨论】:

  • id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE mt_ex_15 ref itype,bdate,s itype 2 const 44157686 using where;使用临时的;使用文件排序
  • 将索引添加到 50 GB 的 200M 行数据会杀了我 :)
  • 如果您的数据库正在使用中,请尝试在副本上执行此操作。杀死你的是派生字段的顺序:没有办法索引它。数据库开发人员的工作是使用中间表,一个真实的表。使用存储过程。 1. 创建一个带有列和索引的临时表。 2.将分组的结果选择进去。 3. 使用排序依据的索引从中选择。如果仍然无法在可接受的时间内运行,请创建 2 个表,一个用于带有 where 条件的选择,一个用于分组结果。然后用 order by 选择。
  • 会有7-8种不同的结果。我不需要订购。我认为这都是关于分组的。当然我不确定,但现在会尝试一下
  • @blacksun:我的错,我错过了ORDER BY total。 MySQL 需要做一个排序操作来得到它;但是对 8 行进行排序不会花费任何时间。真正的问题是GROUP BY 操作所需的排序操作。 MySQL 可以使用适当的索引来避免这种排序(它可以检索使用索引预先排序的行)。要执行“按 s 分组”操作,MySQL 需要按“s”“按顺序”排列行。我的答案中的查询旨在欺骗 MySQL 避免对大量行进行昂贵的排序操作。
【解决方案3】:

GROUP BY s ORDER BY total -- 你至少被一种“文件排序”所困。根据不同的情况,排序实际上可能在 RAM 中。

一个现成的建议:

  • 更改为 GROUP BY itype, s -- GROUP BY 中不必要的字段可能会导致更好的 EXPLAIN。
  • INDEX(itype, s, bdate) -- 按此顺序

如果您使用的是 MySQL 5.6.16 或更高版本,ALTER TABLE ... ALGORITHM = INPLACE 的侵入性会更小。

如果bdateDATE,则bdate &gt; '2014-10-01' 消除10 月1 日;是故意的吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 2018-07-06
    • 1970-01-01
    • 1970-01-01
    • 2018-09-10
    相关资源
    最近更新 更多