単一のテーブルからデータを取得する単純なクエリを実行しています。テーブルにある非 JSON フィールドのみを検索すると、クエリに 16 ミリ秒かかります。JSONB データのフィールドを参照するフィールドを含めると、62 倍になります。2 つの異なる JSONB フィールドを検索すると、それが 2 倍になります。
--EXPLAIN (ANALYZE,buffers)
SELECT
segment as segment_no,
begin_time,
segment_data::json->'summary'->'begin_milage' as begin_milage,
segment_data::json->'summary'->'end_milage' as end_milage
FROM
segments_table
WHERE
vehicle=12 AND trip=3
ORDER BY
begin_time;
クエリは、SELECT 句に含まれる 2 つの JSON フィールドで 2.0 秒かかります。1 つを省略した場合は 1.0 秒かかり、両方の JSON フィールドを省略した場合、クエリは 16 ミリ秒しかかかりません。
テーブル自体には約 700 のレコードがあります。クエリは 83 レコードを返します。さまざまなクエリを実行すると、 2 つの JSON フィールドをクエリする場合、返されるレコードが多いほど、クエリが完了するまでに時間がかかる (約 0.0066 * X 1.32ミリ秒) ことに気付きました。
車両とトリップのルックアップにインデックスを追加してみましたが、(予想どおり) 大きな違いはありませんでした。データの実際の取得であり、JSONB フィールドでデータを見つけるのに時間がかかるようです。ここで、JSON フィールドが WHERE 句で必要だった場合、この種の劣化がより理解しやすくなりますが、そうではありません。
もちろん、単純な解決策は、JSON blob から各フィールドを取り出して、テーブル自体に個別のフィールドを作成することです。しかし、そのルートに行く前に、このパフォーマンスの問題を解決できるものは他にありますか?
ANALYZE の結果は次のとおりです。
Sort (cost=13.25..13.27 rows=10 width=28) (actual time=1999.899..1999.901 rows=71 loops=1)
Sort Key: begin_time
Sort Method: quicksort Memory: 35kB
Buffers: shared hit=5663
-> Bitmap Heap Scan on segments_table (cost=4.38..13.08 rows=10 width=28) (actual time=1.332..1999.730 rows=71 loops=1)
Recheck Cond: ((vehicle = 644) AND (trip = 3))
Heap Blocks: exact=3
Buffers: shared hit=5663
-> Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq (cost=0.00..4.38 rows=10 width=0) (actual time=0.052..0.052 rows=71 loops=1)
Index Cond: ((vehicle = 644) AND (trip = 3))
Buffers: shared hit=2
Planning time: 0.368 ms
Execution time: 2000.000 ms
もう 1 つの興味深い観察結果は、同じクエリを複数回実行しても、後続の同一のクエリで期待されるキャッシュの改善が見られないことです。
ストックpostgresサーバー構成に加えた唯一の変更は、shared_buffers
を128MBから256MBに増やし、effective_cache_size = 1GB
. max_connections
また、を 100 から 20 に減らしました。
上記の結果は、8 コア i7 プロセッサの Win7 で実行されています。また、デュアル コア CPU の Ubuntu で同じテストを実行したところ、クエリにかかった時間はほぼ同じでした: 2.2 秒 (SELECT 句に 2 つの JSONB フィールドを含めた場合)。
更新:
SELECT 句の単一の JSON フィールド:
EXPLAIN (ANALYZE,buffers)
SELECT
segment as segment_no,
begin_time,
segment_data::json->'summary'->'end_mileage' as end_mileage
FROM
segments_table
WHERE
vehicle=644 AND trip=3
ORDER BY
begin_time;
結果:
Sort (cost=13.15..13.17 rows=10 width=28) (actual time=999.695..999.696 rows=71 loops=1)
Sort Key: begin_time
Sort Method: quicksort Memory: 26kB
Buffers: shared hit=2834
-> Bitmap Heap Scan on segments_table (cost=4.38..12.98 rows=10 width=28) (actual time=0.781..999.554 rows=71 loops=1)
Recheck Cond: ((vehicle = 644) AND (trip = 3))
Heap Blocks: exact=3
Buffers: shared hit=2834
-> Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq (cost=0.00..4.38 rows=10 width=0) (actual time=0.052..0.052 rows=71 loops=1)
Index Cond: ((vehicle = 644) AND (trip = 3))
Buffers: shared hit=2
Planning time: 0.353 ms
Execution time: 999.777 ms
SELECT 句に JSON フィールドがありません:
EXPLAIN (ANALYZE,buffers)
SELECT
segment as segment_no,
begin_time
FROM
segments_table
WHERE
vehicle=644 AND trip=3
ORDER BY
begin_time;
結果:
Sort (cost=13.05..13.07 rows=10 width=10) (actual time=0.194..0.205 rows=71 loops=1)
Sort Key: begin_time
Sort Method: quicksort Memory: 19kB
Buffers: shared hit=5
-> Bitmap Heap Scan on segments_table (cost=4.38..12.88 rows=10 width=10) (actual time=0.088..0.122 rows=71 loops=1)
Recheck Cond: ((vehicle = 644) AND (trip = 3))
Heap Blocks: exact=3
Buffers: shared hit=5
-> Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq (cost=0.00..4.38 rows=10 width=0) (actual time=0.048..0.048 rows=71 loops=1)
Index Cond: ((vehicle = 644) AND (trip = 3))
Buffers: shared hit=2
Planning time: 0.590 ms
Execution time: 0.280 ms
テーブル定義:
CREATE TABLE public.segments_table
(
segment_id integer NOT NULL DEFAULT nextval('segments_table_segment_id_seq'::regclass),
vehicle smallint NOT NULL,
trip smallint NOT NULL,
segment smallint NOT NULL,
begin_time timestamp without time zone NOT NULL,
segment_data jsonb,
CONSTRAINT segments_table_pkey PRIMARY KEY (segment_id),
CONSTRAINT segments_table_vehicle_64df5bc5_uniq UNIQUE (vehicle, trip, segment, begin_time)
)
WITH (
OIDS=FALSE
);
CREATE INDEX segments
ON public.segments_table
USING btree
(segment);
CREATE INDEX vehicles
ON public.segments_table
USING btree
(vehicle);
CREATE INDEX trips
ON public.segments_table
USING btree
(trip);
更新 #2:
@Mark_M が指摘したようにキャストの問題を修正し、json
jsonb` に変更すると、クエリ時間が 2 秒から 300 ミリ秒に短縮されます。
EXPLAIN (ANALYZE,buffers)
SELECT
segment as segment_no,
begin_time,
segment_data::jsonb->'summary'->'begin_mileage' as begin_mileage,
segment_data::jsonb->'summary'->'end_mileage' as end_mileage
FROM
segments_table
WHERE
vehicle=644 AND trip=3
ORDER BY
begin_time;
Sort (cost=13.15..13.17 rows=10 width=28) (actual time=296.339..296.342 rows=71 loops=1)
Sort Key: begin_time
Sort Method: quicksort Memory: 35kB
Buffers: shared hit=5663
-> Bitmap Heap Scan on segments_table (cost=4.38..12.98 rows=10 width=28) (actual time=0.275..296.229 rows=71 loops=1)
Recheck Cond: ((vehicle = 644) AND (trip = 3))
Heap Blocks: exact=3
Buffers: shared hit=5663
-> Bitmap Index Scan on segments_table_vehicle_64df5bc5_uniq (cost=0.00..4.38 rows=10 width=0) (actual time=0.045..0.045 rows=71 loops=1)
Index Cond: ((vehicle = 644) AND (trip = 3))
Buffers: shared hit=2
Planning time: 0.352 ms
Execution time: 296.473 ms
これはかなり改善されましたが、まだ JSON 以外のフィールドを使用して検索するだけで 18 倍になりましたが、これははるかに優れています。JSONB フィールドを使用する場合、これは妥当なパフォーマンス ヒットですか?