优化复杂的mysql以减少查询时间

Posted

技术标签:

【中文标题】优化复杂的mysql以减少查询时间【英文标题】:Optimizing a complex mysql to reduce query time 【发布时间】:2021-10-07 16:03:14 【问题描述】:

我有一个非常复杂的查询,我已经优化了很多,但是我找不到更好的方法来编写更优化的查询并减少查询时间。让我分享一下细节,以便您更好地理解它。

以下是我的查询。

SELECT a.msisdn, GROUP_CONCAT( COALESCE(a.answer, 'skip') order by a.question_id separator ',') as answer,

(SELECT GROUP_CONCAT( COALESCE(answer, 'skip') order by question_id separator ',') as answer FROM  `campaign_survey_responses`
 WHERE campaign_id = 11559 and question_id=14751 and msisdn=a.msisdn) as a1,
 
 (SELECT GROUP_CONCAT( COALESCE(s.answer, 'skip') order by s.question_id separator ',') as answer 
FROM  `campaign_survey_responses` s left join campaign_survey_questions q on q.id = s.question_id 
WHERE s.campaign_id = 11559 and q.parent_id=5128 and q.sort_order = 0 and s.msisdn=a.msisdn) as sur1,
 
(SELECT GROUP_CONCAT( COALESCE(answer, 'skip') order by question_id separator ',') as answer FROM  `campaign_survey_responses`
WHERE campaign_id = 11559 and question_id=14768 and msisdn=a.msisdn) as a2,

(SELECT GROUP_CONCAT( COALESCE(s.answer, 'skip') order by s.question_id separator ',') as answer 
FROM  `campaign_survey_responses` s left join campaign_survey_questions q on q.id = s.question_id 
WHERE s.campaign_id = 11559 and q.parent_id=5108 and q.sort_order = 0 and s.msisdn=a.msisdn) as sur2,

(SELECT GROUP_CONCAT( COALESCE(answer, 'skip') order by question_id separator ',') as answer FROM  `campaign_survey_responses`
WHERE campaign_id = 11559 and question_id=14785 and msisdn=a.msisdn) as a3,

(SELECT GROUP_CONCAT( COALESCE(s.answer, 'skip') order by s.question_id separator ',') as answer 
FROM  `campaign_survey_responses` s left join campaign_survey_questions q on q.id = s.question_id 
WHERE s.campaign_id = 11559 and q.parent_id=5148 and q.sort_order = 0 and s.msisdn=a.msisdn) as sur3

FROM  `campaign_survey_responses` a
WHERE a.campaign_id = 11559 and a.question_id=14750 group by msisdn limit 500;

上面的查询组合了 100 万行,仅生成大约 50,000 行,现在唯一的目的是导出为 csv。但是上面的查询在运行时耗时太长。

我从该查询中应用了 500 的限制,500 行的限制大约需要 78.844856977463 秒,但是 50K 行呢?它会关闭服务器。

任何优化查询的更好方法,我使用的是 mysql 5.7,谢谢

它返回如下数据,限制500,将有500条记录,以下仅以2条记录为例。

0 => 
    array (size=8)
      'msisdn' => string '3003932957' (length=10)
      'answer' => string '1' (length=1)
      'answer1' => string '4' (length=1)
      'survey1' => null
      'answer2' => null
      'survey2' => null
      'answer3' => null
      'survey3' => null
  1 => 
    array (size=8)
      'msisdn' => string '3013555354' (length=10)
      'answer' => string '1' (length=1)
      'answer1' => string '3' (length=1)
      'survey1' => string '2,1,1' (length=5)
      'answer2' => null
      'survey2' => null
      'answer3' => null
      'survey3' => null
  2 => 

表结构:

CREATE TABLE `campaign_survey_responses` (
 `id` int unsigned NOT NULL AUTO_INCREMENT,
 `campaign_id` int unsigned NOT NULL,
 `question_id` int unsigned NOT NULL,
 `answer` varchar(20) DEFAULT NULL,
 `msisdn` int unsigned NOT NULL,
 `campaign_date` date DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `campaign_id` (`campaign_id`),
 KEY `question_id` (`question_id`)
) ENGINE=MyISAM AUTO_INCREMENT=9016122 DEFAULT CHARSET=utf8

添加了表结构,基本上系统生成的调用,根据用户输入,它得到的响应,如 1,2,3 等被添加到上表。例如,它是一种多层次调查。有一个父题,有3个选项,如果用户选择选项2,系统将播放与选项2相关的问题。在该问题之后,如果用户选择任何选项,将有10个针对选项2的问题。

选项 1 和 3 类似。

Parent Question ( it has 3 sub questions )
Each 3 of subquestions has 10 questions each.

如果用户选择子问题1,系统将播放与子问题1相关的10个问题,他不会播放选择2,3的其他相关问题。这取决于用户,他想玩哪个问题。

【问题讨论】:

请将表定义添加到问题和EXPLAIN原始查询的结果中。 我知道尝试将所有内容都放到一个查询中很有趣,但有时另一种解决方案是使用更小/更便宜的查询获取相关数据,然后使用另一种语言对其进行后处理.并不总是理想的,但有时可能会有所帮助,具体取决于您的设置。 添加了表结构并添加了更多解释。我已经分享了我得到的回复,这对我来说是正确的。 【参考方案1】:

campaign_survey_responses 上的这些复合索引将帮助很多人:

INDEX(campaign_id, question_id,  msisdn, answer)
INDEX(campaign_id, msisdn, question_id,  answer)

(第一个可能会帮助a;第二个可能会帮助s。)

【讨论】:

这确实有帮助,只是添加复合索引减少了查询时间,500 行需要 78 秒,但添加索引后,500 行需要 0.12 秒。在 3-4 秒内完成 53K 行。 好。索引的新信徒。 非常感谢,我在生产服务器上使用 MySQL 5.1,这是一个旧项目。索引真的很有帮助。 问题说“5.7”;是哪一个?您等待从 5.1 升级的时间越长,就越难。幸运的是,大多数索引建议在两个版本中的工作方式相同。 我多次告诉我的客户让我升级,他不听。随着项目越来越大,每天都会有许多新的变化。我在所有新项目中都使用 Maria db 10 或 Mysql 8。【参考方案2】:

SELECT 中的相关子查询可能是一个真正的拖累。你有很多这样的。如果这是不可避免的,我会将其应用于结果的子集而不是整个结果。即我会先创建内联视图

SELECT 
   -- your correlated subqueries as fields here
FROM
   ( -- start inline view
       SELECT -- your columns
       FROM  `campaign_survey_responses` a
       WHERE a.campaign_id = 11559 and a.question_id=14750 
       GROUP BY msisdn 
       LIMIT 500;
   ) T1

这样,相关子查询只涉及 500 条记录

【讨论】:

所有子查询在选择字段中都是相同的,但它们有不同的 where 条件,使用上面的查询如何实现? @AsfandyarKhan 在您的where 子查询中,您需要参考T1.field。这里的想法是 - 您首先获得基本结果集。然后将子查询与此相关联。假设您的主查询返回 1000 行。如果这些子查询都针对它们执行 - 您将获得最大执行时间。如果您输入LIMIT 500,您仍然会针对 1000 执行,但之后只需要 500。但是如果您首先通过创建内联视图执行不带子查询的 500,那么您的子查询将针对 500 执行,从而减少一半的必要执行时间。 你所说的“内联视图”,MySQL 似乎称之为“派生表”。 @RickJames 是的。但它是同义词。就像“汽车”和“汽车”

以上是关于优化复杂的mysql以减少查询时间的主要内容,如果未能解决你的问题,请参考以下文章

mysql 优化慢复杂sql (多个left join 数量过大 order by 巨慢)

优化 mysql 查询以减少搜索的行数

mysql经典面试题

抛开复杂的架构设计,MySQL优化思想基本都在这

最近很火的MySQL:抛开复杂的架构设计,MySQL优化思想基本都在这

大数据量时Mysql的优化