【问题标题】:MySQL - Views - Super slow queryMySQL - 视图 - 超慢查询
【发布时间】:2011-11-24 02:44:55
【问题描述】:

这是一个奇怪的问题。我正在尝试在 MySQL 中使用视图(我对 MySQL 相当陌生,对 Sybase 和 SQL Server 有更多经验)。无论如何,这个新项目我们正在使用 MySQL,因为它似乎具有良好的性能。然而,为了让 Web 前端的查询更简单,我们决定创建一些视图,它们都运行良好,但它们需要很长时间才能运行。

视图非常简单,只需选择语句(这些表中确实有几百万行)。比如说这个查询:

SELECT CAST(classifier_results.msgDate as DATE) AS mdate
       ,classifier_results.objClass AS objClass
       ,COUNT(classifier_results.objClass) AS obj
       ,classifier_results.subjClass AS subjClass
       ,COUNT(classifier_results.subjClass) AS subj 
FROM classifier_results 
WHERE (classifier_results.msgDate >= (curdate() - 20)) 
GROUP BY 
  CAST(classifier_results.msgDate as DATE)
  ,classifier_results.objClass
  ,classifier_results.subjClass 
ORDER BY classifier_results.msgDate DESC

当作为正常选择运行时,大约需要 1.5 秒才能返回结果。

但是,当这个查询被放入视图时(按原样) - 即

CREATE VIEW  V1a_sentiment_AI_current AS    
SELECT CAST(classifier_results.msgDate as DATE) AS mdate
       ,classifier_results.objClass AS objClass
       ,COUNT(classifier_results.objClass) AS obj
       ,classifier_results.subjClass AS subjClass
       ,COUNT(classifier_results.subjClass) AS subj 
FROM classifier_results 
WHERE (classifier_results.msgDate >= (curdate() - 20)) 
GROUP BY 
  CAST(classifier_results.msgDate as DATE)
  ,classifier_results.objClass
  ,classifier_results.subjClass 
ORDER BY classifier_results.msgDate DESC

查询需要大约 10 倍的时间(22-30 秒)。所以我在想也许有一些优化或查询缓存不适用于视图,或者我们在 MySQL 配置中遗漏了一些设置。但是有什么方法可以加快这个视图,让它只是这个查询的一个很好的占位符?

对两个查询运行 EXPLAIN: 正常的选择给出:

1, SIMPLE, classifier_results, ALL, idx_date, , , , 594845, 使用where;使用临时的;使用文件排序

视图选择给出:

1, 初级, , 全部, , , , , 100,
2、DERIVED、classifier_results、ALL、idx_date、、、、、594845、使用where;使用临时的;使用文件排序

【问题讨论】:

  • 如果对查询和视图选择都使用 EXPLAIN,会得到不同的结果吗?
  • 已添加到问题中。查询计划看起来是一样的,我假设 eprimary 只是视图的返回,因为它在某种意义上是嵌套的,没有什么表明需要额外运行 20 秒以上......
  • 我认为DERIVED 表示它正在使用临时表,这会影响性能
  • 我觉得它看起来不错。但是所以你冷静一下,我已经删除了 tildas。现在开心?有什么成果吗?
  • 我似乎记得 MySQL 的 VIEW 并不像您在其他引擎中发现的那样优化。一个快速的谷歌搜索让我看到这篇关于MySQL VIEW as performance troublemaker的文章

标签: mysql sql view query-optimization


【解决方案1】:

尝试使用以下方法重新创建您的视图:

CREATE ALGORITHM = MERGE VIEW `V1a_sentiment_AI_current` AS    
SELECT CAST(classifier_results.msgDate as DATE) AS mdate
   ,classifier_results.objClass AS objClass
   ,COUNT(classifier_results.objClass) AS obj
   ,classifier_results.subjClass AS subjClass
   ,COUNT(classifier_results.subjClass) AS subj 
FROM classifier_results 
WHERE (classifier_results.msgDate >= (curdate() - 20)) 
GROUP BY 
  CAST(classifier_results.msgDate as DATE)
  ,classifier_results.objClass
  ,classifier_results.subjClass 
ORDER BY classifier_results.msgDate DESC

关于 MySQL 的视图处理算法的更多信息可以找到here。

【讨论】:

  • 好的,感谢您提供此链接。该视图以前是未定义的。通过尝试将算法更改为 MERGE 给出:0 行受影响,1 个警告:1354 视图合并算法暂时不能在此处使用(假设未定义算法)。尝试使用 TEMPTABLE 算法创建视图效果很好。因此,随着 UNDEFINED 我猜测它可能正在使用 TempTable,因为 undefined 似乎在合并和临时表之间做出了选择。所以这可能是问题所在,因为手册说合并更有效......
  • 尝试从视图中删除 ORDER BY 子句,看看它是否适用于 MERGE 算法
  • 删除ORDER BY 仍然不想作为合并工作。 1354 View merge algorithm can't be used here for now (assumed undefined algorithm)
  • 抱歉,我没有主意 :( MySQL 说它不能使用 MERGE 算法,但我不知道为什么 - 希望其他人可以帮忙。
  • 从问题和答案/cmets 中学到了很多东西,谢谢 - 在答案中提供的链接中,它说 MERGE 算法不能与视图的选择部分中的计数一起使用。跨度>
【解决方案2】:

这是一个非常普遍的问题。编写干燥的、可重用的 SQL 可能非常困难。不过我找到了一种解决方法。

首先,正如其他人指出的那样,您可以并且应该尽可能使用 VIEW 来执行此操作,使用 set ALGORITHM = MERGE,以便使用它们的任何查询都在合并的 SQL 语句的 where 子句上进行优化,而不是评估 VIEW对于整个视图,这可能是灾难性的大。

在这种情况下,由于组/计数方面的原因,您不能使用 MERGE,因此您可能希望尝试使用创建临时会话表的存储过程作为解决方法。

此技术允许您编写可从中间件/框架代码访问并从其他存储过程内部调用的可重用查询,因此您可以保持代码的包含性、可维护性和可重用性。

即如果您事先知道查询将在某些条件下被过滤,请将它们放在存储过程中。 (对数据集或组合进行后过滤可能更有效 - 这取决于您如何使用数据以及需要哪些公共集)。

CREATE PROCEDURE sp_create_tmp_V1a_sentiment_AI_current(parm1, parm2 etc)
BEGIN

  drop temporary table if exists tmp_V1a_sentiment_AI_current;

  create temporary table tmp_V1a_sentiment_AI_current
  as
  SELECT CAST(classifier_results.msgDate as DATE) AS mdate
         ,classifier_results.objClass AS objClass
         ,COUNT(classifier_results.objClass) AS obj
         ,classifier_results.subjClass AS subjClass
         ,COUNT(classifier_results.subjClass) AS subj 
  FROM classifier_results 
  WHERE (classifier_results.msgDate >= (curdate() - 20)) 
  -- and/or other filters on parm1, parm2 passed in
  GROUP BY 
    CAST(classifier_results.msgDate as DATE)
    ,classifier_results.objClass
    ,classifier_results.subjClass 
  ORDER BY classifier_results.msgDate DESC;

END;

现在,当您需要处理这些数据时,您可以调用该过程,然后选择结果(可能带有额外的 where 子句参数)或在任何其他查询中加入。

该表是一个会话临时表,因此它会在对过程的调用之后持续存在。调用代码可以在处理完数据后将其删除,也可以在会话结束或随后调用 sproc 时自动运行。

希望对您有所帮助。

【讨论】:

    【解决方案3】:

    由于选择列表中的 count() 聚合,此处不能使用 MERGE;在这些情况下,指定 TEMPTABLE 以使引擎不必在它们之间做出决定可能会有所帮助。一旦我决定使用哪种算法,我会查看 EXPLAIN 计划并尝试添加索引提示或找到丢失的索引。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-20
      • 1970-01-01
      • 2020-09-02
      • 2014-06-16
      • 2010-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多