我有一张大桌子:
CREATE TABLE "orders" (
"id" serial NOT NULL,
"person_id" int4,
"created" int4,
CONSTRAINT "orders_pkey" PRIMARY KEY ("id")
);
所有请求中的90%是关于[2-3]的最近2-3天的订单,例如:
person_id
我如何提高性能?
我知道select * from orders
where person_id = 1
and created >= extract(epoch from current_timestamp)::int - 60 * 60 * 24 * 3;
,但是现有行呢?看来我需要每2-3天手动创建Partitioning表。
具有伪INHERITS
条件的partial, multicolumn index上的(person_id, created)
将有帮助(很多)。需要不时地重新创建以保持性能。
注意,如果表不是很大,则可以在很大程度上简化并使用普通的多列索引。或考虑使用Postgres 12或更高版本中的IMMUTABLE
(功能最终成熟)。
原始功能提供了一个固定的时间点,即3天或更多天后的时间(在您的情况下以unix纪元表示):
table partitioning
CREATE OR REPLACE FUNCTION f_orders_idx_start()
RETURNS integer LANGUAGE sql IMMUTABLE COST 1 PARALLEL SAFE AS
'SELECT 1387497600';
仅对于Postgres 10或更高版本。PARALLEL SAFE
是以下结果:
1387497600
根据您的部分索引在此伪SELECT extract(epoch from now())::integer - 259200;
-- 259200 being the result of 60 * 60 * 24 * 3
条件下:
IMMUTABLE
基于相同条件的query:
CREATE INDEX orders_created_recent_idx ON orders (person_id, created)
WHERE created >= f_orders_idx_start();
[C0行似乎是多余的,但有助于说服Postgres使用部分索引。
A 重新创建函数和索引的函数。可能每天晚上都有cron-job:
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 9.2或更高版本,并且如果您的表只有很少的一小列,并且该表没有被大量写入,则可能需要将其设为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 '
CREATE OR REPLACE FUNCTION f_orders_idx_start()
RETURNS integer AS
$$SELECT ' || _start || '$$
LANGUAGE sql IMMUTABLE COST 1';
-- 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
在Postgres 11或更高版本中,您可能想改用covering index:
CREATE INDEX orders_created_recent_idx ON orders (person_id, created, id)
WHERE created >= f_orders_idx_start();
建议:-
可能会帮助您。由于表的大小在增加,因此查询性能将逐渐降低。最好保留3-5天的记录(如果您非常确定仅要访问2-3天,请确保>),并定期将旧记录迁移到备份表。