269

ユーザー関連のデータをテーブルに保存するために、次のモデルを実装しています-2つの列があります- uid(主キー)とmeta、ユーザーに関する他のデータをJSON形式で保存する列です。

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['foo@bar.com','bar@foo.com']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['sann@bar.com','sann@foo.com']}
--------------------------------------------------

uidこれは、テーブルに、、、のような多くの列が含まれる、プロパティごとに1列のモデルよりも優れた方法(パフォーマンス、デザイン)nameですかemailid

最初のモデルで気に入っているのは、制限なしでできるだけ多くのフィールドを追加できることです。

また、最初のモデルを実装したので、疑問に思いました。'foo'のような名前を持つすべてのユーザーをフェッチしたいなど、クエリを実行するにはどうすればよいですか?

質問-JSONまたはフィールドごとの列を使用してデータベースにユーザー関連データを保存するためのより良い方法はどれですか(フィールドの数は固定されていないことに注意してください)?また、最初のモデルが実装されている場合、上記のようにデータベースをクエリするにはどうすればよいですか?クエリで検索される可能性のあるすべてのデータを別の行に保存し、他のデータをJSON(別の行)に保存して、両方のモデルを使用する必要がありますか?


アップデート

検索を実行する必要のある列が多すぎることはないので、両方のモデルを使用するのが賢明ですか?検索する必要のあるデータの列ごとのキーと他のデータのJSON(同じMySQLデータベース内)?

4

10 に答える 10

242

2017 年 6 月 4 日更新

この質問/回答がある程度人気を得ていることを考えると、更新する価値があると考えました.

この質問が最初に投稿されたとき、MySQL は JSON データ型をサポートしておらず、PostgreSQL でのサポートはまだ始まったばかりでした。5.7 以降、MySQLは JSON データ型(バイナリ ストレージ形式) をサポートするようになり、PostgreSQL JSONBは大幅に成熟しました。どちらの製品も、JSON オブジェクトの特定のキーのインデックス作成のサポートを含め、任意のドキュメントを格納できるパフォーマンスの高い JSON タイプを提供します。

ただし、リレーショナル データベースを使用する場合の既定の設定は、引き続き値ごとの列にする必要があるという最初の声明を支持します。リレーショナル データベースは、内部のデータが十分に正規化されていることを前提に構築されています。クエリ プランナーは、JSON ドキュメント内のキーを見るときよりも、列を見るときの方が優れた最適化情報を持っています。列間で外部キーを作成できます (ただし、JSON ドキュメントのキー間では作成できません)。重要: スキーマの大部分が JSON の使用を正当化するほど揮発性である場合、少なくともリレーショナル データベースが正しい選択であるかどうかを検討することをお勧めします。

とはいえ、完全にリレーショナルまたはドキュメント指向のアプリケーションはほとんどありません。ほとんどのアプリケーションでは、両方が混在しています。以下は、リレーショナル データベースで JSON が役立つと私が個人的に感じたいくつかの例です。

  • 連絡先の電子メール アドレスと電話番号を保存する場合、それらを値として JSON 配列に保存すると、複数の個別のテーブルよりも管理がはるかに簡単になります。

  • 任意のキー/値のユーザー設定を保存する (値はブール値、テキスト、または数値にすることができ、データ型ごとに個別の列を持ちたくない場合)

  • スキーマが定義されていない構成データの保存 (Zapier または IFTTT を構築していて、統合ごとに構成データを保存する必要がある場合)

他にもあると思いますが、これらは簡単な例です。

元の回答

制限なし (任意のドキュメント サイズ制限以外) に必要な数のフィールドを追加できるようにしたい場合は、MongoDB などの NoSQL ソリューションを検討してください。

リレーショナル データベースの場合: 値ごとに 1 つの列を使用します。JSON ブロブを列に配置すると、実質的にクエリが不可能になります (実際に機能するクエリを見つけると、非常に遅くなります)。

リレーショナル データベースは、インデックス作成時にデータ型を利用し、正規化された構造で実装されることを意図しています。

補足として、これは JSON をリレーショナル データベースに保存してはならないということではありません。真のメタデータを追加する場合、または JSON が照会する必要がなく表示のみに使用される情報を記述している場合、すべてのデータ ポイントに対して個別の列を作成するのはやり過ぎかもしれません。

于 2013-03-12T17:15:17.710 に答える
90

ほとんどのものと同様に、「それは依存します」。列または JSON にデータを格納すること自体は、正しいか間違っているか、良いか悪いかということではありません。後で何をする必要があるかによって異なります。このデータにアクセスするための予測される方法は何ですか? 他のデータを相互参照する必要がありますか?

他の人々は、技術的なトレードオフが何であるかについてかなりよく答えています。

アプリと機能が時間の経過とともに進化し、このデータ ストレージの決定がチームにどのように影響するかについて議論した人はあまりいません。

JSON を使用する誘惑の 1 つは、スキーマの移行を回避することであり、チームが訓練されていない場合、さらに別のキーと値のペアを JSON フィールドに貼り付けることは非常に簡単です。そのための移行はありません。その目的を誰も覚えていません。それについての検証はありません。

私のチームは Postgre の従来の列と一緒に JSON を使用しましたが、最初はスライスパン以来最高のものでした。JSON は魅力的で強力でしたが、ある日、柔軟性には代償が伴い、それが突然本当の問題点になることに気付きました。この設計上の決定の上に他の多くのものを構築したため、その点が非常に急速に忍び寄り、変更が難しくなることがあります。

時間の経過とともに、新しい機能を追加し、JSON でデータを保持することで、従来の列に固執した場合に追加されたものよりも複雑に見えるクエリが作成されました。そこで、結合を行って値を比較できるように、特定のキー値を釣り上げて列に戻し始めました。悪いアイデア。これで重複が発生しました。新しい開発者が参加して混乱するでしょうか? 保存し直すべき値はどれですか? JSON のものですか、それとも列ですか?

JSON フィールドは、あれやこれやの小さな断片のジャンク ドロワーになりました。データベース レベルでのデータ検証はなく、ドキュメント間の一貫性や整合性もありません。これにより、従来の列から厳密な型と制約のチェックを取得する代わりに、すべての責任がアプリに押し込まれました。

振り返ってみると、JSON のおかげで非常に迅速に反復処理を行い、成果を出すことができました。よかった。しかし、チームが一定の規模に達した後は、その柔軟性により、技術的負債の長いロープにぶら下がることができ、その後の機能の進化の進行が遅くなりました. 注意して使用してください。

データの性質について、じっくりと考えてみてください。これはアプリの基盤です。時間の経過とともにデータがどのように使用されるか。そして、どのように変化する可能性がありますか?

于 2016-02-10T16:08:10.253 に答える
31

放り投げただけですが、WordPress にはこの種のもののための構造があります (少なくとも、WordPress は私が最初に観察した場所であり、おそらく他の場所で発生したものです)。

無制限のキーが許可され、JSON BLOB を使用するよりも高速に検索できますが、一部の NoSQL ソリューションほど高速ではありません。

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

編集

履歴・複数キーの保管用

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

次のような方法でクエリを実行します。

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
于 2013-03-12T17:49:05.937 に答える
18

アプローチの欠点はまさにあなたが言ったことです:

テキスト検索を実行する必要があるたびに、物を見つけるのが非常に遅くなります。

代わりに、列ごとの値は文字列全体と一致します。

あなたのアプローチ(JSONベースのデータ)は、検索する必要がなく、通常のデータと一緒に表示する必要があるデータには適しています。

編集:明確にするために、上記は古典的なリレーショナルデータベースに当てはまります。NoSQLは内部でJSONを使用しますが、それが望ましい動作である場合は、おそらくより適切なオプションです。

于 2013-03-12T17:14:36.520 に答える
12

基本的に、最初に使用するモデルはドキュメント ベース ストレージと呼ばれます。MongoDB や CouchDB などの一般的な NoSQL ドキュメント ベースのデータベースを確認する必要があります。基本的に、ドキュメント ベースのデータベースでは、json ファイルにデータを格納し、これらの json ファイルに対してクエリを実行できます。

2 番目のモデルは、一般的なリレーショナル データベース構造です。

MySql のようなリレーショナル データベースを使用する場合は、2 番目のモデルのみを使用することをお勧めします。最初のモデルのように MySql を使用してデータを保存しても意味がありません

2番目の質問に答えるために、最初のモデルを使用する場合、「foo」のような名前を照会する方法はありません

于 2013-03-12T17:26:07.167 に答える
5

主にリレーショナル モデルを使用するかどうかを迷っているようです。

現状では、あなたの例はリレーショナル モデルにかなりよく適合しますが、このモデルを進化させる必要がある場合はもちろん問題が発生する可能性があります。

メイン エンティティ (ユーザー) の属性レベルが 1 つ (またはいくつかの事前定義済み) しかない場合でも、リレーショナル データベースでエンティティ属性値 (EAV) モデルを使用できます。(これも一長一短あります。)

アプリケーションを使用して検索したい構造化されていない値を取得することが予想される場合、MySQL はここでの最良の選択ではない可能性があります。

PostgreSQL を使用していた場合は、両方の利点を最大限に活用できる可能性があります。(これは、ここでのデータの実際の構造に大きく依存します... MySQL も必ずしも間違った選択ではなく、NoSQL オプションも興味深い可能性があります。私は代替案を提案しているだけです。)

実際、PostgreSQL は (不変の) 関数 (私の知る限り、MySQL はできません) にインデックスを構築できます。最近のバージョンでは、JSON データに PLV8 を直接使用して、関心のある特定の JSON 要素にインデックスを構築できます。そのデータを検索するときのクエリの速度。

編集:

検索を実行する必要がある列はそれほど多くないので、両方のモデルを使用するのが賢明ですか? 検索する必要があるデータの列ごとのキーと、その他のデータの JSON (同じ MySQL データベース内)?

2 つのモデルを混在させることは必ずしも間違っているわけではありませんが (余分なスペースが無視できると仮定すると)、2 つのデータ セットが同期されていることを確認しないと問題が発生する可能性があります。 .

これを実現する良い方法は、更新または挿入が行われるたびにデータベース サーバー内でストアド プロシージャを実行することにより、自動更新をトリガーで実行することです。私の知る限り、MySQL ストアド プロシージャ言語は、おそらくあらゆる種類の JSON 処理をサポートしていません。ここでも、PLV8 をサポートする PostgreSQL (および、より柔軟なストアド プロシージャ言語を備えた他の RDBMS) がより便利になるはずです (トリガーを使用してリレーショナル列を自動的に更新することは、同じ方法でインデックスを更新することと非常によく似ています)。

于 2013-03-12T17:30:15.930 に答える
0

非リレーショナルモデルをリレーショナルデータベースに適合させようとしているので、 MongoDBなどのNoSQLデータベースを使用した方がよいと思います。フィールドの数に制限がないという要件に適合する事前定義されたスキーマはありません(一般的なMongoDBコレクションの例を参照)。MongoDBのドキュメントをチェックして、ドキュメントをクエリする方法を理解してください。

db.mycollection.find(
    {
      name: 'sann'
    }
)
于 2013-03-12T17:21:20.950 に答える