【问题标题】:MySQL slow queries and memcachedMySQL 慢查询和 memcached
【发布时间】:2012-04-24 13:31:24
【问题描述】:

我用 memcached 缓存了慢查询,它工作正常,但有时我们在 mysql 中遇到很多慢查询,好像 memcached 已经停止,然后我们的网站就关闭了。

慢查询图:http://i.imgur.com/ReyWe.png

此时我们在 30 秒内收到了大约 100 个慢查询。会是什么? 我们的查询:

# Query_time: 5.942602  Lock_time: 0.010214 Rows_sent: 10000  Rows_examined: 493139  

SET timestamp=1335194149;  
SELECT story_id FROM dug_stories d WHERE d.story_is_permanent=0  AND  
                       (  
                        (d.story_time>1335190543 AND (d.story_pluses-d.story_minuses) > 5)  
                        OR  
                        ( ( (d.story_pluses-d.story_minuses) > 12 OR (d.story_pluses >= 10 AND d.story_minuses<=0) )  AND d.story_cat!=48 AND d.st  
ory_cat!=131 AND d.story_cat!=55 AND d.story_cat!=44 AND d.story_cat!=126 AND d.story_cat!=53 AND d.story_cat!=370 AND d.story_cat!=381 AND d.stor  
y_cat!=304 AND d.story_cat!=497)  OR (d.story_cat=48 AND (d.story_pluses-d.story_minuses) > 9) OR (d.story_cat=131 AND (d.story_pluses-d.story_min  
uses) > 8) OR (d.story_cat=55 AND (d.story_pluses-d.story_minuses) > 8) OR (d.story_cat=44 AND (d.story_pluses-d.story_minuses) > 9) OR (d.story_c  
at=126 AND (d.story_pluses-d.story_minuses) > 13) OR (d.story_cat=53 AND (d.story_pluses-d.story_minuses) > 8) OR (d.story_cat=370 AND (d.story_pl  
uses-d.story_minuses) > 8) OR (d.story_cat=381 AND (d.story_pluses-d.story_minuses) > 8) OR (d.story_cat=304 AND (d.story_pluses-d.story_minuses)  
> 8) OR (d.story_cat=497 AND (d.story_pluses-d.story_minuses) > 9)  
                       )  
                       ORDER BY d.story_rating DESC, d.story_time DESC LIMIT 0, 10000;  



稍微调整了查询​​(而不是所有 != 类别,我更改为 NOT Category IN(要排除的类别列表),但格式化以便于阅读...另外,请注意,SET TIMESTAMP 变量不是甚至在查询中使用...这是一个硬编码的值。

SET timestamp=1335194149;  
SELECT 
      story_id 
   FROM 
      dug_stories d 
   WHERE 
          d.story_is_permanent = 0  
      AND (     ( d.story_time > 1335190543 AND d.story_pluses - d.story_minuses > 5 )  
             OR (    (    d.story_pluses - d.story_minuses > 12 
                       OR (   d.story_pluses >= 10 AND d.story_minuses <= 0 ) 
                     )  
                  AND NOT d.story_cat IN ( 44, 48, 53, 55, 126, 131, 304, 370, 381, 497 )
                )
             OR ( d.story_cat = 44  AND d.story_pluses - d.story_minuses > 9 ) 
             OR ( d.story_cat = 48  AND d.story_pluses - d.story_minuses > 9 ) 
             OR ( d.story_cat = 53  AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 55  AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 126 AND d.story_pluses - d.story_minuses > 13 ) 
             OR ( d.story_cat = 131 AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 304 AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 370 AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 381 AND d.story_pluses - d.story_minuses > 8 ) 
             OR ( d.story_cat = 497 AND d.story_pluses - d.story_minuses > 9 )  
          )  
   ORDER BY 
      d.story_rating DESC, 
      d.story_time DESC 
   LIMIT 
      0, 10000;  

非常感谢您的帮助! 对不起我的英语不好:)

【问题讨论】:

    标签: mysql performance memcached


    【解决方案1】:

    如果 rows_examined 大于结果集,慢日志可能表明索引没有被充分利用。

    理想情况下,MySQL 应该能够为 WHERE 子句中的所有过滤器表达式使用单个索引。由于所有嵌套的 AND 和 OR 以及对多列的引用,您的 WHERE 子句非常复杂。 MySQL 可能无法利用索引来优化此查询,即使您有。

    一般来说,这种类型的查询可以通过将其分成几个较小的查询来改进,每个查询都能够利用索引,然后使用UNION将结果组合成一个查询。

    例如,如果我在 first_name 上有一个索引,在 last_name 上有一个索引,则以下查询:

    SELECT *
    FROM Users
    WHERE active = true
      AND ((first_name = 'Marcus') OR (last_name = 'Adams'))
    

    可以这样改进:

    SELECT *
    FROM Users
    WHERE active = true
      AND first_name = 'Marcus'
    UNION
    SELECT *
    FROM Users
    WHERE active = true
      AND last_name = 'Adams'
    

    在 LIMIT 子句中检查的行数多于 10000 行这一事实也表明 ORDER BY 子句可能正在使用文件排序,而不是查询返回已按顺序排列的行。这使得 MySQL 可以完成所有工作,即使您只需要部分记录。您很可能需要使用一个覆盖索引来处理您的所有条件,这可能是不可能的,或者您需要删除 ORDER BY 子句,或者如您所指出的,依靠缓存来挽救这一天。

    查询时间是高负载服务器上性能不佳的衡量标准,因为在繁忙的服务器上任何查询都可能很慢。您应该使用 EXPLAINSHOW PROFILE 来分析您的查询并改进它们,以免它们给您的服务器带来过多的负载。

    允许在服务器上运行过多线程可能会导致级联效应,使您的服务器停止运行。如果您使用 InnoDB,请确保您的 thread concurrency 不会太高。最好让每个人都等待并一次服务 8 个线程(非常快),而不是尝试一次服务 100 个或无限线程(非常慢)。

    【讨论】:

    • 非常感谢!我会尝试:)
    【解决方案2】:

    除了调整查询以提高可读性之外,我还会尝试重新调整查询。
    我会确保您对(Story_Is_Permanent、Story_Cat、Story_Time)有一个索引

    我假设您有一个“故事类别”表,其中包含用于向用户展示的文字描述。由于您无论如何都在查询所有类别,但仅基于您想要包含或排除的某些类别......如果包含,它仅基于该类别的 PLUSES 与 MINUSES 的净计数。我会添加到该类别表的列中......类似于

    DefaultToInclude   integer as 1 or 0
    PlusMinusNetCount  integer
    

    然后,加入类别表并简化查询,例如

    SELECT 
          story_id 
       FROM 
          dug_stories d
             JOIN YourCategories YC
                on d.story_cat = YC.categoryID
       WHERE 
              d.story_is_permanent = 0  
          AND (     ( d.story_time > 1335190543 AND d.story_pluses - d.story_minuses > 5 )  
                 OR (     YC.DefaultToInclude = 0 
                      AND (    d.story_pluses - d.story_minuses > 12 
                           OR (d.story_pluses >= 10 AND d.story_minuses <= 0 ) 
                          )  
                    )
                 OR (     YC.DefaultToInclude = 1 
                      AND d.story_pluses-d.story_minuses > YC.PlusMinusNetCount )
              )
     ORDER BY 
          d.story_rating DESC, 
          d.story_time DESC 
       LIMIT 
          0, 10000;  
    




    (     YC.DefaultToInclude = 0 
      AND (    d.story_pluses - d.story_minuses > 12 
            OR (d.story_pluses >= 10 AND d.story_minuses <= 0 ) 
          )  
    )
    

    最终将取代

    OR (    (    d.story_pluses - d.story_minuses > 12 
              OR (   d.story_pluses >= 10 AND d.story_minuses <= 0 ) 
            )  
        AND d.story_cat!=48 
        AND d.story_cat!=131 
        AND d.story_cat!=55 
        AND d.story_cat!=44 
        AND d.story_cat!=126 
        AND d.story_cat!=53 
        AND d.story_cat!=370 
        AND d.story_cat!=381 
        AND d.story_cat!=304 
        AND d.story_cat!=497 
       )
    

     OR (     YC.DefaultToInclude = 1 
          AND d.story_pluses-d.story_minuses > YC.PlusMinusNetCount )
    

    将最终取代

    ( OR ( d.story_cat = 44  AND d.story_pluses - d.story_minuses > 9 ) 
      OR ( d.story_cat = 48  AND d.story_pluses - d.story_minuses > 9 ) 
      OR ( d.story_cat = 53  AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 55  AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 126 AND d.story_pluses - d.story_minuses > 13 ) 
      OR ( d.story_cat = 131 AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 304 AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 370 AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 381 AND d.story_pluses - d.story_minuses > 8 ) 
      OR ( d.story_cat = 497 AND d.story_pluses - d.story_minuses > 9 ) 
    ) 
    

    这允许灵活地简单地包含/排除另一个类别及其各自的“净”计数,而无需向您已经冗长的查询添加更多 OR / AND 条件。不管怎样,它似乎总是必须遍历整个“dug_Stories”表,因为您正在查看每个类别的一个条件和另一个类别。您的时间戳仅适用于 NOT EQUAL 类别,但您的其他条件没有任何时间戳限制(除非您的查询中出现了错误)。

    【讨论】:

      猜你喜欢
      • 2010-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-27
      相关资源
      最近更新 更多