2

我用 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;  

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

4

2 回答 2

0

除了调整查询以提高可读性之外,我还会尝试对查询进行更多重组。
我会确保您对( 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 类别,但您的其他条件没有任何时间戳限制(除非这是您的查询中的错误)。

于 2012-04-24T15:03:38.577 回答
0

如果 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 子句,或者,如您所指出的,依靠缓存来挽救这一天。

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

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

于 2012-04-24T14:03:20.340 回答