NoSQLドキュメントストアは、大量の非構造化データを管理するのに理想的です。しかし、非構造化データを扱いながらも、従来のSQLデータベースに付随する機能を求める組織もある。例えば、メディアやニュースのコンテンツ・エージェンシーは、膨大な量のテキストや画像コンテンツを中心に、トラフィックの多いウェブサイトを運営している場合がある。このような非構造化データを保存する必要があるにもかかわらず、NoSQLデータベースのような柔軟なスキーマや水平方向のスケーラビリティを必要としない場合があります。その代わりに、PostgreSQLのようなリレーショナル・データベースがもたらすデータベース管理の容易さと一貫性を必要としているのです。
両方の長所を生かすことは可能ですか?可能です。
非構造化データをサポートするデータ型を持つPostgreSQLは、コスト効率が良く、管理が簡単なリレーショナルデータベースの中でNoSQLの機能を利用することを可能にします。この記事では、PostgreSQLのHStoreとJSONBデータ型を使って非構造化データを扱う方法を見ていきます。
本題に入る前に、SQLデータベースとNoSQLデータベースの主な違いを簡単に見ておこう。
SQLとNoSQLの比較
SQLデータベースとNoSQLデータベースには、それぞれ独自の長所と短所があります。どちらのデータベースがお客様のデータニーズに最も適しているか、十分な情報を得た上で決定するには、それぞれの違いをよく理解する必要があります。
PostgreSQLやMySQLのようなSQL(リレーショナル)データベースは、テーブル、行、列の明確で予測可能な構造でデータを表します。これらのデータベースはACID特性(原子性、一貫性、分離、耐久性)を遵守しており、データベーストランザクションの確実な処理を保証することで、データ整合性のための強力な基盤となっている。
SQLデータベースは、複雑なクエリーやトランザクションシステム(金融アプリケーションなど)を扱う場合など、データの一貫性と整合性が極めて重要な場面で威力を発揮する。
対照的に、NoSQLデータベース(ドキュメントストア)は、必ずしも表形式に適していない大規模で多様なデータセットに対応している。NoSQLデータベースの例としては、MongoDB、Cassandra、Couchbaseなどがある。NoSQLデータベースは柔軟なスキーマで動作し、時間の経過とともにデータ構造を進化させることができる。また、水平方向のスケーラビリティにも対応しており、データを複数のサーバーに分散することで、大きなデータ負荷や高トラフィックへの対応を向上させている。
NoSQLデータベースは、リアルタイム・アプリケーションや大規模な言語モデル(LLM)における大量のデータの処理など、スケーラビリティが重要なアプリケーションでよく使用されます。NoSQLデータベースは、多様で進化するデータ構造を扱う場合にも有益で、データ・ニーズの変化に合わせて組織を適応させることができます。
なぜPostgreSQLをドキュメントストアとして使うのか?
PostgreSQLはリレーショナルデータベースなので、NoSQLのニーズを満たすための選択肢として考えるのは型破りに思えるかもしれません。しかし、PostgreSQLをドキュメントストアとして使用する強力なケースがあるかもしれません。
データストレージのニーズが多様で、構造化されたACID準拠のデータストレージと柔軟でスキーマレスのドキュメントストレージの両方を必要とする場合、PostgreSQLを活用してリレーショナルモデルと非リレーショナルモデルを組み合わせることができます。あるいは、ある種のNoSQLの機能が欲しいが、ACIDの特性から来るデータの一貫性の保証も欲しいという場合もあるでしょう。最後に、活発なコミュニティを持つ成熟した技術として、PostgreSQLは包括的なSQLサポート、高度なインデックス作成、全文検索を提供します。これらの機能とNoSQL機能を組み合わせることで、PostgreSQLは汎用的なデータストレージソリューションとなります。
NoSQLスタイルのデータにPostgreSQLを使用することの限界
PostgreSQLはその汎用性にもかかわらず、従来のNoSQLデータベースと比較するとある種の制限があります。PostgreSQLは垂直方向に拡張できますが、NoSQLデータベースが一般的に提供する機能である水平方向の拡張や自動シャーディングによる分散データを本質的にサポートしていません。また、PostgreSQLはワイドカラムストアやグラフデータベースのような特定のNoSQLデータ構造に対する最適化も提供していません。最後に、PostgreSQLは、いくつかのNoSQLデータベースで提供されている、性能を最適化するための調整可能な一貫性を提供しません。
大規模な非構造化データセットにPostgreSQLを使用することを検討している場合、これらの制限が性能とスケーリング能力に影響を与える可能性があることを知っておいてください。さらに、SQLとNoSQLのデータ操作を混在させると複雑さが生じます。慎重に計画を立て、両方のパラダイムを理解することで、潜在的な落とし穴を避けることができます。
しかし、PostgreSQLを正しく理解し使用することで、SQLとNoSQLの両方の長所を備えた強力なツールとなります。
PostgreSQLのHStoreとJSONB
PostgreSQLをNoSQLソリューションとして使用する可能性を検討する中で、NoSQLのような機能を提供する3つのデータ型に出会いますが、それぞれユニークな特徴と使用例を持っています。
- 店舗:このデータ型では、キーと値のペアを1つのPostgreSQLの値に格納することができます。固定スキーマを持たない半構造化データを格納するのに便利です。
- JSONB:これはJSONライクなデータのバイナリ表現である。HStoreに比べてより複雑な構造を格納でき、完全なJSON機能をサポートしている。JSONBはインデックスが可能なので、大量のデータに適している。
- JSON:これはJSONBに似ているが、JSONBの機能や効率性の多くが欠けている。JSONデータ型は、入力テキストの正確なコピーを保存し、空白やキーの重複を含む。
JSONデータ型は、JSONBが提供する完全な機能を必要としない場合に、JSONフォーマットのデータを格納するための有効な選択肢として言及します。しかし、この記事の残りの部分では、HStoreとJSONBに焦点を当てます。
Hストア
PostgreSQLのドキュメントでは、HStoreは "めったに検査されない多くの属性を持つ行や半構造化データ "がある場合に有用であると説明しています。HStoreデータ型を使用する前に、HStore拡張を有効にしてください:
> CREATE EXTENSION hstore;
HStoreは、カンマで区切られた0個以上のキー⇒値で表される。ペアの順序は重要ではなく、出力時に確実に保持されるわけでもない。
> SELECT 'foo => bar, prompt => "hello world", pi => 3.14'::hstore;
hstore
-----------------------------------------------------
"pi"=>"3.14", "foo"=>"bar", "prompt"=>"hello world"
(1 row)
各HStoreキーは一意である。HStore宣言が重複したキーで行われた場合、重複したキーのうちの1つだけが保存され、それがどれになるかは保証されません。
> SELECT 'key => value1, key => value2'::hstore;
hstore
-----------------
"key"=>"value1"
(1 row)
フラットなキーバリュー構造を持つHStoreは、シンプルで高速なクエリを提供し、シンプルなシナリオに最適です。しかし、HStoreはテキストデータのみをサポートし、ネストされたデータをサポートしていないため、複雑なデータ構造には適していません。
一方、JSONBはより多様なデータ型を扱うことができる。
JSONB
JSONBデータ型は、JSON形式の入力テキストを受け入れ、それを分解したバイナリ形式で格納する。この変換により入力は若干遅くなりますが、結果として高速処理と効率的なインデックス作成が可能になります。JSONBは空白やオブジェクト・キーの順序を保持しない。
> SELECT '{"foo": "bar", "pi": 3.14, "nested": { "prompt": "hello", "count": 5 } }'::jsonb;
jsonb
-----------------------------------------------------------------------
{"pi": 3.14, "foo": "bar", "nested": {"count": 5, "prompt": "hello"}}
(1 row)
オブジェクト・キーが重複して与えられた場合は、最後の値が保持される。
> SELECT '{"key": "value1", "key": "value2"}'::jsonb;
jsonb
-------------------
{"key": "value2"}
(1 row)
JSONBは複雑な構造と完全なJSON機能をサポートしているため、複雑なデータやネストされたデータには理想的な選択肢であり、HStoreやJSONよりも望ましい。ただし、JSONBを使用すると、HStoreに比べてパフォーマンスのオーバーヘッドが発生し、ストレージの使用量が増えます。
実践的な例HStoreとJSONBを使う
これらのデータ型をどのように扱うかを示すために、いくつかの実践的な例を考えてみましょう。テーブルの作成、基本的なクエリーと操作、インデックス作成について見ていきます。
HStoreの基本操作
他のデータ型と同様に、PostgreSQLデータテーブルのフィールドをHStoreデータ型として定義することができます。
> CREATE TABLE articles ( id serial primary key, title varchar(64), meta hstore );
HStore属性を持つレコードの挿入は次のようになる:
> INSERT INTO articles (title, meta)
VALUES (
'Data Types in PostgreSQL',
'format => blog, length => 1350, language => English, license => "Creative Commons"');
> SELECT * FROM articles;
id | title | meta ----+--------------------------+------------------------------------------ 1 | Data Types in PostgreSQL | "format"=>"blog", "length"=>"1350", "license"=>"Creative Commons", "language"=>"English"(1 row)
HStoreフィールドを使用すると、指定したキーで指定されたフィールドから特定のキーと値のペアをフェッチすることができます:
> SELECT title, meta -> 'license' AS license, meta -> 'format' AS format FROM articles;
title | license | format
---------------------------------+------------------+------------
Data Types in PostgreSQL | Creative Commons | blog
Advanced Querying in PostgreSQL | None | blog
Scaling PostgreSQL | MIT | blog
PostgreSQL Fundamentals | Creative Commons | whitepaper
(4 rows)
また、HStoreフィールド内の特定の値に基づいた条件でクエリを実行することもできます。
> SELECT id, title FROM articles WHERE meta -> 'license' = 'Creative Commons';
id | title
----+--------------------------
1 | Data Types in PostgreSQL
4 | PostgreSQL Fundamentals
(2 rows)
HStoreフィールドに特定のキーを含む行のみを検索したい場合があります。例えば、以下のクエリは、メタHStoreにnoteキーが含まれる行のみを返します。これを行うには、?
> SELECT title, meta->'note' AS note FROM articles WHERE meta ? 'note';
title | note
---------------------------------+-----------------
PostgreSQL Fundamentals | hold for review
Advanced Querying in PostgreSQL | needs edit
(2 rows)
便利なHStoreの演算子や関数の一覧は、こちらにあります。例えば、HStoreのキーを配列に抽出したり、HStoreをJSON表現に変換することができます。
> SELECT title, akeys(meta) FROM articles where id=1;
title | akeys
--------------------------+----------------------------------
Data Types in PostgreSQL | {format,length,license,language}
(1 row)
> SELECT title, hstore_to_json(meta) FROM articles where id=1;
title | hstore_to_json
--------------------------+------------------------------------------------
Data Types in PostgreSQL | {"format": "blog", "length": "1350", "license": "Creative Commons", "language": "English"}
(1 row)
JSONBの基本操作
PostgreSQLでJSONBデータ型を扱うのは簡単です。テーブルの作成とレコードの挿入は以下のようになります:
> CREATE TABLE authors (id serial primary key, name varchar(64), meta jsonb);
> INSERT INTO authors (name, meta) VALUES ('Adam Anderson', '{ "active":true, "expertise": ["databases", "data science"], "country": "UK" }');
jsonbメタフィールドはJSON形式のテキスト文字列として提供されることに注意してください。指定した値が有効なJSONでない場合、PostgreSQLは文句を言います。
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "this is not valid JSON" }');
ERROR: invalid input syntax for type json
HStore型とは異なり、JSONBはネストされたデータをサポートしている。
> INSERT INTO authors (name, meta) VALUES ('Barbara Brandini', '{ "active":true, "expertise": ["AI/ML"], "country": "CAN", "contact": { "email": "barbara@example.com", "phone": "111-222-3333" } }');
HStoreと同様に、JSONBフィールドは特定のキーのみで部分的に取得することができる。例えば
> SELECT name, meta -> 'country' AS country FROM authors;
name | country ------------------+--------- Adam Anderson | "UK" Barbara Brandini | "CAN" Charles Cooper | "UK"(3 rows)
JSONBデータ型には、HStoreと使い方が似ている演算子がたくさんあります。たとえば、次のように ? 演算子を使用すると、meta フィールドに contact キーが含まれている行のみを検索できます。
> SELECT name, meta -> 'active' AS active, meta -> 'contact' AS contact FROM authors WHERE meta ? 'contact';
name | active | contact
------------------+--------+-----------------------------------------------
Barbara Brandini | true | {"email": "barbara@example.com", "phone": "111-222-3333"}
Charles Cooper | false | {"email": "charles@example.com"}
(2 rows)
インデックスを使う
ドキュメントによると、HStoreデータ型は"@>, ?, ?&, ?|演算子に対してGiSTとGINインデックスをサポートしている"。この2種類のインデックスの違いについての詳しい説明は、こちらを参照してください。JSONBのインデックスでは、GINインデックスを使用して、キーまたはキーと値のペアを効率的に検索します。
インデックスを作成するステートメントは期待通りである:
> CREATE INDEX idx_hstore ON articles USING GIN(meta);
> CREATE INDEX idx_jsonb ON authors USING GIN(meta);
NoSQLの柔軟性を備えたSQL構造
冒頭で述べた元のユースケースをもう一度考えてみよう。NoSQLドキュメントストアとほぼ同じ方法で記事を保存しているニュースコンテンツ会社を想像してほしい。おそらく、記事はJSONでセクションを表すオブジェクトの順序付けられた配列として表現され、それぞれがテキストコンテンツ、表記、フォーマットを持つ。加えて、多くのメタデータがそれぞれの記事に関連付けられ、それらのメタデータ属性は記事ごとに一貫性がない。
上記の説明は、この組織が必要とするNoSQLの大部分を要約したものですが、データを管理・整理する方法については、他のすべてがリレーショナル・データモデルと密接に一致しています。
JSONBのようなデータ型のNoSQLの機能とPostgreSQLの伝統的なSQLの強みを組み合わせることで、組織は柔軟なスキーマとネストされたデータでの高速なクエリを享受することができます。PostgreSQLのHStoreとJSONBデータ型は、リレーショナルデータベースの構造を必要としながらもNoSQLスタイルのデータ格納を必要とする開発者に強力な選択肢を提供します。
スケールでのPostgreSQL
伝統的なリレーショナル・データベースの枠組みの中で、NoSQLスタイルのデータ・ストレージとクエリをサポートしたいと考えていませんか?おそらくあなたの組織では、この投稿で説明したような方法でドキュメントを扱っているのではないでしょうか。あるいは、大規模言語モデル(LLM)やその他の人工知能/ML事業のために、非構造化データのストレージを処理するオプションをお探しかもしれません。
LinodeMarketplace のPostgreSQLクラスタは、SQLデータベースのリレーショナルモデルと構造、NoSQLデータベースの水平スケーラビリティを提供します。これをHStoreまたはJSONBデータ型の使用と組み合わせることで、PostgreSQLで作業しながらNoSQLの機能を利用するための理想的なハイブリッドソリューションができます。
コメント