NoSQL ドライバーの機能比較
この記事では、さまざまなベンダーが提供する標準ベースのドライバーが、さまざまな NoSQL ユースケースをどのように処理するかを比較します。MongoDB をサンプルデータソースとして使用し、特定のテーブル内のすべてのデータを取得する単純なリクエストから、埋め込み配列を含む複雑なクエリまでを対象とします。
この比較では、MongoDB, Inc が提供する restaurants データセット(こちらからダウンロード可能)のインスタンスに対して各クエリを送信しています。以下に、データセット内のドキュメントの代表的なサンプルを示します。
比較するユースケース
- SELECT * FROM restaurants: テーブル内のすべてのデータを取得するリクエストの結果を比較
- 埋め込み配列を別テーブルとして扱う: テーブルと埋め込み配列データを操作するための JOIN クエリの結果を比較
- 埋め込み配列とサブドキュメントをテーブル要素として扱う: 埋め込み配列をテーブル内の要素として操作した結果を比較
サンプルドキュメント
{
"_id" : ObjectId("5780046cd5a397806c3dab38"),
"address" : {
"building" : "1007",
"coord" : [-73.856077, 40.848447],
"street" : "Morris Park Ave",
"zipcode" : "10462"
},
"borough" : "Bronx",
"cuisine" : "Bakery",
"grades" : [{
"date" : ISODate("2014-03-03T00:00:00Z"),
"grade" : "A",
"score" : 2
}, {
"date" : ISODate("2013-09-11T00:00:00Z"),
"grade" : "A",
"score" : 6
}, {
"date" : ISODate("2013-01-24T00:00:00Z"),
"grade" : "A",
"score" : 10
}, {
"date" : ISODate("2011-11-23T00:00:00Z"),
"grade" : "A",
"score" : 9
}, {
"date" : ISODate("2011-03-10T00:00:00Z"),
"grade" : "B",
"score" : 14
}],
"name" : "Morris Park Bake Shop",
"restaurant_id" : "30075445"
}
SELECT * FROM restaurants
従来のリレーショナルデータベースを操作する場合、特定のテーブル内のすべてのデータを取得する最も簡単な方法は、SELECT * FROM table クエリを送信することです。MongoDB のデータは従来のリレーショナルデータとは異なることが多いため、特定のドライバーがこのようなクエリをどのように処理するかを理解することが重要です。 以下に、デフォルトの接続プロパティを使用した各ドライバーの SELECT * ... クエリの結果を示します。
Competitor 1 のドライバーは、データベースの解析時に grades 配列と address オブジェクトを別々のテーブルとして解析します。Competitor 2 のドライバーは、grades 配列と address.coord 配列を別々のテーブルとして解析します。別々のテーブルを作成することで NoSQL データベース内のさまざまなデータタイプを区別するのに役立ちますが、親テーブルと子テーブルの両方からデータをクエリする際にはいくつかの欠点が生じる可能性があります。CData ドライバーは、デフォルトで最も多くのデータを公開し、address オブジェクトをフラット化し、grades 配列内の要素で利用可能なフィールドを取得します。
CData Software
| _id | address.building | address.coord.0 | address.coord.1 | address.street | address.zipcode | borough | cuisine | name | restaurant_id | grades.0.date | grades.0.grade | grades.0.score |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5780046cd5a397806c3dab38 | 1007 | -73.856077 | 40.848447 | Morris Park Ave | 10462 | Bronx | Bakery | Morris Park Bake Shop | 30075445 | 2014-03-03T00:00:00.000Z | A | 2 |
| 5780046cd5a397806c3dab39 | 469 | -73.961704 | 40.662942 | Flatbush Avenue | 11225 | Brooklyn | Hamburgers | Wendy'S | 30112340 | 2014-12-30T00:00:00.000Z | A | 8 |
| 5780046cd5a397806c3dab3a | 351 | -73.98513559999999 | 40.7676919 | West 57 Street | 10019 | Manhattan | Irish | Dj Reynolds Pub And Restaurant | 30191841 | 2014-09-06T00:00:00.000Z | A | 2 |
| 5780046cd5a397806c3dab3b | 2780 | -73.98241999999999 | 40.579505 | Stillwell Avenue | 11224 | Brooklyn | American | Riviera Caterer | 40356018 | 2014-06-10T00:00:00.000Z | A | 5 |
Competitor 1
| BOROUGH | RESTAURANT_ID | _ID | CUISINE | NAME |
|---|---|---|---|---|
| Bronx | 30075445 | 5780046CD5A397806C3DAB38 | Bakery | Morris Park Bake Shop |
| Brooklyn | 30112340 | 5780046CD5A397806C3DAB39 | Hamburgers | Wendy'S |
| Manhattan | 30191841 | 5780046CD5A397806C3DAB3A | Irish | Dj Reynolds Pub And Restaurant |
| Brooklyn | 40356018 | 5780046CD5A397806C3DAB3B | American | Riviera Caterer |
Competitor 2
| _id | address_building | address_street | address_zipcode | borough | cuisine | name | restaurant_id |
|---|---|---|---|---|---|---|---|
| 5780046cd5a397806c3dab38 | 1007 | Morris Park Ave | 10462 | Bronx | Bakery | Morris Park Bake Shop | 30075445 |
| 5780046cd5a397806c3dab39 | 469 | Flatbush Avenue | 11225 | Brooklyn | Hamburgers | Wendy'S | 30112340 |
| 5780046cd5a397806c3dab3a | 351 | West 57 Street | 10019 | Manhattan | Irish | Dj Reynolds Pub And Restaurant | 30191841 |
| 5780046cd5a397806c3dab3b | 2780 | Stillwell Avenue | 11224 | Brooklyn | American | Riviera Caterer | 40356018 |
埋め込み配列を別テーブルとして扱う
サンプルデータでは、restaurants テーブルの各ドキュメントに、grades 要素内に埋め込みドキュメントの配列が含まれており、レストランが時間の経過とともに受け取ったさまざまなグレードを表しています。MongoDB のドキュメントによると、「埋め込みドキュメントは、単一のドキュメント構造内にデータを格納することで、データ間の関係をキャプチャします」。デフォルトでは、Competitor 1 と Competitor 2 のドライバーは、埋め込みドキュメントを別々のテーブルとしてのみ認識するスキーマを作成し(Competitor 1 は配列を仮想テーブル、Competitor 2 は子テーブルと呼びます)、grades テーブルが restaurants テーブルと外部キー関係を共有するテーブルスキーマを作成します。CData ドライバーは、埋め込みドキュメントを元のドキュメント内の要素として維持しながら、埋め込み値を別々のテーブルとして扱うこともできます。
スキーマの定義方法に関係なく、すべてのドライバーは JOIN クエリを実行して、2 つのテーブルから関連データを取得できます。このセクションでは、各グレードをレストランの名前と ID とともに単一の行として返すために各ドライバーが必要とするクエリと、クエリのデータを取得するのに要した時間を比較します。
期待される結果セット
以下に、プレースホルダーデータで埋められた各ドライバーの期待される結果セットを示します。
| restaurant_id | date | grade | score | P_id |
|---|---|---|---|---|
| 30075445 | 2014-03-03T00:00:00.000Z | A | 2 | 568c37b748ddf53c5ed98932 |
| 30075445 | 2013-09-11T00:00:00.000Z | A | 6 | 568c37b748ddf53c5ed98932 |
| 30075445 | 2013-01-24T00:00:00.000Z | A | 10 | 568c37b748ddf53c5ed98932 |
| 30075445 | 2011-11-23T00:00:00.000Z | A | 9 | 568c37b748ddf53c5ed98932 |
| 30075445 | 2011-03-10T00:00:00.000Z | B | 14 | 568c37b748ddf53c5ed98932 |
ドライバー別のクエリと時間
各グレードを個別の行として取得するために各ドライバーが必要とするクエリは比較的似ており、いずれも暗黙の JOIN を使用していますが、Competitor 1 と Competitor 2 のドライバーは、2 つのテーブル間の関係を識別するために WHERE 句の使用が必要であることに注意してください。CData ドライバーは、垂直フラット化(子配列が親テーブル内のフィールドとして認識されますが、別々のテーブルとして扱うこともできる)を使用して JOIN クエリを管理します。Competitor 1 と Competitor 2 のドライバーは、デフォルトで grades 配列を別々のテーブルとして扱います。これは、両方のテーブルからデータがメモリに読み込まれ、ドライバーがクライアントサイドで JOIN を実行することを意味します。
| ドライバー | 時間(秒) | クエリ(約 1,000 万件のレストランの全グレードを返す) |
|---|---|---|
| CData Software | 252.9 (+35% - +59%) | SELECT [restaurants].[restaurant_id], [restaurants.grades].* FROM [restaurants.grades] JOIN [restaurants] |
| Competitor 1 | 341.5 | SELECT restaurants_grades.*, restaurants.restaurant_id FROM restaurants_grades, restaurants WHERE restaurants._ID = restaurants_grades.restaurants_id |
| Competitor 2 | 401.2 | SELECT restaurants_grades.*, restaurants.restaurant_id FROM restaurants_grades, restaurants WHERE restaurants._ID = restaurants_grades._id |
埋め込み配列とサブドキュメントをテーブル要素として扱う
MongoDB ドキュメントには、個々の要素として埋め込み BSON/JSON オブジェクトや配列が含まれることがよくあります。このセクションでは、水平方向にフラット化されたデータセット、つまりテーブル内の各レストランの ID と最初の 5 つのグレードを取得するために各ドライバーが必要とするクエリを検証します。
期待される結果セット
以下に、プレースホルダーデータで埋められた各ドライバーの期待される結果セットを示します。
| restaurant_id | grades.0.grade | grades.1.grade | grades.2.grade | grades.3.grade | grades.4.grade |
|---|---|---|---|---|---|
| 123456780 | A | A | A | A | A |
| 123456781 | B | B | B | B | B |
| 123456782 | C | C | C | C | C |
CData Software
CData のドライバーでは、上記のようなデータを取得するために自由形式のクエリを送信するだけで済みます。ドライバーは、個々の配列オブジェクトやサブドキュメント内のフィールドへのリクエストを解釈するためにドット記法を使用します。
SELECT [restaurant_id], [grades.0.grade], [grades.1.grade], [grades.2.grade], [grades.3.grade], [grades.4.grade] FROM [restaurants]
Competitor 1
Competitor 1 のドライバーは、ドキュメントの配列を仮想テーブルとして解釈するため、レストランとグレードの両方のデータを取得するには JOIN クエリを実行する必要があります。このデータの解釈方法では、特定のレストランのグレードを単一の行で取得する簡単な方法がありません。
Competitor 2
Competitor 2 のドライバーも、ドキュメントの配列を別々のテーブルとして解釈します。Competitor 1 のドライバーと同様に、レストランとグレードの両方のデータを取得するには JOIN クエリを実行する必要があり、特定のレストランのグレードを単一の行で取得する簡単な方法がありません。
関連記事
- NoSQL ドライバー:パフォーマンス比較 - NoSQL データソースから大規模なデータセットをクエリおよび処理する際の、さまざまなベンダーのドライバーのパフォーマンスを比較します。
- CData NoSQL 概要 - NoSQL を操作する際に CData のドライバー技術を際立たせる機能について説明します。