疑似条件を使用した部分的な複数列のインデックスが(大いに) 役立ちます。パフォーマンスを維持するには、時々再作成する必要があります。(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ジョブを使用します:
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();