35

現在の Postgresql のバージョンでは、JSON コンテンツにさまざまな機能が導入されていますが、本当にそれらを使用する必要があるかどうかは心配です。つまり、何が機能し、何が機能しないかについて確立された「ベスト プラクティス」がまだないということです。見つけられません。

具体的な例があります。オブジェクトに関するテーブルがあり、とりわけ、そのオブジェクトの代替名のリストが含まれています。そのすべてのデータは、取得目的で JSON 列にも含まれます。たとえば(他のすべての無関係なフィールドをスキップします)。

create table stuff (id serial primary key, data json);
insert into stuff(data) values('{"AltNames":["Name1","Name2","Name3"]}')

「altnames の 1 つが 'foobar' であるすべてのオブジェクトを一覧表示する」という形式のクエリが必要になります。予想されるテーブル サイズは、数百万レコードのオーダーです。そのために Postgres JSON クエリを使用でき、インデックスを作成することもできます (たとえば、JSON 配列内の要素を検索するためのインデックス)。ただし、そのようにする必要がありますか、それとも推奨されていないひねくれた回避策ですか?

もちろん、従来の代替方法は、メイン テーブルへの名前と外部キーを含む、1 対多の関係用のテーブルを追加することです。その性能がよくわかります。ただし、それには独自の欠点があります。これは、そのテーブルと JSON の間でデータが重複することを意味するためです (整合性のリスクの可能性があります)。または、その JSON 戻りデータをリクエストごとに動的に作成しますが、これには独自のパフォーマンス ペナルティがあります。

4

2 に答える 2

27

「altnames の 1 つが 'foobar' であるすべてのオブジェクトを一覧表示する」という形式のクエリが必要になります。予想されるテーブル サイズは、数百万レコードのオーダーです。そのために Postgres JSON クエリを使用でき、インデックスを作成することもできます (たとえば、JSON 配列内の要素を検索するためのインデックス)。ただし、そのようにする必要がありますか、それとも推奨されていないひねくれた回避策ですか?

それはそのように行うことができますが、それはそうすべきだという意味ではありません。ある意味で、ベスト プラクティスはすでに十分に文書化されています (たとえば、hstore の使用、XML の使用、EAV の使用、別のテーブルの使用を参照)。すべての意図と実用的な目的 (検証と構文以外) で、違いはありません。以前の構造化されていない、または半構造化されたオプションから。

別の言い方をすれば、それは新しいメイクを施した同じ古い豚です。

JSON は、hstore、配列型、および tsvector と同じ方法で、逆検索ツリー インデックスを使用する機能を提供します。それらは問題なく動作しますが、辞書順で値のリストを抽出するためではなく、主に距離で並べられた近傍 (ジオメトリ タイプを考えてください) 内のポイントを抽出するように設計されていることに注意してください。

説明するために、Roman の回答で概説されている 2 つの計画を取り上げます。

  • インデックス スキャンを実行するものは、ディスク ページを直接調べて、インデックスが示す順序で行を取得します。
  • ビットマップ インデックス スキャンを実行するものは、行を含む可能性のあるすべてのディスク ページを識別することから開始し、ディスクに表示されるときにそれらを読み取ります。これは、無駄な領域をスキップするシーケンス スキャンを実行しているかのように (そして実際にはまったく同じように) 行われます。

質問に戻ります。Postgres テーブルを巨大な JSON ストアとして使用すると、乱雑で大きすぎる逆ツリー インデックスによってアプリのパフォーマンスが実際に向上します。しかし、それらも特効薬ではありません。また、ボトルネックに対処する場合、適切なリレーショナル デザインまで到達することはできません。

最終的には、hstore または EAV を使用することを決定したときに得られる結果と何ら変わりはありません。

  1. インデックスが必要な場合 (つまり、頻繁に where 句や、さらに重要なことに join 句に表示される場合)、別のフィールドにデータが必要になる可能性があります。
  2. それが主に表面的なものであれば、JSON/hstore/EAV/XML/whatever-makes-you-sleep-at-night は問題なく動作します。
于 2013-09-14T11:52:36.490 に答える
20

試してみる価値はあると思います。いくつかのテスト (100000 レコード、JSON 配列の最大 10 要素) を作成し、それがどのように機能するかを確認しました。

create table test1 (id serial primary key, data json);
create table test1_altnames (id int, name text);

create or replace function array_from_json(_j json)
returns text[] as
$func$
    select array_agg(x.elem::text)
    from json_array_elements(_j) as x(elem)
$func$
language sql immutable;

with cte as (
    select
        (random() * 100000)::int as grp, (random() * 1000000)::int as name
    from generate_series(1, 1000000)
), cte2 as (
    select
        array_agg(Name) as "AltNames"
    from cte
    group by grp
)
insert into test1 (data)
select row_to_json(t)
from cte2 as t

insert into test1_altnames (id, name)
select id, json_array_elements(data->'AltNames')::text
from test1

create index ix_test1 on test1 using gin(array_from_json(data->'AltNames'));
create index ix_test1_altnames on test1_altnames (name);

JSON のクエリ (私のマシンでは 30 ミリ秒) :

select * from test1 where '{489147}' <@ array_from_json(data->'AltNames');

"Bitmap Heap Scan on test1  (cost=224.13..1551.41 rows=500 width=36)"
"  Recheck Cond: ('{489147}'::text[] <@ array_from_json((data -> 'AltNames'::text)))"
"  ->  Bitmap Index Scan on ix_test1  (cost=0.00..224.00 rows=500 width=0)"
"        Index Cond: ('{489147}'::text[] <@ array_from_json((data -> 'AltNames'::text)))"

名前付きのクエリ テーブル (私のマシンでは15ms ):

select * from test1 as t where t.id in (select tt.id from test1_altnames as tt where tt.name = '489147');

"Nested Loop  (cost=12.76..20.80 rows=2 width=36)"
"  ->  HashAggregate  (cost=12.46..12.47 rows=1 width=4)"
"        ->  Index Scan using ix_test1_altnames on test1_altnames tt  (cost=0.42..12.46 rows=2 width=4)"
"              Index Cond: (name = '489147'::text)"
"  ->  Index Scan using test1_pkey on test1 t  (cost=0.29..8.31 rows=1 width=36)"
"        Index Cond: (id = tt.id)"

また、名前 ( ) を持つテーブルに行を挿入/削除するにはコストがかかるためtest1_altnames、単に行を選択するよりも少し複雑です。個人的には、JSON を使用したソリューションが好きです。

于 2013-09-14T10:56:58.337 に答える