【问题标题】:How can I make this sql query faster?如何使这个 sql 查询更快?
【发布时间】:2017-01-03 05:54:41
【问题描述】:

我有一个表 user_notifications 有 1100000 条记录,我必须在下面运行这个查询,但是完成查询需要 3 分钟以上我可以做些什么来改善获取时间。

SELECT `user_notifications`.`user_id`
FROM `user_notifications`
WHERE `user_notifications`.`notification_template_id` = 175
AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day))
AND `user_notifications`.`user_id` IN (
  1203, 1282, 1499, 2244, 2575, 2697, 2828, 2900, 3085, 3989,
  5264, 5314, 5368, 5452, 5603, 6133, 6498..
)

IN 块中的用户 ID 有时高达 1k。

为了优化,我在user_notification 表中的user_idnotification_template_id 列上建立了索引。

【问题讨论】:

  • 比较 1000 user_id 1+ 百万条记录的值需要一些时间,即使 MySQL 正在使用你想要的索引。 “年龄”有多长?
  • @TimBiegeleisen 平均 2-5 分钟
  • 您是在 user_id 和 notification_template_id 上创建了单独的索引,还是为两者创建了一个?试试后者。
  • @wumpz 单独索引。请看我附上的截图。如何为两者创建一个?
  • create index my_compound_index on user_notifications(user_id, notification_template_id)

标签: mysql sql query-performance


【解决方案1】:

Big IN() 列表本身就很慢。创建一个带有索引的临时表,并将 IN() 列表中的值放入该临时表中,然后您将获得索引连接的强大功能,而不是巨大的 IN() 列表。

【讨论】:

  • 感谢@Dan,这似乎合乎逻辑。你能告诉我我们还可以为 sent_at 字段做些什么吗,我认为这也是导致查询延迟的原因。
  • @Prem 你有什么事实支持这个观点吗? codeforester 解决了这个问题,我对这个答案的评论也很相关。但是您的 IN 子句几乎可以肯定是造成大部分延迟的原因。
  • 我在IN() 中看到了 70K 项的问题,但 1K 没有。
【解决方案2】:

您似乎在查询一个小的日期范围。有一个基于 SENT_AT 列的索引怎么样?你知道当前查询使用的是什么索引吗?

【讨论】:

  • 也在那个主题上,你真的需要将sent_at 转换为日期吗?似乎摆脱该演员会产生相同的结果,因为如果 DATE(sent_at) 大于给定值,则 sent_at 本身必须至少那么大。
  • @DanFarrell 不能 100% 确定,但我敢打赌 1000 次用户 ID 比较是导致查询失败的原因。
  • DATE(sent_at) 可能有问题,因为它可能会因为函数调用而阻止使用任何基于 sent_at 的索引,除非您有基于函数的索引。
  • INDEX(sent_at) 绝对不如INDEX(notification_template_id, sent_at) 好(对于这个查询)。先等于,然后是一个范围。
【解决方案3】:

(1) 如果您可能需要使用索引,请不要在函数中隐藏列:

AND (DATE(sent_at) >= DATE_SUB(CURDATE(), INTERVAL 4 day))

-->

AND sent_at >= CURDATE() - INTERVAL 4 day

(2) 使用“复合”索引

WHERE `notification_template_id` = 175
  AND sent_at >= ...
  AND `user_id` IN (...)

第一列应该是带有“=”的列。目前还不清楚接下来要放什么,所以我建议添加这两个索引:

INDEX(notification_template_id, user_id, sent_at)
INDEX(notification_template_id, sent_at)

优化器可能会正确地在它们之间进行选择。

复合索引与单个列上的索引相同。

(3) 是的,您可以尝试将 IN 列表放入 tmp 表中,但这样做的成本可能超过收益。我不认为IN() 中的 1K 值“太多”。

(4) My cookbook 关于构建索引。

【讨论】:

  • 如果我需要创建composite_index INDEX(notification_template_id, user_id, sent_at) 是否需要删除单独的索引?
  • 这样想...当姓名列表按姓氏排序时,“不可能”通过名来搜索某人。所以,不,不要删除任何其他索引(在没有首先发现没有其他可能需要它们的情况下)。
  • 我的索引可能对其他查询有用也可能没用。
猜你喜欢
  • 1970-01-01
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2014-11-29
  • 1970-01-01
相关资源
最近更新 更多