【问题标题】:MySQL Performance ORDERBY datetime on single table单个表上的 MySQL 性能 ORDERBY 日期时间
【发布时间】:2018-07-30 18:40:06
【问题描述】:

我有一个这样的 MySql 事件表:

+---------------------+--------------+------+-----+---------+----------------+
| Field               | Type         | Null | Key | Default | Extra          |
+---------------------+--------------+------+-----+---------+----------------+
| EventId             | int(11)      | NO   | PRI | NULL    | auto_increment |
| ControllerId        | int(11)      | NO   | MUL | NULL    |                |
| EventTypeId         | int(11)      | NO   | MUL | NULL    |                |
| DateTime            | datetime(3)  | NO   | MUL | NULL    |                |
| InputId             | int(11)      | YES  | MUL |         |                |
...
| AdditionalInfo      | text         | YES  |     |         |                |
+---------------------+--------------+------+-----+---------+----------------+

目前它有 200M 条记录,并且它一直在运行文件。为了保持速度,我不使用任何连接查询它,但现在我开始遇到一些长时间运行的查询。一个运行缓慢的查询示例如下:

SELECT E.* 
FROM Event E
WHERE (E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)) 
AND E.EventTypeId != 27 AND E.EventTypeId != 12  
AND E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200

该查询需要 5 分钟!解释的重要(我认为)部分如下所示:

"key_length": "7",
"rows_examined_per_scan": 180071,
"rows_produced_per_join": 125770,
"filtered": "0.06",
"cost_info": {
    "read_cost":    "284389.84",
    "eval_cost":    " 25154.17",
    "prefix_cost":  "309544.01",
    "data_read_per_join": "20M"
},

现在,如果我在查询结束时删除ORDER BY E.DateTime DESC,大约需要 0.1 秒才能完成。我已经在 DateTime 上有一个索引。

我想我理解服务器必须读取所有 180k 的概念? WHERE 子句返回的行在返回给客户端之前对其进行排序,但为什么需要这么长时间?我能做些什么吗?复合索引在这里有用吗?

【问题讨论】:

  • 那么数据库要遍历和排序的记录很多,尝试将您的限制缩小到更小的数量?!
  • 查询是针对向用户显示的“最近事件”,它们有可选择的最近事件数量,最大为 200,默认为 20。我有 some 如果我取消了查看 200 个最近事件的选项,用户会大吃一惊。
  • 这不是有点像eventtypeid NOT IN(4,27,12) OR (...)
  • 是的,我没有考虑过-尽管答案中提到了,谢谢。已将其修改为WHERE (E.EventTypeId != 4 OR (E.InputId IS NOT NULL)) AND E.EventTypeId NOT IN (12, 27)

标签: mysql sql performance select query-performance


【解决方案1】:

对 180k 行进行排序不应花费 5 分钟,除非您的硬件非常非常慢。对于这个查询:

SELECT E.* 
FROM Event E
WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
      )  AND
      E.EventTypeId NOT IN (12, 27) AND
      E.ControllerId in (5190, 5191, 5192, 5193)
ORDER BY E.DateTime DESC
LIMIT 0, 200;

您可以尝试索引(ControllerId, EventTypeId, InputId)。不过,我猜这不会很好。

一种可能性是使用上面的索引,然后一次做一个控制器:

(SELECT E.* 
 FROM Event E
 WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
       )  AND
       E.EventTypeId NOT IN (12, 27) AND
       E.ControllerId  = 5190
 ORDER BY E.DateTime DESC
 LIMIT 0, 200
) UNION ALL
(SELECT E.* 
 FROM Event E
 WHERE (E.EventTypeId <> 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL)
       )  AND
       E.EventTypeId NOT IN (12, 27) AND
       E.ControllerId  = 5191
 ORDER BY E.DateTime DESC
 LIMIT 0, 200
) 
. . . 
ORDER BY DateTime DESC
LIMIT 0, 200;

索引可以更有效地用于每个子查询。

【讨论】:

  • 这极大地提高了查询的速度,(将其降低到 2 秒以下)而且我觉得自己像个白痴,因为没有尝试单独查询它们!谢谢,我可能会在我的数据层而不是 SQL 中实现单独的查询。
  • 哦,我忘了补充,它在使用 SSD 的 AWS m4.large 实例上运行。我知道它不在上面,但无论如何它并不慢。也许这个查询很慢,因为有INSERTs 的恒定流
  • 我怀疑它会影响性能,但E.EventTypeId = 4 条件实际上是多余的。
  • 这是一个很好的观点,我没有注意到这一点。我将在我的代码中修改查询,谢谢。
  • @shawnt00 我的错,你是对的,错过了查询;)(我删除,因为它是错误的)
【解决方案2】:

您对JOINs 的恐惧是错误的。当然,有些JOINs 的成本很高,但有些则通过避免JOIN 来慢得多。

这里的查询不能通过更改/添加索引来优化。

没有ORDER BY,它会快速扫描表的部分,找到200行并退出。使用ORDER BY,它必须扫描整个表,排序,然后剥离 200 行。

戈登的索引可能是最好的选择。但是,如果IN 列表是动态的,则意味着动态构建UNION

较新版本的 MySQL 将更好地利用他的 3 列 INDEX,因为过滤现在完全在 InnoDB 引擎中完成,而不是返回到通用“处理程序”。

这可以简化

(E.EventTypeId != 4 OR (E.EventTypeId = 4 AND E.InputId IS NOT NULL))

到 (E.EventTypeId != 4 或 E.InputId 不为空)

但它不会加快速度到足以引起注意。

请使用SHOW CREATE TABLE;它比DESCRIBE 更具描述性。

INT 允许 +/-20 亿的巨大范围。需要那么多id吗?它需要 4 个字节。考虑其他大小,例如SMALLINT UNSIGNED(2 字节,0..65K)。缩小表大小对性能有一些影响。

你真的需要E.*吗?如果您不需要所有列,则拼出您确实需要的列会运行得更快;在某些情况下明显更快。

还有一件事......如果你正在“分页”,那么有一个技巧可以处理UNION + LIMIT + OFFSET;见here

【讨论】:

  • 谢谢,但您似乎没有很好地阅读这个问题。我不担心 JOIN,在我的用例中不使用它们会更快,我在它的位置大量使用缓存。此外,您关于简化 WHERE 条件的评论是此页面上其他 cmets 的重复,您似乎完全按照我在一周前的评论中已将其更改为的内容写了。
  • 不幸的是,虽然这里没有列出,但事件表中还有很多其他列带有外键,所有这些列都以类似的方式查看。因此,我必须在一个有 2 亿行的表上创建相当多的索引,而这并没有强烈的共识,这会有所帮助。我确实需要所有列向用户显示事件是的,谢谢。关于 Id,正如问题中已经说明的那样,我已经有 2 亿行了。
猜你喜欢
  • 2011-02-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2021-06-13
相关资源
最近更新 更多