带有伪条件的部分多列索引会有所帮助(很多)。需要不时重新创建以保持性能。(person_id, created)
IMMUTABLE
请注意,如果您的表不是很大,您可以在很大程度上简化并使用普通的多列索引。
或者考虑Postgres 12 或更高版本(该功能最终成熟)中的表分区。
原始函数提供了一个恒定的时间点,3 天或更长时间(在您的情况下由 unix 纪元表示):
CREATE OR REPLACE FUNCTION f_orders_idx_start()
RETURNS int LANGUAGE sql IMMUTABLE PARALLEL SAFE COST 1 AS
'SELECT 1387497600';
PARALLEL SAFE
仅适用于 Postgres 10 或更高版本。
1387497600
是以下结果:
SELECT extract(epoch from now())::integer - 259200;
-- 259200 being the result of 60 * 60 * 24 * 3
基于此伪条件的部分索引:IMMUTABLE
CREATE INDEX orders_created_recent_idx ON orders (person_id, created)
WHERE created >= f_orders_idx_start();
将您的查询基于相同的条件:
SELECT *
FROM orders
WHERE person_id = 1
AND created >= f_orders_idx_start() -- match partial idx condition
AND created >= extract(epoch from now())::integer - 259200; -- actual condition
该行AND created >= f_orders_idx_start()
似乎是多余的,但有助于说服 Postgres 使用部分索引。
不时重新创建函数和索引的函数。可能每晚都有一个 cron-job:
CREATE OR REPLACE FUNCTION f_orders_reindex_partial()
RETURNS void AS
$func$
DECLARE
-- 3 days back, starting at 00:00
_start int := extract(epoch from now()::date -3)::int;
BEGIN
IF _start = f_orders_idx_start() THEN
-- do nothing, nothing changes.
ELSE
DROP INDEX IF EXISTS orders_created_recent_idx;
-- Recreate IMMUTABLE function
EXECUTE format('
CREATE OR REPLACE FUNCTION f_orders_idx_start()
RETURNS int LANGUAGE sql IMMUTABLE PARALLEL SAFE COST 1 AS
$$SELECT %s $$'
, _start
);
-- Recreate partial index
CREATE INDEX orders_created_recent_idx ON orders (person_id, created)
WHERE created >= f_orders_idx_start();
END IF;
END
$func$ LANGUAGE plpgsql;
然后,要重新设置索引,调用(理想情况下很少或没有并发负载):
SELECT f_orders_reindex_partial(); -- that's all
如果由于并发负载而无法删除和重新创建索引,请考虑REINDEX CONCURRENTLY
使用 Postgres 12 或更高版本。这很简单:
REINDEX INDEX orders_created_recent_idx;
即使您从未调用此函数,所有查询都会继续工作。随着部分索引的增长,性能会随着时间的推移而缓慢下降。
我成功地使用了这个制度,有几个大桌子和类似的要求。非常快。
对于 Postgres 9.2 或更高版本,如果您的表只有很少的小列,并且如果表没有大量写入,则将其设为覆盖索引可能是值得的:
CREATE INDEX orders_created_recent_idx ON orders (person_id, created, id)
WHERE created >= f_orders_idx_start();
在 Postgres 11 或更高版本中,您可能希望INCLUDE
改用:
CREATE INDEX orders_created_recent_idx ON orders (person_id, created) INCLUDE (id)
WHERE created >= f_orders_idx_start();