5

nosql を使用したリレーショナル データのモデリングに関する私の質問をフォローアップするために、この件に関するいくつかの記事を読みました。

Nosqlは非リレーショナルを意味するものではありません

Nosql e コマースの例

彼らは、nosql が正規化されたリレーショナル データを処理できることを示唆しているようです。

それでは、以前の例を続けましょう。記事と著者の 2 種類のデータを持つ CMS システムです。記事には (ID による) 著者への参照があります。

以下は、システムがサポートする必要がある操作です。

  1. 著者とともに id で記事を取得する
  2. 特定の著者によるすべての記事を取得する
  3. 著者を作成日順にソートして、最初の 10 件の記事を検索します

同じデータが RDBMS に格納されている場合の同じ操作と比較して、これらの操作のパフォーマンスを理解したいと思います。 特に、操作で MapReduce を使用するか、nosql ストア (リンク) への複数回のトリップが必要か、または事前に参加するかを指定してください。

mongodb、couchdb、riak などのドキュメント ベースのnosql ソリューションに限定して説明したいと思います。

編集1:

Spring-data プロジェクトは Riak と Mongodb で利用可能です

4

3 に答える 3

5

興味のある人のために、CouchDBの回答を投げたかっただけです。:)

上記の最初の回答で述べたように、著者ドキュメントを記事ドキュメントに埋め込むことは賢明ではないため、以下の例では、記事と著者の 2 つのドキュメント タイプを想定しています。

CouchDB は、通常 JavaScript で記述された MapReduce クエリを使用します (ただし、Python、Ruby、Erlang なども利用できます)。MapReduce クエリの結果は、最初のリクエスト時にインデックスに保存され、その保存されたインデックスはその後のすべてのルックアップに使用されます。データベースへの変更は、その後の要求に応じてインデックスに追加されます。

CouchDB の API は完全に HTTP ベースであるため、データベースへのすべてのリクエストは、さまざまな URL での HTTP 動詞 (GET、POST、PUT、DELETE) です。MapReduce クエリ (JavaScript で記述) と、インデックスから関連する結果を要求するために使用される URL の両方をリストします。

1. 著者と一緒に id で記事を取得する

これを行うための最も簡単な方法は、2 つの直接ドキュメント検索です。

/db/{article_id} を取得
GET /db/{author_id}

...ここで、{author_id} は記事の author_id フィールドから取得した値です。

2. 特定の著者によるすべての記事を取得する

MapReduce

function (doc) {
  if (doc.type === 'article') {
    emit(doc.author_id, doc);
  }
}
GET /db/_design/cms/_view/articles_by_author?key="{author_id}"

...ここで、{author_id} は著者の実際の ID です。

3. 最初の 10 件の記事を検索し、著者を作成日順に並べ替えます

MapReduce

function (doc) {
  function arrayDateFromTimeStamp(ts) {
    var d = new Date(ts);
    return [d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
  }

  var newdoc = doc;
  newdoc._id = doc.author_id;
  newdoc.created_at = arrayDateFromTimeStamp(doc.created_at);

  if (doc.type === 'article') {
    emit(newdoc.created_at, newdoc); 
  }
}

?include_docs=trueビュー リクエストを使用して、CouchDB にスタイルの「結合」を含めることができます。エミット (2 番目の引数) の値側に「_id」キーを含める場合、include_docs=trueクエリ パラメーターに追加すると、指定された「_id」によって参照されるドキュメントが含まれます。上記の場合、ドキュメント自体を置き換えます。 "_id" (これはもう必要ありません) を、参照された著者の "_id" (記事ドキュメントの "author_id" の値) に置き換えます。関連する著者情報を含む上位 10 件の記事をリクエストすると、次のようになります。

GET /db/_design/cms/_view/articles_by_date?descending=true&limit=10&include_docs=true

その URL をリクエストすると、最新の 10 件の記事のリストが次のような形式で返されます。

{"行":[
  { "id":"article_id",
    "キー":[2011, 9, 3, 12, 5, 41],
    "value":{"_id":"author_id", "タイトル":"..."},
    "doc":{"_id":"author_id", "名前":"著者名"}
  }
]}

この同じインデックスを使用すると、著者データの有無にかかわらず、任意の年、月、日、時間などの粒度ですべてのドキュメントのリストを取得できます。

ビュー照合を使用して、複数のドキュメントを 1 つのドキュメントに集約する方法もあります (異種のコンテンツを参照する CMS のページのように)。7 月に CouchConf で行ったこれらのスライドに、その方法に関する情報がいくつかあります: http://www.slideshare.net/Couchbase/couchconfsfdesigningcouchbasedocuments

他にご不明な点がございましたら、お気軽にお問い合わせください。

于 2011-10-03T16:14:06.613 に答える
4

著者とともに id で記事を取得する

SQL :

  • 1 件のクエリ
  • 2 つのインデックス ルックアップ
  • 2回のデータ検索
  • 返されるデータ = 記事 + 著者

モンゴDB

  • 2 つのクエリ
  • 2 つのインデックス ルックアップ
  • 2回のデータ検索
  • 返されるデータ = 記事 + 著者

特定の著者によるすべての記事を取得する

SQL :

  • 1 件のクエリ
  • 1 つのインデックス ルックアップ
  • N データ ルックアップ
  • 返されるデータ = N 個の記事

モンゴDB

  • 1 件のクエリ
  • 1 つのインデックス ルックアップ
  • N データ ルックアップ
  • 返されるデータ = N 個の記事

著者を作成日順にソートして、最初の 10 件の記事を検索します

SQL :

  • 1 件のクエリ
  • 2 つのインデックス ルックアップ
  • 11 から 20 のデータ ルックアップ (記事、次に固有の著者)
  • 返されるデータ = 10 件の記事 + 10 件の著者

モンゴDB

  • 2 つのクエリ ( articles.find().sort().limit(10)authors.find({$in:[article_authors]})
  • 2 つのインデックス ルックアップ
  • 11 から 20 のデータ ルックアップ (記事、次に固有の著者)
  • 返されるデータ = 10 件の記事 + 1 ~ 10 人の著者

概要

2 つのケースでは、MongoDB は追加のクエリを必要としますが、その下で同じ総作業のほとんどを実行します。場合によっては、MongoDB がネットワーク経由で返すデータが少なくなります (エントリが繰り返されない)。結合クエリは、結合するすべてのデータが同じボックスに存在するという要件によって制限される傾向があります。Authors と Articles が別の場所に住んでいる場合、とにかく 2 つのクエリを実行することになります。

MongoDB は、書き込みのたびにディスクにフラッシュしないため、「生の」パフォーマンスが向上する傾向があります (つまり、実際には「耐久性」のトレードオフです)。また、クエリ パーサーがはるかに小さいため、クエリごとのアクティビティが少なくなります。

基本的なパフォーマンスの観点からは、これらは非常に似ています。彼らは、あなたのデータとあなたがしたいトレードオフについて異なる仮定をしているだけです.

于 2011-10-01T01:29:36.827 に答える
2

MongoDB の場合、作成者レコードに埋め込みドキュメントを使用しません。したがって、事前参加は終了し、DBへの複数回の旅行です。ただし、作成者をキャッシュすることはでき、レコードごとに 1 回だけ 2 回目のトリップを行う必要があります。あなたが示したクエリは、MongoDB ではかなり簡単です。

var article = db.articles.find({id: article_id}).limit(1);
var author = db.authors.find({id: article.author_id});

ORM/ODM を使用してアプリケーション内のエンティティを管理している場合、これは透過的です。ただし、データベースへの2回の旅行になります。ただし、応答は速いはずですが、2 回のヒットはまったく目立たないはずです。

特定の著者による記事の検索は逆です...

var author = db.authors.find({id: author_name}).limit(1);
var articles = db.articles.find({author_id: author.id});

繰り返しますが、2 つのクエリですが、単一の作成者のフェッチは高速で、簡単にキャッシュできます。

var articles = db.articles.find({}).sort({created_at: 1}).limit(10);
var author_ids = articles.map(function(a) { return a.author_id });
var authors = db.authors.find({id: { '$in': authors_ids }});

最後に、2 つのクエリですが、少しだけ複雑です。これらを mongo シェルで実行して、結果がどのようになるかを確認できます。

これが map reduce to complete を書く価値があるかどうかはわかりません。2、3 回の短いラウンド トリップでは遅延が少し長くなる可能性がありますが、mongo プロトコルはかなり高速です。私はそれについて過度に心配することはありません。

最後に、この方法による実際のパフォーマンスへの影響... 理想的には、ドキュメント内のインデックス付きフィールドに対してのみクエリを実行するため、かなり高速になるはずです。唯一の追加ステップは、他のドキュメントを取得するための 2 回目の往復です。アプリケーションとデータベースの構造によっては、これは大したことではない可能性があります。mongo に、特定のしきい値 (有効にするとデフォルトで 100 ミリ秒または 200 ミリ秒) を超えるクエリのみをプロファイルするように指示できるため、データが大きくなるにつれてプログラムに時間がかかっているものを監視できます。

ここで RDMS が提供しない利点の 1 つは、データの分割がはるかに簡単になることです。他のものをサポートするために CMS を超えてアプリケーションを拡張し、同じ認証ストアを使用するとどうなりますか? たまたま完全に独立した DB になり、多くのアプリケーションで共有されています。データベース全体でこれらのクエリを実行する方がはるかに簡単です。RDMS ストアを使用すると、複雑なプロセスになります。

これが NoSQL の発見に役立つことを願っています!

于 2011-10-01T01:14:39.047 に答える