Postgresql 11には非常にシンプルな3列のテーブルがあります。これは時系列データであり、テーブルには何十億もの行が含まれる可能性があります。テーブルサイズと合計サイズが気になるので、バイト/行を改善するためにデザインを最適化したいと考えています。
このテーマに関して、本当に役立つ質問と回答がいくつか見つかりました
これらのディスカッションで示したクエリの一部を実行したところ、改善の余地があると思いますが、それらを改善するための十分な理解がありません:)
私の作成スクリプトは次のとおりです:
-- table
CREATE TABLE public.vector_events
(
vector_stream_id integer NOT NULL,
event_time timestamp without time zone NOT NULL,
event_data0 real NOT NULL
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
-- index
CREATE INDEX vector_events_stream_id_event_time_index
ON public.vector_events USING btree
(vector_stream_id, event_time DESC)
TABLESPACE pg_default;
私の列幅は最適であると思います-vector_stream_idは100000を超える可能性があり、イベント時間はミリ秒の精度を必要とし、データはフロート内に格納できます。
クエリは常に次の形式になるため、インデックスを選択しました。
SELECT event_time, event_data
FROM vector_events
WHERE stream_id=@streamId
AND event_time >= @lowerBound
-- (optionally with upper bound) AND event_time <= @upperBound
ORDER BY event_time DESC -- (sometimes ASC)
テーブルに少なくとも100万行(おそらく数億)の行がある場合、上記のクエリがパフォーマンスを発揮することが重要です。 TBHバイナリツリーインデックスの選択は、ちょっとした推測でした。
Erwin Brandstetterのクエリを使用してテーブルサイズを確認する場合:(クエリは簡潔にするために省略されていますが、ここで見つかります: Answer to 'Measure the size of a PostgreSQL table row' )次のようになります(これは小さいサンプルテーブルから):
metric bytes/ct|bytes_pretty|bytes_per_row
core_relation_size 9076736 8864 kB 52
visibility_map 8192 8192 bytes 0
free_space_map 24576 24 kB 0
table_size_incl_toast 9109504 8896 kB 52
indexes_size 9256960 9040 kB 53
total_size_incl_toast_and_indexes 18366464 18 MB 106
live_rows_in_text_representation 5685353 5552kB 32
------------------------------
row_count 172800
live_tuples 172800
dead_tuples 0
テーブルの単純なビューでは、int(4バイト)、tzなしのタイムスタンプ(8バイト)、およびfloat(4バイト)なので、16バイトの実際のデータがあると言えます。
それほど単純ではないことを理解していますが、52バイトのテーブルサイズは過剰に見えます。
さらに、インデックスのサイズは53バイトとさらに大きくなります(これは単なるインデックスであり、イベントデータは含まれません)。
だから私は各行の合計サイズが105バイトです-これを改善するために私ができることが何かあるはずです?
この「列テトリス」手法を適用し(幅の広い列を最初に配置)、列の順序をevent_time、stream_id、event_dataに変更することで、数バイト(約8)を節約できるようです。 97バイト?適切に設計されたテーブルとインデックスには、どのサイズが必要ですか?
ノート:
現在、Windowsでpostgresql 11を使用していますが、比較のためにLinuxボックスをプロビジョニングしています。
私の「実際の」データベースはtimescaledbを使用していますが、プレーンなpostgresqlテーブルにtablesize/indexサイズの同じパターンが表示されるため、テーブルサイズが過剰になる原因はpostgresqlスキーマまたはインデックスの設計にあると考えています。 (タイムスケールでは、数十億のイベントがそれぞれ数百万を含むチャンクテーブルに分割されますが、スキーマとインデックスの選択は、ディスクの効率的な使用とパフォーマンスに不可欠です)サーバー構成も改善できると思いますが、最初に、最適なテーブルサイズを取得します。
私の現在の3つの考慮事項は(重要な順に)
数百万を含むテーブルから数万行を取得するときに、パフォーマンスを読み取ります。クエリも集計します。
ディスク使用量。これは、イベントの合計数が数十億に達するため、法外に高額になります。
一部のストリームは他のストリームよりも遅れることがあり、データをバックフィルすることもありますが、通常は任意のストリームについて時系列でパフォーマンスを書き込みます。
このような質問で行う最善の方法は測定です。
CREATE TABLE public.vector_events (
vector_stream_id integer NOT NULL,
event_time timestamp without time zone NOT NULL,
event_data0 real NOT NULL
);
INSERT INTO vector_events
SELECT i,
current_timestamp + i * INTERVAL '1 second',
3.1415
FROM generate_series(1, 200000) AS i;
SELECT pg_relation_size('public.vector_events');
pg_total_relation_size
------------------------
10461184
(1 row)
test=> SELECT 10461184 / 200000.0;
?column?
---------------------
52.3059200000000000
(1 row)
したがって、1行あたり52バイトはかなり適切です。
インデックスについて:
CREATE INDEX vector_events_stream_id_event_time_index
ON public.vector_events (vector_stream_id, event_time DESC);
SELECT pg_total_relation_size('vector_events_stream_id_event_time_index');
pg_total_relation_size
------------------------
6324224
(1 row)
test=> SELECT 6324224 / 200000.0;
?column?
---------------------
31.6211200000000000
(1 row)
それは私にはかなり普通のようです。
ワークロードにDELETE
sとUPDATE
sがある場合、特定の内部断片化(膨張)が発生するため、最終的にはデータがより多くのスペースを占めることが予想されます。特に、インデックスは2倍または3倍大きくなる可能性があります。
あなたの質問に答えるには:
インデックスはクエリに最適であり、ASC
とDESC
のどちらを宣言してもかまいません。したがって、アクセス速度は最適である必要があります。
あなたが言ったように、event_time
を最初または最後の行として。それが可能なことの限界です。
書き込みパフォーマンスを向上させるには、高速ディスクを使用し、max_wal_size
高い。
テーブルの主キーインデックスが必要です。最も安価な方法は、そのためにインデックスを使用することです(それをUNIQUE
に設定できる場合)。ただし、DESC
を削除する必要があります。