首页 文章

MySQL独特的排序查询性能问题

提问于
浏览
1

问题

以下查询运行时间超过30秒,除非:

  • 我删除排序(查询然后<1秒)

  • 我删除了distinct关键字:(查询然后<1秒)

  • 开始删除联接(查询然后<5秒)

问题

如何在1秒内运行此查询 . 必需:如何获得具有相关数据的唯一 Session 列表,如下面的联接所描述的那样,包括某种类型 .

相关数据既用于确定是否存在相关字段,也用于执行GROUP_CONCAT操作 - 因此要求对同一个registereditems表具有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 ;

EXPLAIN SELECT的结果

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的帮助,我已经分析了查询,并通过向MySQL配置添加max_heap_table_size = 256M&tmp_table_size = 256M,我已经能够消除“将HEAP转换为MyISAM”和“复制到磁盘上的tmp表”的执行步骤” .

虽然这会使总执行时间缩短到2秒以下,但我仍然不相信这是我能做的一切,如果在查询优化方面有任何其他建议请告诉我 .

在max_heap_table_size和tmp_table_size配置之后进行性能分析

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

3 回答

  • 0

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

    EXPLAIN输出显示真正的罪魁祸首:“使用临时,使用filesort” . 有时您可以通过让MySQL使用与加入相同的密钥执行排序来避免这种情况 . 如果将index1更改为(meeting_id,datetime),则可能会这样做 . 如果需要在index1中保留item_type,可以将其添加为索引中的第三列,或者在查询中包含所有值的IN()子句 .

  • 0

    让我们检查索引 .

    您是否在铺设项目中的meeting_id和item_type上有复合索引?

    您是否在 Session 上有 Session ID的索引,如果它在复合键中,它是第一个索引吗?

    您在 Session 的日期时间是否有聚集索引?

    您可以使用子查询来获取每个 Session ID的堆栈顶部,而不是区分吗?

    就像是:

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

    您是否可以使用case语句而不是 Session 类型来获取相同的数据?

  • 0

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

    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语句中,我们使用 GROUP_CONCATCASE 语句来选择 Session 组中符合特定条件的所有ID . 此外,为此,我们需要添加 GROUP BY 语句来对表示同一 Session 的所有行进行分组 .

    这意味着我们可能会为 meetings_SERV_idsmeetings_TRANSP_idsmeetings_ACCO_idsmeetings_BOOKEDITEMS_ids 获取一串id . 所以请记住处理结果行的客户端代码中的 explode 或等效项 .

相关问题