为什么即使行数非常不同,查询也需要相同的时间来获取数据?
Why do the queries take the same time to get data even though the number of rows is very different?
我在 VISITS table 中有 29,938,766 行,table 看起来像这样
USER_ID (INT)
VISITED_IN (DATETIME)
65
2020-08-26 07:57:43
1182
2019-03-15 02:46:48
1564
2015-07-04 10:59:44
73
2021-03-18 00:25:08
3791
2017-10-17 12:22:45
51
2022-05-02 19:11:09
917
2017-11-20 15:32:06
3
2019-12-29 15:15:51
51
2015-02-08 17:48:30
1531
2020-08-05 08:44:55
Etc...
Etc...
当运行这个查询时,耗时17-20秒,returns63514(该用户有63514次访问)
SELECT COUNT(*) FROM VISITS WHERE USER_ID = 917
当运行这个查询时,需要17-20秒,returns193(该用户有193次访问)
SELECT COUNT(*) FROM VISITS WHERE USER_ID = 716
问题是即使用户只有 3、50、70 或 1,000,000 次访问,查询也总是需要 17-20 秒来查询 29,938,766 行。
我认为问题是因为它正在循环所有行?
第二个查询必须比第一个查询快。这取决于行数。但是两个查询花费的时间相同!
你有什么建议可以避免这个问题?
Table结构
更新:这是一个新的建议方案:
当用户输入他或其他人的个人资料时,他可以看到个人资料访问的次数,并且他可以使用这种方式过滤访问
Last 24 hours
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 1 DAY);
Last 7 days
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 7 DAY);
Last 30 days
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 30 DAY);
All time
|
---> SELECT VISITS FROM USERS WHERE USER_ID = 5;
此外,我将创建一个每天执行此命令的循环事件。
DELETE FROM VISITS WHERE VISITED_IN <= DATE_SUB(NOW(), INTERVAL 30 DAY);
此外,在 VISITS table 中添加新行时,我会确保增加 VISITS 列。
UPDATE USERS SET VISITS = VISITS + 1 WHERE ID = 5
INDEX(user_id, visited_in)
将加快您提到的所有 SELECTs
。他们将不得不扫描一大块索引;他们将不必“扫描整个 table”。
DELETE
需要 `INDEX(visited_in)。但是,如果您 运行 不够频繁,就会有问题。这是因为一次删除数千行可能是一个问题。考虑 运行ning 至少每小时删除一次。
如果 table 真的很大,等等,考虑使用“时间序列”的分区。有了 DROP PARTITION
, 快了很多。 Partition
任何缓存服务都会提供陈旧的计数,但有时会更快。
“每次有人打开页面时都访问数据库”是可以的,但前提是查询足够高效。做索引。
在我对您的其他问题的 中,我解释了摘要 table 如何进一步加快处理速度。但是,它假设“最后 N 天”是从午夜到午夜测量的。您当前的查询是 NOW() - INTERVAL N DAY
。这比午夜更难实施。你愿意改变“最后N天”的意思吗?
(一些 INDEX 基础知识...)
任何索引的一个重要原因是它能够根据某些列快速查找行。
INDEX
是映射到行的键列表。
- A
UNIQUE INDEX
是一个 INDEX
,加上一个唯一性约束——这意味着没有两行在索引中具有相同的值。
- 唯一的
PRIMARY KEY
是唯一索引,指定用于唯一标识 table 中的每一行。
“键”和“索引”是同义词。
索引(在 MySQL 的 InnoDB 引擎中)被实现为 BTree(实际上是 B+Tree;参见维基百科)。在 PK 的情况下,其余列与 PK 值一起坐在那里。在“辅助”键的情况下,BTree 的 'value' 部分是 PK 列。
任何索引都可以包含 1 列或多列(称为“复合”)
INDEX(lastname)
不太可能是唯一的
INDEX(lastname, firstname)
仍然不太可能是唯一的,但它是“复合的”。
我在 VISITS table 中有 29,938,766 行,table 看起来像这样
USER_ID (INT) | VISITED_IN (DATETIME) |
---|---|
65 | 2020-08-26 07:57:43 |
1182 | 2019-03-15 02:46:48 |
1564 | 2015-07-04 10:59:44 |
73 | 2021-03-18 00:25:08 |
3791 | 2017-10-17 12:22:45 |
51 | 2022-05-02 19:11:09 |
917 | 2017-11-20 15:32:06 |
3 | 2019-12-29 15:15:51 |
51 | 2015-02-08 17:48:30 |
1531 | 2020-08-05 08:44:55 |
Etc... | Etc... |
当运行这个查询时,耗时17-20秒,returns63514(该用户有63514次访问)
SELECT COUNT(*) FROM VISITS WHERE USER_ID = 917
当运行这个查询时,需要17-20秒,returns193(该用户有193次访问)
SELECT COUNT(*) FROM VISITS WHERE USER_ID = 716
问题是即使用户只有 3、50、70 或 1,000,000 次访问,查询也总是需要 17-20 秒来查询 29,938,766 行。
我认为问题是因为它正在循环所有行?
第二个查询必须比第一个查询快。这取决于行数。但是两个查询花费的时间相同!
你有什么建议可以避免这个问题?
Table结构
更新:这是一个新的建议方案:
当用户输入他或其他人的个人资料时,他可以看到个人资料访问的次数,并且他可以使用这种方式过滤访问
Last 24 hours
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 1 DAY);
Last 7 days
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 7 DAY);
Last 30 days
|
---> SELECT COUNT(*) FROM VISITS WHERE USER_ID = 5 AND VISITED_IN >= DATE_SUB(NOW(), INTERVAL 30 DAY);
All time
|
---> SELECT VISITS FROM USERS WHERE USER_ID = 5;
此外,我将创建一个每天执行此命令的循环事件。
DELETE FROM VISITS WHERE VISITED_IN <= DATE_SUB(NOW(), INTERVAL 30 DAY);
此外,在 VISITS table 中添加新行时,我会确保增加 VISITS 列。
UPDATE USERS SET VISITS = VISITS + 1 WHERE ID = 5
INDEX(user_id, visited_in)
将加快您提到的所有 SELECTs
。他们将不得不扫描一大块索引;他们将不必“扫描整个 table”。
DELETE
需要 `INDEX(visited_in)。但是,如果您 运行 不够频繁,就会有问题。这是因为一次删除数千行可能是一个问题。考虑 运行ning 至少每小时删除一次。
如果 table 真的很大,等等,考虑使用“时间序列”的分区。有了 DROP PARTITION
, 快了很多。 Partition
任何缓存服务都会提供陈旧的计数,但有时会更快。
“每次有人打开页面时都访问数据库”是可以的,但前提是查询足够高效。做索引。
在我对您的其他问题的 NOW() - INTERVAL N DAY
。这比午夜更难实施。你愿意改变“最后N天”的意思吗?
(一些 INDEX 基础知识...)
任何索引的一个重要原因是它能够根据某些列快速查找行。
INDEX
是映射到行的键列表。- A
UNIQUE INDEX
是一个INDEX
,加上一个唯一性约束——这意味着没有两行在索引中具有相同的值。 - 唯一的
PRIMARY KEY
是唯一索引,指定用于唯一标识 table 中的每一行。
“键”和“索引”是同义词。
索引(在 MySQL 的 InnoDB 引擎中)被实现为 BTree(实际上是 B+Tree;参见维基百科)。在 PK 的情况下,其余列与 PK 值一起坐在那里。在“辅助”键的情况下,BTree 的 'value' 部分是 PK 列。
任何索引都可以包含 1 列或多列(称为“复合”)
INDEX(lastname)
不太可能是唯一的
INDEX(lastname, firstname)
仍然不太可能是唯一的,但它是“复合的”。