データに関連するアカウントを示すreceiver
を格納するデータベースがあります。 1セットのデータがreceiver
列を除いてすべての列データが同じである3つの個別の行を作成する可能性があるため、これはデータの大量の複製につながりました。データベースの再設計中に、レシーバーの現在のBツリーインデックスではなく、GINインデックスを持つ配列を使用することを検討しました。
現在のテーブル定義:
CREATE TABLE public.actions (
global_sequence bigint NOT NULL DEFAULT nextval('actions_global_sequence_seq'::regclass),
time timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP,
receiver text NOT NULL,
tx_id text NOT NULL,
block_num integer NOT NULL,
contract text NOT NULL,
action text NOT NULL,
data jsonb NOT NULL
);
インデックス:
フィールドの詳細:
つのスキーマオプションのカーディナリティ:
一般的なクエリ:
SELECT * FROM actions WHERE receiver = 'Alpha' ORDER BY time DESC LIMIT 100
クエリにはすべての列が必要です。 NULL値は表示されません。正規化されたスキーマでの結合は遅くなり、クエリの速度が最優先です)
最適なDB設計は常に全体像に依存します。
一般に、単純なクエリでは、プレーンなbtreeインデックスよりも高速なものはほとんどありません。 json
またはjsonb
を導入するか、またはGINインデックスと組み合わせてプレーン配列タイプを導入しても、おそらく遅くなります。
元のテーブルでこれマルチカラムインデックス正しいソート順で、一般的なクエリのgame changerである必要があります。
CREATE INDEX game_changer ON actions (receiver, time DESC);
このように、Postgresはインデックスから直接上位100行を選択できます。超早い。
関連:
現在のインデックスreceiver_idx
およびactions_time_idx
は目的を失う可能性があります。
完全なインデックスの隣に、ストレージサイズは大きなテーブルの重要な要素であるため、重複を避けることは正しい考えかもしれません。しかし、それはさまざまな方法で実現できます。古き良き正規化はもう考えましたか?
CREATE TABLE receiver (
receiver_id serial PRIMARY KEY
, receiver text NOT NULL -- UNIQUE?
);
CREATE TABLE action ( -- I shortened the name to "action"
action_id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
-- global_sequence bigint NOT NULL DEFAULT nextval('actions_global_sequence_seq'::regclass), -- ??
time timestamptz NOT NULL DEFAULT now(),
block_num int NOT NULL,
tx_id text NOT NULL,
contract text NOT NULL,
action text NOT NULL,
data jsonb NOT NULL
)
CREATE TABLE receiver_action (
receiver_id int REFERENCES receiver
, action_id bigint REFERENCES action
, PRIMARY KEY (receiver_id, action_id)
);
また、テーブルaction
の列の順序が変更されているため、行ごとに数バイトが節約され、数十億行の場合は数GBになります。
見る:
あなたの共通のクエリは少し変更されます:
SELECT a.*
FROM receiver_action ra
JOIN action a USING (action_id)
WHERE ra. receiver_id = (SELECT receiver_id FROM receiver WHERE receiver = 'Alpha')
ORDER BY a.time DESC
LIMIT 100;
欠点:現在、一般的なクエリを高速にすることははるかに困難です。関連:
迅速な(そして少し汚れた)修正:テーブルreceiver_action
にtime
列を冗長に含める(またはそこに移動する) 。
CREATE TABLE receiver_action (
receiver_id int REFERENCES receiver
, action_id bigint REFERENCES action
, time timestamptz NOT NULL DEFAULT now() -- !
, PRIMARY KEY (receiver_id, action_id)
);
インデックスを作成します。
CREATE INDEX game_changer ON receiver_action (receiver_id, time DESC) INCLUDE (action_id);
INCLUDE
にはPostgres 11以降が必要です。見る:
そして、このクエリを使用します:
SELECT a.*
FROM (
SELECT action_id
FROM receiver_action
WHERE receiver_id = (SELECT receiver_id FROM receiver WHERE receiver = 'Alpha')
ORDER BY time DESC
LIMIT 100
)
JOIN action a USING (action_id);
one set of data may create 3 separate rows
の背後にある正確なストーリーによっては、n:m実装と式GINインデックスの代わりに、テーブルアクションの3つの個別の列でさえ、さらに多くのことが可能になる場合があります...しかし、それは深すぎます。ここをタップします。