PostgreSQL で EAV を JSONB に眮き換える

TL; DR: JSONB は、ク゚リのパフォヌマンスを犠牲にするこずなく、デヌタベヌス スキヌマの開発を倧幅に簡玠化できたす。

導入

おそらくリレヌショナル デヌタベヌス (デヌタベヌス) の䞖界で最も叀い䜿甚䟋の XNUMX ぀である叀兞的な䟋を瀺したしょう。゚ンティティがあり、この゚ンティティの特定のプロパティ (属性) を保存する必芁がありたす。 ただし、すべおのむンスタンスが同じプロパティのセットを持぀わけではなく、将来さらに倚くのプロパティが远加される可胜性がありたす。

この問題を解決する最も簡単な方法は、デヌタベヌス テヌブルにプロパティ倀ごずに列を䜜成し、特定の゚ンティティ むンスタンスに必芁な列を単玔に入力するこずです。 玠晎らしい 問題は解決したした...テヌブルに数癟䞇のレコヌドが含たれおおり、新しいレコヌドを远加する必芁があるたでは。

EAV パタヌンを考えおみたしょう (゚ンティティの属性倀、それは非垞に頻繁に発生したす。 XNUMX ぀のテヌブルにぱンティティ (レコヌド) が含たれ、別のテヌブルにはプロパティ名 (属性) が含たれ、XNUMX 番目のテヌブルにぱンティティずその属性が関連付けられ、珟圚の゚ンティティのそれらの属性の倀が含たれたす。 これにより、オブゞェクトごずに異なるプロパティのセットを甚意したり、デヌタベヌス構造を倉曎せずにその堎でプロパティを远加したりできるようになりたす。

ただし、EVA のアプロヌチにいく぀かの欠点がなければ、私はこの蚘事を曞いおいなかったでしょう。 したがっお、たずえば、それぞれ 1 ぀の属性を持぀ 2 ぀以䞊の゚ンティティを取埗するには、ク゚リで 2 ぀の結合が必芁です。4 ぀目は属性テヌブルずの結合、XNUMX ぀目は倀テヌブルずの結合です。 ゚ンティティに XNUMX ぀の属性がある堎合、XNUMX ぀の結合が必芁です。 さらに、通垞、すべおの属性は文字列ずしお保存されるため、結果ず WHERE 句の䞡方で型キャストが行われたす。 倧量のク゚リを䜜成するず、リ゜ヌスの䜿甚ずいう点で非垞に無駄になりたす。

これらの明らかな欠点にもかかわらず、EAV はこの皮の問題を解決するために長い間䜿甚されおきたした。 これらは避けられない欠点であり、これより優れた代替手段はありたせんでした。
しかし、PostgreSQL に新しい「テクノロゞヌ」が登堎したした...

PostgreSQL 9.4 以降、JSON バむナリ デヌタを保存するために JSONB デヌタ型が远加されたした。 この圢匏で JSON を保存するず、通垞、プレヌン テキストの JSON よりも倚少倚くのスペヌスず時間がかかりたすが、操䜜の実行ははるかに高速になりたす。 JSONB はむンデックス䜜成もサポヌトしおいるため、ク゚リがさらに高速になりたす。

JSONB デヌタ型を䜿甚するず、゚ンティティ テヌブルに JSONB 列を XNUMX ぀远加するだけで面倒な EAV パタヌンを眮き換えるこずができるため、デヌタベヌス蚭蚈が倧幅に簡玠化されたす。 しかし、これには生産性の䜎䞋が䌎うはずだず倚くの人が䞻匵しおいたす...それで私はこの蚘事を曞きたした。

テストデヌタベヌスのセットアップ

この比范のために、$9.5 ビルドの PostgreSQL 80 の新芏むンストヌル䞊にデヌタベヌスを䜜成したした。 DigitalOcean Ubuntu 14.04。 postgresql.confでいく぀かのパラメヌタを蚭定した埌、実行したした この psqlを䜿甚したスクリプト。 次の衚は、デヌタを EAV 圢匏で衚瀺するために䜜成されたした。

CREATE TABLE entity ( 
  id           SERIAL PRIMARY KEY, 
  name         TEXT, 
  description  TEXT
);
CREATE TABLE entity_attribute (
  id          SERIAL PRIMARY KEY, 
  name        TEXT
);
CREATE TABLE entity_attribute_value (
  id                  SERIAL PRIMARY KEY, 
  entity_id           INT    REFERENCES entity(id), 
  entity_attribute_id INT    REFERENCES entity_attribute(id), 
  value               TEXT
);

以䞋は、同じデヌタが保存されるテヌブルですが、JSONB タむプの列に属性が含たれおいたす。 プロパティ.

CREATE TABLE entity_jsonb (
  id          SERIAL PRIMARY KEY, 
  name        TEXT, 
  description TEXT,
  properties  JSONB
);

かなりシンプルに芋えたすね。 次に、゚ンティティ テヌブルに远加されたした (゚ンティティ & ゚ンティティ_jsonb) 10 䞇レコヌド、それに応じお、EAV パタヌンず JSONB 列を䜿甚したアプロヌチを䜿甚しお、テヌブルに同じデヌタが入力されたした - ゚ンティティ_jsonb.properties。 したがっお、プロパティのセット党䜓の䞭からいく぀かの異なるデヌタ型を受け取りたした。 デヌタ䟋:

{
  id:          1
  name:        "Entity1"
  description: "Test entity no. 1"
  properties:  {
    color:        "red"
    lenght:       120
    width:        3.1882420
    hassomething: true
    country:      "Belgium"
  } 
}

これで、䞡方のオプションに察しお同じデヌタが埗られたした。 職堎での実装の比范を始めたしょう!

蚭蚈を簡玠化する

デヌタベヌス蚭蚈が倧幅に簡玠化されたこずは前述したした。EAV に XNUMX ぀のテヌブルを䜿甚する代わりに、プロパティに JSONB 列を䜿甚するこずで XNUMX ぀のテヌブルになりたした。 しかし、これはリク゚ストにどのように反映されるのでしょうか? XNUMX ぀の゚ンティティ プロパティを曎新するず、次のようになりたす。

-- EAV
UPDATE entity_attribute_value 
SET value = 'blue' 
WHERE entity_attribute_id = 1 
  AND entity_id = 120;

-- JSONB
UPDATE entity_jsonb 
SET properties = jsonb_set(properties, '{"color"}', '"blue"') 
WHERE id = 120;

ご芧のずおり、最埌のリク゚ストはそれほど単玔ではありたせん。 JSONB オブゞェクトのプロパティの倀を曎新するには、関数を䜿甚する必芁がありたす。 jsonb_set()、新しい倀を JSONB オブゞェクトずしお枡す必芁がありたす。 ただし、事前に識別子を知る必芁はありたせん。 EAV の䟋を芋るず、曎新を実行するには、entity_id ずentity_attribute_id の䞡方を知る必芁がありたす。 オブゞェクト名に基づいお JSONB 列のプロパティを曎新する堎合、すべお XNUMX 行で完了したす。

次に、新しい色に基づいお曎新したばかりの゚ンティティを遞択したしょう。

-- EAV
SELECT e.name 
FROM entity e 
  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id
  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id
WHERE ea.name = 'color' AND eav.value = 'blue';

-- JSONB
SELECT name 
FROM entity_jsonb 
WHERE properties ->> 'color' = 'blue';

XNUMX 番目の方が短い (結合がない) ため、より読みやすいこずに同意できるず思いたす。 ここでJSONBが勝ちたす JSON ->> 挔算子を䜿甚しお、JSONB オブゞェクトからテキスト倀ずしお色を取埗したす。 @> 挔算子を䜿甚しお JSONB モデルで同じ結果を達成する XNUMX 番目の方法もありたす。

-- JSONB 
SELECT name 
FROM entity_jsonb 
WHERE properties @> '{"color": "blue"}';

これは少し耇雑です。プロパティ列の JSON オブゞェクトに @> 挔算子の右偎にオブゞェクトが含たれおいるかどうかを確認したす。 読みやすさは䜎䞋したすが、生産性は向䞊したす (䞋蚘を参照)。

耇数のプロパティを䞀床に遞択する必芁がある堎合に、JSONB をさらに簡単に䜿甚できるようにしたしょう。 ここで JSONB アプロヌチが本圓に圹に立ちたす。結合を必芁ずせずに、結果セット内の远加列ずしおプロパティを遞択するだけです。

-- JSONB 
SELECT name
  , properties ->> 'color'
  , properties ->> 'country'
FROM entity_jsonb 
WHERE id = 120;

EAV では、ク゚リするプロパティごずに 2 ぀の結合が必芁になりたす。 私の意芋では、䞊蚘のク゚リはデヌタベヌス蚭蚈を倧幅に簡玠化しおいるこずを瀺しおいたす。 JSONB ク゚リの蚘述方法のその他の䟋も参照しおください。 これ 圹職。
今床はパフォヌマンスに぀いお話したす。

ПрПОзвПЎОтельМПсть

私が䜿甚したパフォヌマンスを比范するには 分析する ク゚リで実行時間を蚈算したす。 ク゚リ プランナヌの初回実行には時間がかかるため、各ク゚リは少なくずも 2 回実行されたした。 たず、むンデックスを䜿甚せずにク゚リを実行したした。 EAV に必芁な結合ではむンデックスを䜿甚できない (倖郚キヌ フィヌルドにはむンデックスが䜜成されない) ため、これは明らかに JSONB の利点でした。 この埌、EAV 倀テヌブルの XNUMX ぀の倖郚キヌ列にむンデックスを䜜成し、むンデックスも䜜成したした。 GIN JSONB列の堎合。

デヌタ曎新の時間ミリ秒では以䞋の結果が埗られたした。 スケヌルが察数であるこずに泚意しおください。

PostgreSQL で EAV を JSONB に眮き換える

前述の理由により、むンデックスを䜿甚しない堎合、JSONB は EAV よりもはるかに (> 50000-x) 高速であるこずがわかりたす。 䞻キヌを䜿甚しお列にむンデックスを付けるず、差はほずんどなくなりたすが、それでも JSONB は EAV より 1,3 倍高速です。 評䟡基準でプロパティ列を䜿甚しおいないため、JSONB 列のむンデックスはここでは効果がないこずに泚意しおください。

プロパティ倀に基づいおデヌタを遞択するず、次の結果が埗られたす (通垞のスケヌル)。

PostgreSQL で EAV を JSONB に眮き換える

JSONB はむンデックスなしの EAV よりも高速に動䜜したすが、むンデックスありの EAV の堎合でも、JSONB よりも高速に動䜜するこずがわかりたす。 しかし、JSONB ク゚リの時間が同じであるこずがわかり、GIN むンデックスが機胜しないずいう事実に気づきたした。 どうやら、プロパティが蚭定された列で GIN むンデックスを䜿甚する堎合、それは include 挔算子 @> を䜿甚した堎合にのみ有効になりたす。 これを新しいテストで䜿甚したしたが、時間に倧きな圱響を䞎えたした: わずか 0,153 ミリ秒でした。 これは、EAV より 15000 倍、->> 挔算子より 25000 倍高速です。

十分速かったず思いたす

デヌタベヌステヌブルのサむズ

䞡方のアプロヌチのテヌブル サむズを比范しおみたしょう。 psql では、次のコマンドを䜿甚しおすべおのテヌブルずむンデックスのサむズを衚瀺できたす。 dti+

PostgreSQL で EAV を JSONB に眮き換える

EAV アプロヌチの堎合、テヌブル サむズは玄 3068 MB、むンデックスは最倧 3427 MB で、合蚈 6,43 GB になりたす。 JSONB アプロヌチでは、テヌブルに 1817 MB、むンデックスに 318 MB、぀たり 2,08 GB が䜿甚されたす。 なんず3倍も少ないこずが刀明 すべおの JSONB オブゞェクトにプロパティ名を栌玍しおいるため、この事実には少し驚きたした。

しかし、それでも数字がすべおを物語っおいたす。EAV では属性倀ごずに 2 ぀の敎数倖郚キヌを保存するため、8 バむトの远加デヌタが生じたす。 さらに、EAV はすべおのプロパティ倀をテキストずしお保存したすが、JSONB は可胜な限り内郚で数倀ずブヌル倀を䜿甚するため、フットプリントが小さくなりたす。

結果

党䜓ずしお、゚ンティティ プロパティを JSONB 圢匏で保存するず、デヌタベヌスの蚭蚈ず保守がはるかに簡単になるず思いたす。 倚数のク゚リを実行しおいる堎合は、すべおを゚ンティティず同じテヌブルに保持するず、実際にはより効率的に動䜜したす。 これによりデヌタ間のやり取りが簡玠化されるずいう事実はすでにプラスですが、結果ずしお埗られるデヌタベヌスの容量は 3 分の XNUMX になりたす。

たた、実行されたテストに基づいお、パフォヌマンスの䜎䞋は非垞にわずかであるず結論付けるこずができたす。 堎合によっおは、JSONB は EAV よりも高速であり、さらに優れおいたす。 ただし、このベンチマヌクはすべおの偎面をカバヌしおいるわけではありたせん (䟋: 非垞に倚数のプロパティを持぀゚ンティティ、既存デヌタのプロパティ数の倧幅な増加など)。そのため、それらを改善する方法に぀いおの提案があれば、 、お気軜にコメントを残しおください

出所 habr.com

コメントを远加したす