1

问题

以下查询需要超过 30 秒才能运行,除非:

  • 我删除排序(查询然后<1秒)
  • 我删除了 distinct 关键字:(查询然后 <1 秒)
  • 开始删除连接(查询然后 <5 秒)

问题

我怎样才能让这个查询在 1 秒内运行。必需:如何获得具有相关数据的唯一会议列表,如下面的连接所述,包括某种类型。

相关数据既用于确定是否存在相关字段,也用于执行 GROUP_CONCAT 操作 - 因此需要对同一个预定项目表进行 3 个不同的连接。

提前感谢您的任何帮助和建议!我已经在这个问题上敲了几个小时!

询问

SELECT

DISTINCT( `meetings`.`id` ) AS `meeting_id`,
`meetings`.`uid` AS meeting_uid,
`meetings_SERV`.`id` AS meetings_SERV_id, 
`meetings_TRANSP`.`id` AS meetings_TRANSP_id, 
`meetings_ACCO`.`id` AS meetings_ACCO_id, 
`meetings_BOOKEDITEMS`.`id` AS meetings_BOOKEDITEMS_id

FROM `meetings` AS meetings 

LEFT OUTER JOIN `bookeditems` AS `meetings_SERV` 
ON `meetings`.`uid` = `meetings_SERV`.`meeting_uid`
AND 'SER' = `meetings_SERV`.`item_type` 

LEFT OUTER JOIN `bookeditems` AS `meetings_TRANSP` 
ON `meetings`.`uid` = `meetings_TRANSP`.`meeting_uid`
AND 'TRA' = `meetings_TRANSP`.`item_type` 

LEFT OUTER JOIN `bookeditems` AS `meetings_ACCO` 
ON `meetings`.`uid` = `meetings_ACCO`.`meeting_uid`
AND 'ACC' = `meetings_ACCO`.`item_type` 

LEFT OUTER JOIN `bookeditems` AS `meetings_BOOKEDITEMS` 
ON `meetings`.`uid` = `meetings_BOOKEDITEMS`.`meeting_uid` 

ORDER BY `meetings`.`datetime`

LIMIT 0, 50

表定义

CREATE TABLE IF NOT EXISTS `bookeditems` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `meeting_uid` varchar(256) NOT NULL,
  `item_type` varchar(256) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `meeting_uid` (`meeting_uid`(255)),
  KEY `index1` (`meeting_uid`(255),`item_type`(255))
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=5889 ;

CREATE TABLE IF NOT EXISTS `meetings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uid` varchar(256) NOT NULL,
  `datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `uid` (`uid`(255)),
  KEY `datetime` (`datetime`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=7487 ;

解释选择的结果

id | select_type | table                | type | possible_keys      | key         | key_len | ref                      | rows | Extra
-------------------------------------------------------------------------------------------------------------------------------------
1  | SIMPLE      | meetings             | ALL  | NULL               | NULL        | NULL    | NULL                     | 7483 | Using temporary; Using filesort
1  | SIMPLE      | meetings_SERV        | ref  | meeting_uid,index1 | meeting_uid | 767     | test.meetings.uid        | 1    | 
1  | SIMPLE      | meetings_TRANSP      | ref  | meeting_uid,index1 | meeting_uid | 767     | test.meetings.uid        | 1    | 
1  | SIMPLE      | meetings_ACCO        | ref  | meeting_uid,index1 | meeting_uid | 767     | test.meetings.uid        | 1    | 
1  | SIMPLE      | meetings_BOOKEDITEMS | ref  | meeting_uid,index1 | meeting_uid | 767     | test.meetings.uid        | 1    | 

分析结果

starting                      0.000092
checking permissions          0.000003
checking permissions          0.000002
checking permissions          0.000001
checking permissions          0.000001
checking permissions          0.000003
Opening tables                0.000036
System lock                   0.000008
init                          0.000033
optimizing                    0.000005
statistics                    0.000035
preparing                     0.000019
Creating tmp table            0.000165
executing                     0.000004
Copying to tmp table          1.790968
converting HEAP to MyISAM     1.669041
Copying to tmp table on disk  28.32606
Sorting result                0.141737
Sending data                  0.000099
end                           0.000005
removing tmp table            0.022097
end                           0.000014
query end                     0.000008
closing tables                0.000017
freeing items                 0.000779
logging slow query            0.000004
cleaning up                   0.000005

零件解决方案

根据下面 Eric R. Rath 的帮助,我已经分析了查询,并通过将 max_heap_table_size=256M 和 tmp_table_size=256M 添加到 MySQL 配置中,我已经能够消除执行步骤“将 HEAP 转换为 MyISAM”和“复制到磁盘上的 tmp 表” ”。

尽管这将总执行时间降至 2 秒以下,但我仍然不相信这是我能做的一切,如果在查询优化方面有任何其他建议,请告诉我。

按照 max_heap_table_size 和 tmp_table_size 配置进行分析

...
executing                     0.000004
Copying to tmp table          1.790968
Sorting result                0.141737
...
4

3 回答 3

0

您可以尝试通过首先SELECT-ing 感兴趣的记录(使用ORDER BYLIMIT)来帮助优化器,然后使用该结果与其他表连接。使用这种方法,索引 ondatetime可以完全用于ORDER BYandLIMIT子句。这是这种方法的样子:

SELECT
  `meetings`.`id` AS `meeting_id`,
  `meetings`.`uid` AS meeting_uid,
  GROUP_CONCAT(
   DISTINCT CASE bookeditems.item_type
     WHEN 'SER' THEN bookeditems.id
     ELSE NULL
   END
  ) AS meetings_SERV_ids,
  GROUP_CONCAT(
   DISTINCT CASE bookeditems.item_type
     WHEN 'TRA' THEN bookeditems.id
     ELSE NULL
   END
  ) AS meetings_TRANSP_ids,
  GROUP_CONCAT(
   DISTINCT CASE bookeditems.item_type
     WHEN 'ACC' THEN bookeditems.id
     ELSE NULL
   END
  ) AS meetings_ACCO_ids,
  GROUP_CONCAT(bookeditems.id) AS meetings_BOOKEDITEMS_ids
FROM (
  SELECT id
  FROM meetings
  ORDER BY `datetime`
  LIMIT 0, 50
) filtered_meetings
INNER JOIN meetings
  ON meetings.id = filtered_meetings.id
LEFT OUTER JOIN bookeditems
  ON meeting_uid = bookeditems.meeting_uid
GROUP BY meeting_uid

显着差异:

  • 我们只加入一次预订项目表。在 select-statement 中,我们使用GROUP_CONCATtogether withCASE语句来选择会议组中符合特定条件的所有 id。此外,为此,我们需要添加一个GROUP BY语句来对代表同一会议的所有行进行分组。

    这意味着我们可能会得到meetings_SERV_idsmeetings_TRANSP_idsmeetings_ACCO_ids的id 字符串meetings_BOOKEDITEMS_ids。因此,请记住explode在处理结果行的客户端代码中使用 或等效项。
于 2012-06-11T19:07:07.933 回答
0

我认为 index2 是不必要的,可以删除;它是 index1 的前缀。不过,这不会减少查询时间。

EXPLAIN 输出显示了真正的罪魁祸首:“使用临时,使用文件排序”。有时您可以通过让 MySQL 使用用于连接的相同键执行排序来避免这种情况。如果将 index1 更改为 (meeting_id, datetime),它也许可以这样做。如果您需要将 item_type 保留在 index1 中,您可以将其添加为索引中的第三列,或者在查询中包含一个包含所有值的 IN() 子句。

于 2012-06-08T23:39:33.450 回答
0

让我们检查索引。

您在预定项目中是否有关于 meeting_id 和 item_type 的复合索引?

您是否有关于会议的会议 ID 的索引,如果它在复合键中,它是第一个索引吗?

您是否有关于会议日期时间的聚集索引?

您可以放置​​一个子查询来获取每个会议 ID 的堆栈顶部,而不是做不同的操作吗?

就像是:

select * from meetings a
where datetime = (select max(datetime) from meetings 
where meetingid = a.meetingid)

您可以使用 case 语句而不是会议类型来获取相同的数据,而不是左加入吗?

于 2012-06-08T14:07:12.900 回答