3

私は大きなテーブルを持っています:

CREATE TABLE "orders" (
"id" serial NOT NULL,
"person_id" int4,
"created" int4,
CONSTRAINT "orders_pkey" PRIMARY KEY ("id")
);

すべてのリクエストの 90% は、次person_idのような による過去 2 ~ 3 日間の注文に関するものです。

select * from orders
where person_id = 1
and created >= extract(epoch from current_timestamp)::int - 60 * 60 * 24 * 3;

どうすればパフォーマンスを向上させることができますか?

Partitioningについては知っていますが、既存の行はどうですか? INHERITSまた、 2 ~ 3 日ごとにテーブルを手動で作成する必要があるようです。

4

2 に答える 2

3

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

提案:-

それはあなたを助けるかもしれません。テーブルのサイズが大きくなっているため、クエリのパフォーマンスは徐々に低下します。3 ~ 5 日 ( 2 ~ 3 日だけアクセスすることが確実な場合) のレコードを維持し、定期的に古いレコードをバックアップ テーブルに移行します。

于 2013-12-23T12:12:47.503 に答える