通过在 postgresql 上索引时间戳列来加快搜索速度?

Faster search by indexing timestamp column on postgresql?

我有一个 PostgreSQL 数据库和一个由事件组成的 table。这些事件有列 end_time,它有一个时间戳类型(没有时区信息)。在我的应用程序中,我经常查询 table,试图 select 将来发生的所有事件。所以基本上我正在做这种 SQL 查询:

SELECT * FROM events WHERE end_time >= ?::timestamp

我目前在 end_time 列上没有索引。我担心一旦我的 table 行大小变大(实际上它已经做了很多),对未来事件的搜索查询会变慢?因为现在数据库搜索必须遍历所有行以选择将来发生(或更准确地说,结束)的行。我以前用过索引,但不能说我是最熟悉的。我想知道通过为它创建默认的 Postgres 索引来索引 end_time 列是否会提高查询的性能?我还没有真正的问题,但我不想等到数据量增加后才出现。因为那时有点晚了,至少终端应用的用户体验下降了。

我想指出,我确实使用了不带时区的时间戳,因为我的应用程序始终采用当地时间,我不需要时区信息。但我听说它可能对索引有影响?此外,我的时间戳目前不受任何限制。所以他们理论上可以从现在到无限的未来。我想知道设置一些约束是否可以使索引更好?活动时间之类的应该在15年之内吧?

另一种选择是,我会将事件移动到另一个 table 过去的事件 (archived_events)。这样 table 事件的大小就不会变得太大。例如,我可以有一个定期执行的 cron 作业。

我还听说 运行ning analyze/explain 到数据库实际上可以提高它的性能?如果是这种情况,我应该多久 运行 一次?

PostgreSQL 版本:12.3

I wonder if indexing the end_time column [...] would increase the performance of the query?

如果 Postgres 预计只有百分之几或更少符合条件(将来有 end_time),它将在“索引扫描”或“位图索引扫描”中使用列上的索引。

如果这个估计不太远,它实际上也会提高性能。这就是为什么您应该默认启用 autovacuum 的原因:使列统计信息保持最新。

如果您实际上不需要查询中的所有列 (SELECT *)(您通常不需要),则仅列出您实际需要的列以使其更快。甚至可能允许“仅索引扫描”。参见:

I wonder if setting some constraints could make the indexing better? Something like the event time should be within 15 year or something?

否。对您的查询没有任何影响。以后的行数是决定因素

I would move events to another table that are in the past (archived_events) ...?

Btree 索引扩展性极佳。这意味着,只要只有几行符合条件,被淘汰的行数就无关紧要。如果您的 table 是 巨大的 (数百万或数十亿行)并且其中大部分在过去,partial index可能会更好,主要是由于索引大小和索引维护成本的减少。

特殊困难:“现在”是一个动态值。索引定义需要 immutable 值。解决方法是选择一个任意的“现在”来切断大部分行。类似于:

CREATE INDEX ON events(end_time) WHERE end_time > '2021-01-30';

现代 Postgres 足够聪明,可以理解它可以为未来的日期使用索引。 旧版本可能需要一个冗余的WHERE子句来理解部分索引是适用的:

SELECT * FROM events
WHERE  end_time >= ?::timestamp
AND    end_time > '2021-01-30';  -- match index

索引的有用性会随着时间的推移而降低,这也取决于行流失。您可能会不时重新创建索引以切断更多行。


另外,不要让类型名称 timestamp with time zone 误导您。它不存储时区信息。它通常是最佳选择。参见:

  • Ignoring time zones altogether in Rails and PostgreSQL