Azure Cosmos DB入門(2)

本コンテンツは「Azure Cosmos DB入門」の(2)です。

ryuichi111std.hatenablog.com

2 Cosmos DBの主要概念

前回の「Azure CosmosDB入門(1)」では、Cosmos DBの「特徴」と「Hello Cosmos DB」と題した簡単なサンプルについて説明しました。
早く具体的なデータベース操作に入りたいところですが、その前にCosmos DBを利用する上で押さえておきたい主要な概念について説明します。

ここでは、各技術要素の説明に関して「主要概念」の範囲にとどめます。
RU(Request Unit)やパーティショニングなどは、より複雑な内容を理解する必要がありますが、それは「Azure CosmosDB入門(4)」で説明する事とし、ここではCosmos DBプログラミングを始めるための概要を理解することを目的とします。

2.1 データモデルとアクセスAPI

Cosmos DBでは、以下のような多様なデータモデルをサポートします。

  • ドキュメント
  • テーブル
  • グラフ

f:id:daigo-knowlbo:20170521182812p:plain

各データモデルに対しては、それぞれに対応したアクセス用のAPIが提供されています。
「アクセス用API」と「データモデル」は密接な関係を持ちます。AzureにCosmos DBアカウントを作成する際は、初期作成パラメータとして「アクセス用API」を選択します。すると「アクセス用API」に対応した「データモデル」でデータベースアカウントが作成されます。
以下が「アクセス用API」の一覧と、それに対応する「データモデル」です。

  • SQL(DocumentDB)用API : ドキュメント データモデル
  • MongoDB用API : ドキュメント データモデル
  • Gremlin用API : グラフ データモデル
  • Table(Key-Value)用API : Key-Value データモデル

f:id:daigo-knowlbo:20170522005959p:plain

2.1.1 atom-record-sequence(ARS)

マルチデータモデルによるデータ保持を行うCosmos DBですが、内部では「atom-record-sequence(ARS)」というデータ保持の仕方をしています。
公式ドキュメントでは以下のように説明されています。

  • atomは、string/bool/numberのようなプリミティブ型の小さなセットから構成されます
  • recordは、atomから構成される構造体です
  • sequenceは、atom / record / sequenceから構成される配列です

つまり、以下のようなイメージでしょうか。

f:id:daigo-knowlbo:20170521231534p:plain

2.1.2 AzureポータルでCosmos DBを作成する

アクセス用API / データモデルの種類は、Cosmos DBをAzureに作成する際に決定します。
また、Azure Cosmos DBを作成すると「データベースアカウント」が作成されます。これはAzure Cosmos DB全体においてユニークなIDを持つアカウントとなります。
1つのデータベースアカウントにおいて、複数のデータモデルのデータを混在させることはできません。

既に「1.2 Hello Cosmos DB」で、Azureポータルを利用してCosmos DBデータベースを作成する手順については説明しました。
以下は、それぞれのアクセスAPI(=データモデル)のCosmos DBデータベースアカウント作成のイメージです。

(1) DocumentDBデータモデル

f:id:daigo-knowlbo:20170521153217p:plain

(2) MongoDBデータモデル

f:id:daigo-knowlbo:20170521153236p:plain

(3) Graphデータモデル

f:id:daigo-knowlbo:20170521153306p:plain

(4) Tableデータモデル

f:id:daigo-knowlbo:20170521153331p:plain

2.2 各種データモデルのデータ構造

「ドキュメント」「グラフ」「テーブル」の各データモデルにおける「データの構造」について見ていきます。

2.2.1 「ドキュメント」モデル

「ドキュメント」データモデルでは以下のような構造のデータを保持します。

f:id:daigo-knowlbo:20170521231729p:plain

※厳密な構造上はコレクション配下にストアドプロシージャなども含まれますが、それらは適時後述します。

(1) データベースアカウント

「データベースアカウント」がルート要素となります(Azureポータルで作成したリソースのルートです。Azureポータルで「Cosmos DBを作成する」=「Cosmos DBデータベースアカウントを作成する」となります)。

(2) データベース

データベースアカウントの下に「データベース」をN個作成することができます。

(3) コレクション

データベースの下に「コレクション」をN個作成することができます。

(4) ドキュメント

コレクションの下の「ドキュメント」が、データとしての1項目であり、JSON形式のデータで表現されます。

SQL Serverにマッチさせて考えると以下のようになります。

JSONドキュメントデータはスキーマレスであり、異なる形式のデータを同一コレクション内に混在させることができます。
ただし、全く関係のない種類のデータを混在させるというのではなく、同一の概念を表すが、データ毎に詳細は異なるデータ群を同一コレクションに保存することが多いと思います。
以下はショッピングサイトを想定したケースで、商品である「書籍」と「CD」を表したJSONドキュメントです。「商品」という共通概念のデータを表しますが、書籍はページ数(PageCount)という独自の属性があり、CDには「トラック数(TrackCount)」という独自の属性があります。

書籍を表すJSON
{
  "Id": "001",
  "Title": "0から始めるCosmos DB",
  "Price": 2800,
  "PageCount": 360
}
CDを表すJSON
{
  "Id": "102",
  "Title": "毎日10分TOEIC Listening",
  "Price": 1600,
  "TrackCount": 30
}

2.2.2 「グラフ」 モデル(2017/5現在Preview版)

これはDocumentDBがCosmos DBとなったタイミング(2017/5/10)でサポートされた新たなデータモデルです。

f:id:daigo-knowlbo:20170521184357p:plain

グラフデータベース自体は既に世の中には存在し、Neo4Jなどが代表的なものとして存在していました。
Cosmos DBでも同様のグラフデータモデルをサポートし、クエリー言語として「Gremlin」をサポートします。

Vertex と Edge

グラフデータベースの概念の詳細にはここでは深くは触れませんが簡単に・・・
グラフデータベースは一般的に、「twitterfacebookの友達の繋がり」を表す例で説明されることが多いです。それ以外にも「交通機関の駅の繋がりからの最短経路検索」「物流における配送経路の決定」「関連する商品をグラフ管理することにより顧客への商品レコメンド抽出」等々で活用の用途があります。

Vertexは人や物を表す「頂点」であり、それからEdgeはVertex間の関係性を表す「辺・端線」となります。
Vertexの直接的な繋がり、複数のVertexを経由した間接的繋がり、またEdgeは繋がりの種類・方向を表すことができます。

2.2.3 「テーブル」モデル(2017/5現在Preview版)

「テーブル」データモデルは、その名前の通りテーブル構造でデータを保持します。ただしRDBのテーブルと異なり、スキーマレスのテーブル構造となります。
以下の図のように「1つのテーブル」に異なるスキーマ(異なる列構造)のデータを格納することができます。

f:id:daigo-knowlbo:20170521155634p:plain

「Azure入門」と「解剖C#」は書籍データであり「PageCount列」があります。
美女と野獣 bluelay」はブルーレイいコンテンツなので「PageCount列」は無く「Duration列」があります。
コカ・コーラ」については「PageCount列」も「Duration列」も存在しません。

Azure Storage Tableとの関係

テーブルは従来の「Azure Storage Table」のCosmos DB版との位置付けです。
APIもAzure Storage Table APIと互換性のあるインターフェイスが用意されています。
「Cosmos DBのテーブル」と「Azure Storage Table」の使い分けは以下のようになります。

2.3 パーティショニング

Cosmos DBのバックエンドでは、固定化されたサイズのSSDベースのストレージが動作しており、ラッチフリーを実現したBW-Treeのデータベースシステムが動作しています。
また、Cosmos DBは「パーティション」という単位でデータを分割して保持します。パーティションSSDストレージに関連付けられて保持されることになります。
コレクション群を保持する論理リソースを「コンテナ」と呼び、コンテナはパーティションやサーバーを跨いで保持されます。

コレクションに対して用意されるパーティション数は、Cosmos DBにより自動的に適時決定されます(ストレージサイズや予約されたスループット(RU)により決定)。外から実際の動作を確認する限り、コレクションのStorage Capacityが10GB設定の場合には 1パーティション 構成、250GB設定の場合には 25パーティション 構成が取られるようです。

Storage Capacity=250GB(以上)の設定を行う場合、コレクションに対して「パーティションキー」というものを設定する必要があります。
パーティションキー」は、ドキュメント内の データ要素 を指定します。
パーティションキーが同一のドキュメント(データ)は、同一のパーティションに保存されます。
例えば以下のようなドキュメントに対して /cityName をパーティションキーとした場合、cityNameがTokyoのドキュメントは必ず同一パーティションに保存されます。

{
  'id': 1,
  'userName': 'ryuichi',
  'cityName': 'Tokyo'
  ...省略...
}

パーティションについては、設計上の注意点と、良くないパーティション設計を行った場合の問題点があります。

さらに詳細なパーティションについての情報は「Azure CosmosDB入門(4)」で説明します。

2.3.1 Azureポータルでパーティションキーを設定

Data Explorerでコレクションを追加する際に「Partition key」を設定することができます。

f:id:daigo-knowlbo:20170521185102p:plain

2.3.2 プログラムでパーティションキーを設定

以下がプログラムでパーティションキーを設定するコードスニペットです。

client = new DocumentClient(
  new Uri("https://cosmosdb.documents.azure.com:443/"), "[キー]");
await client.CreateDatabaseIfNotExistsAsync(new Database { Id = "Database1" });

var collection = 
  new DocumentCollection { Id = "Collection1" };
collection.PartitionKey.Paths.Add("/cityName");

await client.CreateDocumentCollectionIfNotExistsAsync(
  UriFactory.CreateDatabaseUri("Database1"),
  collection);

DocumentCollectionオブジェクトのPathsプロパティに「/cityName」を追加しています。
コレクション作成時(CreateDocumentCollectionIfNotExistsAsync())の引数で渡されるDocumentCollectionオブジェクトを通じて、指定のパーティションキーが適用されます。

2.4 RU(Request Unit)

RU(Request Unit)はCosmos DBでは非常に重要な概念です。
RUは「Cosmos DBに対する操作(クエリーや書き込み)を行うのに要する計算量」の単位になります。

2.4.1 1RUとは?

RUは「処理を行い為の計算量」の単位ですが、では 1RU とはどれくらいの処理量でしょうか。
公式ドキュメントにおいて 1RU は以下とされています。

The baseline of 1 request unit for a 1KB item corresponds to a simple GET by self link or id of the item.

つまり、
「1KBのデータを、セルフリンクもしくはIDでシンプルに取得する操作に要するRUが約1」
ということです。
大きなデータの取得、複雑な抽出条件の指定等を行うと、そのデータ抽出操作を行うのに必要なRU値が増えます。

2.4.2 RUはコレクションに割り当てる

RUは「コレクション」に対して 100RU/秒 単位で割り当てます。
RUは「扱うデータサイズ・量」「抽出条件の複雑度」「リクエストのトラフィック量」により必要量が決まります。また、RUのサイズがCosmos DBの課金における大きな要素となりますので、割り当てサイズの決定は非常に重要になります。

2.4.3 RUという概念のすばらしさ

従来のハードウェアリソースを意識した、つまり、CPUの割り当て数・メモリの割り当て量を基準としたスケール概念の場合、データ量の増大・抽出条件の複雑化が発生すると、結果取得のパフォーマンス(応答時間)の遅延につながります。
これに対してCosmos DBでは、CPUやメモリのような物理リソースではなく、RUという抽象単位の割り当てを行う考え方をとります。RU/秒は1秒間にこなせる計算量であり、応答時間については「read <10ms、write <15ms」がSLAで保障され続けます。

つまり、Cosmos DBでは「コレクション毎に、1秒間に処理できるRU」を予約設定します。予約したRUを使い切ろうが、使わなかろうが、予約したRU分が課金対象となります。そして、「read <10ms、write <15ms」のパフォーマンスを発揮するのに要するCPUやメモリはAzureバックエンドの話であり、Cosmos DB利用者は意識する必要がないし、見ることすらできません。バックエンドのハードウェインフラを意識することなく、一定の契約パフォーマンスが常に得られる、まさに PaaS の素晴らしさといえるでしょう。

2.4.4 予約RUの設定方法

コレクションに対するRUの設定は、Azureポータルやプログラムから行うことができます。

(1) Azureポータルによる設定

Azureポータルでは、コレクションを作成する際にRUの設定を行うことができます。
項目としては「Throughput」となります。
以下の画面キャプチャに示すように、Storage Capacityが「Fixed(10GB)」の場合には 400~10,000 RU の範囲で、Storage Capacityが「Unlimited」の場合には 2,500~100,000 RU の範囲で設定が可能です。

f:id:daigo-knowlbo:20170522011055p:plain

f:id:daigo-knowlbo:20170522011121p:plain

初期作成後もData Explorerでスループットの変更は柔軟に行うことができます。

f:id:daigo-knowlbo:20170522011537p:plain

(2) プログラムによる設定

1000RUのスループットを定義したDocumentDBコレクションを作成するコードスニペットは以下の通りです。

// 接続用クライアントオブジェクトの作成
this.client = new DocumentClient(
  new Uri(EndpointUrl), PrimaryKey);

// データベースの作成
await this.client.CreateDatabaseIfNotExistsAsync( new Database { Id = DatabaseId });

// コレクションの作成
var collection = new DocumentCollection { Id = CollectionId };
var requestOptions = new RequestOptions() { OfferThroughput = 1000 };
await this.client.CreateDocumentCollectionIfNotExistsAsync(
  UriFactory.CreateDatabaseUri(DatabaseId),
  collection,
  requestOptions);

RequestOptionsオブジェクトの「OfferThroughputプロパティ」に、必要なスループット(RU)を指定してコレクションの作成メソッドを呼び出します。
10,000RUを超える値を設定する場合は、パーティションキーを設定する必要があります。設定しなかった場合、例外が発生します。

2.4.5 消費RUの確認方法

Cosmos DBにおいては RU/秒 を予約設定して運用します。
つまり、アプリケーション実装におけるCosmos DBへの読み込み・書き込みがどれだけのRUを消費するかは、RUの予約数を決定するうえで非常に重要です。
前述のようにCosmos DB公式ドキュメントにおいても 1RU の目安は以下とされています。

「1KBのデータを1件取得する(一貫性レベルはSession)」操作に要するRUが約「1」

実際に実装したアプリケーション上の操作におけるRUの消費量は以下の方法で確認することが出来ます。

(1) Cosmos DBへの操作を行った際のResponseヘッダ「x-ms-request-charge」で確認

DocumentDB API(接続オプションをTCPではなくHTTPSとしている場合)・REST APIによる操作を行っている場合にはHTTPS通信を監視するFiddlerなどを使って確認することができます。   以下は「WHERE Age > 20」という条件付きであるコレクションのドキュメント検索を行った際のHTTPS通信内容です。

f:id:daigo-knowlbo:20170521174333p:plain

右下のペインにおいてHTTP Response Headerを確認しています。
「x-ms-request-charge」項目が今回の検索を行うのに消費したRUであり 3.72 という値が返却されています。

(2) Azureポータルのクエリーエクスプローラで確認

Azureポータルにはクエリーエクスプローラ機能が用意されています。
以下の画面のようにコレクションに対して任意のSQLを実行すると「請求の要求」という項目で消費RUが表示されます。

f:id:daigo-knowlbo:20170521175115p:plain

(3) GetLastRequestStatisticsコマンド(MongoDB)で確認

GetLastRequestStatisticsコマンドを呼び出すことで直前の処理の消費RUを確認することができます。
C#上から呼び出す例は以下です(詳細については「Azure CosmosDB入門(3)」で説明します)。

MongoClient client = new MongoClient(settings);
var database = client.GetDatabase(dbName);

...任意のMongoDBアクセス処理

var result = database.RunCommand<BsonDocument>(new CommandDocument { { "getLastRequestStatistics", 1 } });
Console.WriteLine( "消費RU={0}", result["RequestCharge"].RawValue);

2.4.6 RUの話は深い・・・(後半に続く)

記述の通りRUはCosmos DBでは非常に重要な要素です。
Cosmos DBを運用する際のコストに対して非常に大きな比重を持ちますし、運用においてのシステムとして利用するスケーラビリティのバリューでもあります。
また、まだ触れていない「インデックス」「一貫性レベル」の設定状態によっても変動があります。
そして、以下のような要素についての解説がまだまだ必要なのですが、これはまた後半で説明する事とします。

  • RU/秒 と RU/分
  • RU超過時のレスポンスコード429
  • etc…

RUについての理解はひとまずここまでにし、もう少し具体的なCosmos DBプログラミングの理解を進めた後で、さらに深堀したほうが理解しやすいと考える為です。

<< Azure Cosmos DB入門(1)へ | Azure Cosmos DB入門(3)へ >>