web-dev-qa-db-ja.com

JSONとしてMySQLにデータを保存する

これはn00bのやるべきことだと思いました。そして、そう、私はそれをやったことがない。それから、FriendFeedがこれを実行し、実際にDBのスケールを改善し、レイテンシーを削減したことがわかりました。これを行う必要がある場合、私は興味があります。そして、もしそうなら、それを行う正しい方法は何ですか?

基本的に、MySQLにすべてをCouchDBのようなDBとして保存する方法を学ぶのに適した場所は何ですか?すべてをJSONとして保存する方が簡単で迅速なようです(構築するのではなく、レイテンシーを減らします)。

また、DBにJSONとして保存されているものを編集、削除などするのは簡単ですか?

112
Oscar Godson

CouchDBとMySQLは、まったく異なる2つの獣です。 JSONは、CouchDBにデータを保存するネイティブな方法です。 MySQLでできる最善の方法は、JSONデータを単一のフィールドにテキストとして保存することです。これは、RDBMSに保存する目的を完全に無効にし、すべてのデータベーストランザクションを大幅に複雑にします。

しないでください

そうは言っても、FriendFeedはMySQLの上で 極めてカスタムスキーマ を使用しているように見えました。本当に何を保存したいかに依存します。データベースシステムを悪用する方法について明確な答えはほとんどありません。記事が非常に古く、MongoとCouchに対する彼らの主な理由が未熟であることを考えると、MySQLがあなたのためにそれをカットしないなら、私はこれら2つを再評価します。彼らは今までに大きく成長していたはずです。

55
deceze

誰もが間違った角度からこれに来ているようです、PHPを介してJSONコードをリレーショナルDBに保存することは問題ありません。実際、このような複雑なデータの読み込みと表示はより速くなります。検索、インデックス作成などの設計上の考慮事項があります。

これを行う最良の方法は、ハイブリッドデータを使用することです。たとえば、MySQL(パフォーマンス調整済み)がPHPよりもはるかに高速で、会場の距離を検索するような場合に検索する必要がある場合また、MySQLははるかに高速である必要があります(検索にアクセスできないことに注意してください)。検索する必要のないデータは、JSON、BLOB、または必要と思われるその他の形式で保存できます。

アクセスする必要があるデータは、基本的なケースごとの請求書システムなど、JSONとして非常に簡単に保存されます。それらはRDBMSの恩恵をまったく受けず、正しいHTMLフォーム構造があればjson_encoding($ _ POST ['entires'])だけでJSONに保存できます。

MongoDBを使用して満足していることをうれしく思います。引き続き役立つことを願っていますが、MySQLが常にあなたのレーダーから外れるとは思わないでください。アプリの複雑さが増すため、RDBMSが必要になる可能性があります一部の機能と機能(アーカイブされたデータまたはビジネスレポートを廃止するためだけのものであっても)

MySQL 5.7は、MongoDBや他のスキーマレスドキュメントデータストアに類似したネイティブJSONデータタイプをサポートするようになりました。

JSONサポート

MySQL 5.7.8以降、MySQLはネイティブJSONタイプをサポートします。 JSON値は文字列として保存されず、代わりにドキュメント要素への迅速な読み取りアクセスを許可する内部バイナリ形式を使用します。 JSON列に格納されたJSONドキュメントは、挿入または更新されるたびに自動的に検証され、無効なドキュメントはエラーを生成します。 JSONドキュメントは作成時に正規化され、=、<、<=、>、> =、<>、!=、および<=>などのほとんどの比較演算子を使用して比較できます。サポートされる演算子、およびJSON値を比較するときにMySQLが従う優先順位およびその他の規則については、JSON値の比較と順序付けを参照してください。

MySQL 5.7.8では、JSON値を操作するための多くの関数も導入されています。これらの機能には、以下にリストされている機能が含まれます。

  1. JSON値を作成する関数:JSON_ARRAY()、JSON_MERGE()、およびJSON_OBJECT()。セクション12.16.2「JSON値を作成する関数」を参照してください。
  2. JSON値を検索する関数:JSON_CONTAINS()、JSON_CONTAINS_PATH()、JSON_EXTRACT()、JSON_KEYS()、およびJSON_SEARCH()。セクション12.16.3「JSON値を検索する関数」を参照してください。
  3. JSON値を変更する関数:JSON_APPEND()、JSON_ARRAY_APPEND()、JSON_ARRAY_INSERT()、JSON_INSERT()、JSON_QUOTE()、JSON_REMOVE()、JSON_REPLACE()、JSON_SET()、およびJSON_UNQUOTE()。セクション12.16.4「JSON値を変更する関数」を参照してください。
  4. JSON値に関する情報を提供する関数:JSON_DEPTH()、JSON_LENGTH()、JSON_TYPE()、およびJSON_VALID()。セクション12.16.5「JSON値の属性を返す関数」を参照してください。

MySQL 5.7.9以降では、JSON_EXTRACT(column、path)の省略形としてcolumn-> pathを使用できます。これは、WHERE句、ORDER BY句、GROUP BY句など、SQLステートメントで列識別子が発生する可能性のある列のエイリアスとして機能します。これには、SELECT、UPDATE、DELETE、CREATE TABLE、およびその他のSQLステートメントが含まれます。左側は、エイリアスではなくJSON列識別子である必要があります。右側は、引用符で囲まれたJSONパス式で、列値として返されたJSONドキュメントに対して評価されます。

->およびJSON_EXTRACT()の詳細については、セクション12.16.3「JSON値を検索する関数」を参照してください。 MySQL 5.7でのJSONパスのサポートについては、JSON値の検索と変更を参照してください。セカンダリインデックスと仮想生成列も参照してください。

詳細:

https://dev.mysql.com/doc/refman/5.7/en/json.html

64
eecue

json文字は、ストレージなどの特殊な文字ではありません。

{}[]'a-z0-9....は本当に特別なものではなく、テキストとして保存できます。

あなたが持っている最初の問題はこれです

{profile_id:22、ユーザー名: 'Robert'、パスワード: 'skhgeeht893htgn34ythg9er'}

データベースに保存されているものは、独自の手順を経てmysql用のjsondecodeを開発しない限り、更新するのはそれほど簡単ではありません。

UPDATE users SET JSON(user_data,'username') = 'New User';

そのため、最初にjsonを選択し、デコード、変更、更新する必要があるため、理論的には、適切なデータベース構造の構築により多くの時間を費やす必要があります。

私はjsonを使用してデータを保存しますが、メタデータのみを頻繁に更新し、ユーザー固有ではありません。ユーザーが投稿を追加する場合、その投稿に画像を追加し、画像を解析して親指を作成します次に、json形式のサムURLを使用します。

24
RobertPitt

クエリを使用してJSONデータを取得することがいかに難しいかを示すために、これを処理するために作成したクエリを共有します。

配列やその他のオブジェクトは考慮せず、基本的なデータ型のみを考慮します。 columnの4つのインスタンスをJSONを格納する列名に変更し、myfieldの4つのインスタンスをアクセスするJSONフィールドに変更する必要があります。

SELECT
    SUBSTRING(
        REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', ''),
        LOCATE(
            CONCAT('myfield', ':'),
            REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', '')
        ) + CHAR_LENGTH(CONCAT('myfield', ':')),
        LOCATE(
            ',',
            SUBSTRING(
                REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', ''),
                LOCATE(
                    CONCAT('myfield', ':'),
                    REPLACE(REPLACE(REPLACE(column, '{', ''), '}', ','), '"', '')
                ) + CHAR_LENGTH(CONCAT('myfield', ':'))
            )
        ) - 1
    )
    AS myfield
FROM mytable WHERE id = '3435'
14
Jorjon

それは本当にあなたのユースケースに依存します。レポートにまったく価値のない情報を保存し、他のテーブルとJOINを介してクエリされない場合は、JSONとしてエンコードされた単一のテキストフィールドにデータを保存することが理にかなっています。

これにより、データモデルが大幅に簡素化されます。ただし、RobertPittが述べたように、このデータを正規化された他のデータと組み合わせることができるとは思わないでください。

10
Phil LaNasa

これは古い質問ですが、Googleの検索結果の上部に表示されるので、質問の4年後に新しい回答を追加することは意味があると思います。

まず、JSONをRDBMSに保存する際のサポートが改善されています。 PostgreSQLへの切り替えを検討することもできます(ただし、MySQLはv5.7.7からJSONをサポートしています)。 PostgreSQLは、より多くの機能をサポートすることを除き、MySQLと非常によく似たSQLコマンドを使用します。追加された機能の1つは、JSONデータ型を提供し、保存されたJSONを照会できるようになったことです。 ( これに関するいくつかの参照 )たとえば、PHPでPDOまたはLaravelでeloquentを使用して、プログラムで直接クエリを作成していない場合は、PostgreSQLをインストールするだけです。サーバーおよびデータベース接続設定の変更。コードを変更する必要さえありません。

他の回答が示唆したように、ほとんどの場合、データをJSONとしてRDBMSに直接保存することはお勧めできません。ただし、いくつかの例外があります。私が考えることができる1つの状況は、リンクされたエントリの可変数を持つフィールドです。

たとえば、ブログ投稿のタグを保存するには、通常、ブログ投稿用のテーブル、タグのテーブル、および一致するテーブルが必要になります。そのため、ユーザーが投稿を編集するときに、その投稿に関連するタグを表示する必要がある場合、3つのテーブルをクエリする必要があります。マッチングテーブル/タグテーブルが長い場合、これはパフォーマンスに大きなダメージを与えます。

タグをブログ投稿テーブルにJSONとして保存することにより、同じアクションで必要なのは単一のテーブル検索のみです。これにより、ユーザーはブログの投稿をより速く編集できるようになりますが、タグにリンクされている投稿をレポートしたり、タグで検索したりすると、パフォーマンスが低下します。

データベースの非正規化を試みることもできます。データを複製し、両方の方法でデータを保存することにより、両方の方法の利点を享受できます。データを保存するためにもう少し時間が必要で、より多くのストレージスペースが必要です(計算能力のコストと比較すると安価です)

9
cytsunny

これを検討する唯一の2つの理由は次のとおりです。

  • 正規化されたアプローチではパフォーマンスが十分ではありません
  • 特に流動的/柔軟/変化するデータを簡単にモデル化することはできません

ここに自分のアプローチについて少し書きました。

NoSQLデータストアを使用してどのようなスケーラビリティの問題が発生しましたか?

(トップの回答を参照)

JSONでさえ十分に高速ではなかったため、カスタムテキスト形式のアプローチを使用しました。働いた/私たちのためにうまく機能し続けています。

MongoDBのようなものを使用していない理由はありますか? (MySQLは「必要」である可能性があります;好奇心が強い)

8
Brian

この質問に答える誰もが、@ deceze -仕事に適切なツールを使用するを除いて、1つの重要な問題を見逃しているようです。リレーショナルデータベースに強制的にほぼすべてのタイプのデータを保存することができ、Mongoにリレーショナルデータを処理させることができますが、どのようなコストがかかりますか?最終的には、スキーマ設計からアプリケーションコードに至るまで、開発と保守のすべてのレベルで複雑さが生じます。パフォーマンスヒットは言うまでもありません。

2014年には、特定の種類のデータを非常にうまく処理する多くのデータベースサーバーにアクセスできます。

  • Mongo(ドキュメントストレージ)
  • Redis(キーと値のデータストレージ)
  • MySQL/Maria/PostgreSQL/Oracle/etc(リレーショナルデータ)
  • CouchDB(JSON)

RabbirMQやCassandraのような他のいくつかを見逃したと思います。私のポイントは、保存する必要があるデータに適切なツールを使用することです。

アプリケーションでさまざまなデータの保存と取得が本当に高速で必要な場合は、アプリケーションに複数のデータソースを使用することをためらわないでください(だれもそうしません)。最も人気のあるWebフレームワークは、複数のデータソース(Rails、Django、Grails、Cake、Zendなど)のサポートを提供します。この戦略は、アプリケーションの特定の1つの領域、ORMまたはアプリケーションのデータソースインターフェイスに複雑さを制限します。

6
CheddarMonkey

以下は、JSON配列のキーを列に保存/更新する関数と、JSON値を取得する別の関数です。この関数は、JSON配列を格納する列名がjsonであると仮定して作成されます。 PDOを使用しています。

保存/更新機能

function save($uid, $key, $val){
 global $dbh; // The PDO object
 $sql = $dbh->prepare("SELECT `json` FROM users WHERE `id`=?");
 $sql->execute(array($uid));
 $data      = $sql->fetch();
 $arr       = json_decode($data['json'],true);
 $arr[$key] = $val; // Update the value
 $sql=$dbh->prepare("UPDATE `users` SET `json`=? WHERE `id`=?");
 $sql->execute(array(
   json_encode($arr), 
   $uid
 ));
}

ここで、$ uidはユーザーのID、$ key-更新するJSONキーとその値は$ valと記述されています。

値取得関数

function get($uid, $key){
 global $dbh;
 $sql = $dbh->prepare("SELECT `json` FROM `users` WHERE `id`=?");
 $sql->execute(array($uid));
 $data = $sql->fetch();
 $arr  = json_decode($data['json'], true);
 return $arr[$key];
}

ここで、$ keyは、値が必要なJSON配列のキーです。

5
Subin

MySQL 5.7.7 JSON labs releaselinux binariessource )!にMySQLにJSONを保存するための初期サポートが追加されました!リリースは、公開された一連のJSON関連のユーザー定義関数から成長したようです 2013年に戻る

この初期のネイティブJSONサポートは、INSERTでのJSON検証、プリアンブルのルックアップテーブルを含む最適化されたバイナリストレージ形式など、JSN_EXTRACT関数がアクセスごとに解析するのではなくバイナリルックアップを実行できるようにするなど、非常にポジティブな方向に向かっているようです。特定のJSONデータ型を処理およびクエリするための新しい関数も多数あります。

CREATE TABLE users (id INT, preferences JSON);

INSERT INTO users VALUES (1, JSN_OBJECT('showSideBar', true, 'fontSize', 12));

SELECT JSN_EXTRACT(preferences, '$.showSideBar') from users;

+--------------------------------------------------+
| id   | JSN_EXTRACT(preferences, '$.showSideBar') |
+--------------------------------------------------+
| 1    | true                                      |
+--------------------------------------------------+

私見、上記はこの新機能の素晴らしいユースケースです。多くのSQLデータベースには既にユーザーテーブルがあり、進化する一連のユーザー設定に対応するために無限のスキーマ変更を行うのではなく、単一のJSON列を単一のJOINで完結させるのが最適です。特に、個々のアイテムに対してクエリを実行する必要はほとんどありません。

まだ初期段階ですが、MySQLサーバーチームは、変更の伝達に関して素晴らしい仕事をしています ontheblog

2
Rich Pollock

JSONは、PostgreSQLデータベースでも有効なデータ型です。ただし、MySQLデータベースはまだJSONを公式にサポートしていません。しかし、それはベーキングです: http://mysqlserverteam.com/json-labs-release-native-json-data-type-and-binary-format/

また、一部のデータをデータベース内の文字列にシリアル化する方が適切であるという多くの有効なケースがあることに同意します。主な理由は、定期的にクエリが実行されず、スキーマ自体が変更される可能性がある場合です。それに対応するデータベーススキーマを変更する必要はありません。 2番目の理由は、シリアル化された文字列が外部ソースから直接のものである場合、それらをすべて解析して、いずれかを使用するまでデータベースにフィードする必要がない場合があることです。したがって、異なるデータベース間の切り替えがより簡単になるため、新しいMySQLリリースがJSONをサポートするのを待っています。

2

JSONをmysqlデータベースに保存することは、実際にはRDBMSを使用することを目的としているため、RDBMSを使用する目的に反すると思います。複雑さを増すだけでなく、使用方法によっては簡単にパフォーマンスに影響を与える可能性があるため、ある時点で操作または報告されるデータには使用しません。

しかし、実際にこれを行う理由として考えられるものが他にあると思ったら、私は興味がありました。ロギングの目的で例外を作成することを考えていました。私の場合、さまざまな量のパラメーターとエラーがあるリクエストをログに記録します。この状況では、リクエストのタイプにテーブルを使用し、取得されたさまざまな値のJSON文字列を持つリクエスト自体を使用します。

上記の状況では、リクエストはログに記録され、JSON文字列フィールド内で操作またはインデックス付けされることはありません。しかし、より複雑な環境では、おそらくこのタイプのデータをより意図したものを使用して、そのシステムに保存しようとします。他の人が言っているように、それは本当にあなたが達成しようとしていることに依存しますが、標準に従うことは常に長寿と信頼性に役立ちます!

2
Mark

私はこれが本当に遅いことを知っていますが、ポイントまでテーブルを正規化し、そのポイントを超えてテキスト値としてJSONでデータを保存するRDBMS標準を維持するハイブリッドアプローチを使用した同じような状況がありました。たとえば、RDBMSの正規化規則に従って、4つのテーブルにデータを保存します。ただし、動的スキーマに対応する4番目の表では、JSON形式でデータを保存します。データを取得するたびに、JSONデータを取得して解析し、Javaで表示します。これはこれまでのところうまくいき、ETLを使用して、テーブル内のjsonデータに変換するフィールドに正規化された方法でインデックスを付けることができるようになりました。これにより、ユーザーがアプリケーションで作業している間、彼は最小限の遅延に直面し、フィールドはデータ分析などのためにRDBMSフレンドリーな形式に変換されます。このアプローチはうまく機能し、MYSQL(5.7+)このアプローチにより、RDBMSおよびNOSQLデータベースの両方の利点が得られます。

1
prashant

私はjsonを使用してプロジェクトのすべてを記録します。実際には3つのテーブルを使用します! 1つはjsonのデータ用、1つはjson構造の各メタデータのインデックス用(各メタは一意のIDによってエンコードされます)、もう1つはセッションユーザー用です。この初期状態のコードではベンチマークを定量化することはできませんが、たとえば、ユーザービュー(インデックスとの内部結合)でカテゴリ(またはユーザーとしての何か)を取得し、非常に低速でした(非常に非常に低速でした) 、mysqlの使用済みビューは良い方法ではありません)。この構造の検索モジュールは、私がやりたいことは何でもできますが、完全なjsonデータレコードの概念では、mongodbの方が効率的だと思います。私の例では、カテゴリのツリーとパンくずリストを作成するためにビューを使用しています。実行するクエリが非常に多い! Apache自体は消えました!実際、この小さなWebサイトでは、ツリーとブレッドクラムを生成するphpを使用しています。データの抽出は検索モジュール(インデックスのみを使用)によって行われ、データテーブルは更新のみに使用されます。必要に応じて、すべてのインデックスを破棄し、各データでそれを再生成し、すべてのデータ(json)を破棄してインデックステーブルでのみ再生成するなど、逆の作業を行うことができます。私のプロジェクトはphpとmysqlの下で実行されていますが、このプロジェクトではノードjsとmongodbを使用する方が効率的である場合があります。

あなたができると思うなら、jsonを使用してください。そして、それが間違いだったら忘れてください。良い選択か悪い選択かを試してみてください、でも試してください!

フランスのユーザー

1
low

このGistを使用できます: https://Gist.github.com/AminaG/33d90cb99c26298c48f670b8ffac39c

サーバーにインストールした後(スーパーではなくroot権限が必要です)、次のようなことができます:

select extract_json_value('{"a":["a","2"]}','(/a)')

これはa 2を返します。これを使用してJSON内のすべてを返すことができます。良い点は、MySQL 5.1、5.2、5.6をサポートしていることです。また、サーバーにバイナリをインストールする必要はありません。

古いプロジェクトcommon-schemaに基づいていますが、現在も機能しています https://code.google.com/archive/p/common-schema/