【问题标题】:Improving performance when querying tables by datetime按日期时间查询表时提高性能
【发布时间】:2021-06-13 06:39:42
【问题描述】:

我制作了一个应用程序,在登录和退出另一个应用程序时注册用户。该应用程序基本上将用户 ID、设备、IP 和登录记录为时间戳。

id ???? user ???? device ip signed_in signed_out
1 38 ChromeOS 174.134.24.35 2020-05-01 09:32:01 2020-05-01 18:32:01
2 39 ChromeOS 174.134.24.35 2020-05-01 09:32:01 2020-05-01 18:32:01

我将保存详细信息,只是说这个应用程序的目的是然后汇总用户每个月在另一个应用程序中花费的时间。

获取一个月内所有用户的SQL查询比较简单:

SELECT * 
FROM users
WHERE id = 38 
  AND signed_in >= 2020-05-01 00:00:00
  AND signed_out < 2020-06-01 00:00:00

虽然最初没有任何问题,但据报道,生成总和的速度大幅放缓,随着越来越多的人使用网络服务,该总和已经增长。经过一些干预后,由于时间戳的原因,查询速度大大减慢,因为必须进行全面扫描。

可能的解决方案

我还没有找到任何解决这个性能问题的方法,除了一个:为年月组合添加列,索引它们,并将它们与查询一起使用。

id ???? user ???? device ip year ???? month ???? signed_in signed_out
1 38 ChromeOS 174.134.24.35 2020 5 2020-05-01 09:32:01 2020-05-01 18:32:01
2 39 ChromeOS 174.134.24.35 2020 5 2020-05-01 09:32:01 2020-05-01 18:32:01
SELECT * 
FROM users
WHERE id = 38 
  AND year = 2020
  AND month = 5

这可能是不将解决方案绑定到单个数据库引擎(我正在考虑 MySQL 和 PostgreSQL)的好方法。考虑到数据库越大,数据库扫表的时间越长,这种方法似乎是不可避免的,而索引可以帮助处理每月生成的数千条记录。

除了这个解决方案,我没有想法。

【问题讨论】:

  • 您使用原始列创建了什么索引?我的猜测是您将日期时间作为第一列,这意味着您必须阅读所有符合日期范围的索引(包括在 id 过滤器上不匹配的索引)
  • 总和是什么?是您问题中的查询还是其他查询有问题?

标签: mysql sql postgresql performance datetime


【解决方案1】:

您的“可能的解决方案”可以很好地配合

INDEX(id, year, month)

以任意顺序排列列。如果您有ID...AUTO_INCREMENTPRIMARY KEY(id),那么更好的解决方案是

PRIMARY KEY(id, year, month)

(它本身会丢失对ID 的唯一性检查;但这可能并不重要。它会加快您的查询速度。)

缺点是查询只能用于测试一个月,而不是一周等。

我很困惑。 id 是什么?该表名为users,但您同时拥有iduser 列。也许表应该被称为logins 并且查询应该有WHERE user=39 AND ...??如果是这样,我将需要重新考虑我的答案。

此外,您说“用户 ID”但显示两列??

【讨论】:

  • 主键用于检查会话。例如,如果有人说“我整天都在登录!”,我们可以检查那个特定的登录。这种情况经常发生,因此在获取14568 行时不会减速。
【解决方案2】:

我假设您的查询最多选择表中 1% 的行。这是索引有效所必需的。

然后,我将您的查询稍微改写为:

SELECT * FROM users
WHERE id = 38 
  AND signed_in between '2020-05-01 00:00:00' AND '2020-05-30 23:59:59'
  AND signed_out < '2020-05-30 23:59:59'

以这种方式改写的查询可以使用索引有效地访问idsigned_in 的行:

create index ix1 on users (id, signed_in, signed_out);

请注意,我还在索引中添加了signed_out,以用于覆盖范围和避免对索引的双重打击。

另外,我假设列 signed_insigned_out 不是 VARCHAR 而是 DATETIME

【讨论】:

  • 我无法绕过这种方法。考虑到使用某种 BTree 的普通索引,我认为按年和月的索引会更好,并且索引整个日期时间。
  • @DarkGhostHunter 按年和月的索引将与此答案中的索引一样有效。他们都将扫描完全相同的行。此解决方案的好处是您的表中不需要冗余(年、月)。
【解决方案3】:

首先,我将查询写成:

SELECT *
FROM users u
WHERE id = 38 AND
      signed_in >= '2020-05-01' AND
      signed_out < '2020-05-31'

虽然我猜你真的想要:

SELECT *
FROM users u
WHERE id = 38 AND
      signed_in >= '2020-05-01' AND
      signed_out < '2020-06-01'

其中包含仅在 5 月份登录的用户。我希望users(id, signed_in)users(id, signed_out) 上的索引就足够了。

【讨论】:

  • 谢谢,但还是没有解决性能问题。
  • @DarkGhostHunter 。 . .你试过索引了吗?
猜你喜欢
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-06
相关资源
最近更新 更多