デザイン パターン – IoTと集約 (Design patterns – IoT and aggregation)

Posted: 2018/11/14 カテゴリー: Uncategorized
タグ:, , , ,

Design patterns – IoT and aggregation


Rafat Sarosh (Principal Program Manager, Azure Cosmos DB)

この記事では、高スループットでIoTデータを挿入し、それから、レポートのために異なるフィールドで集約を使う方法を学んでいきます。このデザイン パターンを理解するためには、Azure Cosmos DBをよく知っており、変更フィード要求ユニット (RU)、Azure Functionsを十分理解している必要があります。これらがあなたにとって新しい概念である場合は、これらについて学ぶために、前述のリンクに進んでください。

多くのデータベースは、データをパーティション分割することで、極めて高いスループットと低いレイテンシを達成しています。これは、MongoDB、HBase、Cassandra、Azure Cosmos DBといった、すべてのNoSQLデータベース エンジンに当てはまります。これらすべてのデータベースは、パーティション分割、つまりシャーディングのために、無限にスケール アウト可能です。

Azure Cosmos DBを、より詳しく見てみましょう。トップ レベルで、コンテナーが定義されています。コンテナーをテーブル、コレクション、またはグラフと考えることができますが、コンテナーは、すべての情報を保持する主要なエンティティです。Azure Cosmos DBは、このトップ レベル エンティティを定義するために、「コンテナー」という用語を使っています。Azure Cosmos DBはマルチ モデル データベースなので、このコンテナーは、SQL API、MongoDB APIではコレクションと、Gremlin APIではグラフと、Cassandra API、Table APIではテーブルと同義です。

コレクションは、コレクションのスループット要件を基にして割り当てられた、多数の物理パーティションを持ちます。今日、10,000 RUに対して10パーティションを持つ可能性がありますが、明日にはこの数値が変わる可能性があります。常に必要となるスループットに集中すべきであり、割り当てられるパーティション数を気にするべきではありません。前述したように、データの使用状況によって、パーティション数は変わります。

高スケールのスループットと低レイテンシを達成するためには、データの挿入時にパーティション キー、行キーを指定し、データの読み取り時に同じパーティション キー、行キーを使う必要があります。適切なパーティション キーを選択すれば、データがすべてのパーティションにわたって均等に分散され、読み取り/書き込み操作を1桁ミリ秒で実行できるようになります。

Azure Cosmos DBは、内部でハッシュ ベースのパーティション分割を使っています。アイテムが書き込まれると、Azure Cosmos DBは、どのパーティションにアイテムを格納するかを決定するために、パーティション キー値をハッシュし、ハッシュされた結果を使います。次の図に示したように、優れたパーティション キーは、利用可能なすべてのパーティションにわたって、データを均等に分散します。

優れたパーティション キー。データが均等に分散されている

優れたパーティション キー。データが均等に分散されている

パーティション キーと物理パーティションとの間の1対1マッピングは、存在しません。これは、単一の物理パーティションが多数のパーティション キーを格納できることを意味しています。これは、一方は論理概念 (パーティション キー) であり、他方は物理概念だからです。初心者は、しばしば、パーティション キーが物理パーティションと同じだと考えてしまいます。一方は論理概念であり、他方は物理概念であり、両者は1対1にマッピングされないことを、覚えておいてください。各キーはハッシュされ、それから、モジュロ演算を使ってパーティションにマッピングされます。各論理パーティションは、10 GBのデータを格納できます。今後、この上限が変わる可能性があります。データが10 GB以上に増加すると、論理パーティションは自動的に分割されます。自分でパーティションを分割することを気にする必要はありません。Azure Cosmos DBが、舞台裏でそれを行います。ですが、10 GB以上のデータを持つ可能性があるパーティション キーを持つべきではありません。

100万個のパーティション キーが、100万個の物理パーティションを作成するわけではありません。

それでは、例を見てみましょう。あなたは、ビルに設置された温度を維持するためのIoTデバイスを持つ、IoT企業で働いており、世界中に数十万の顧客がいます。各顧客は、1分ごとに温度を更新する、数千のIoTデバイスを持っています。データがどのようなものになるかを、定義してみましょう。

{
    CustomerId: Microsoft,
    DeviceId: XYZ-23443,
    Temperature: 68
    DateTime: 32423512
}

すべての国にオフィスを持つグローバル企業で、100,000個のIoTデバイスを持っているの顧客がいると、仮定します。これらのデバイスは、1分ごとに2 KBのデータを送信し、1日の合計は2 GBになります。このペースでは、5日でパーティションが一杯になるでしょう。データを自動的に削除するために、有効期限 (TTL) メカニズムを使えますが、この例では、このデータを30日間保持しなければならないと仮定しましょう。

パーティション キーとして「CustomerId」を選択した場合、大規模顧客に対してデータ スキュー (非対称) が起こり、パーティションは次のようになります。

悪いパーティション

また、この種のパーティション分割は、数千のIoTデバイスを持ち、「CustomerId」でパーティション分割されたコレクションにデータを挿入している大規模顧客に対して、スロットリングを引き起こします。なぜスロットリングされるのか、と思うかもしれません。これを理解するには、コレクションが5,000 RU/sを持つように定義され、5つのパーティションを持っていると仮定します。これは、各パーティションのスループットが、1,000 RUになることを意味しています。

ここで5パーティションと言いましたが、ここでもこの数は議論のためのものであることに、注意してください。今後、あなたのスループットに対して、この数が変わる可能性があります。明日、ハードウェアの変更によって、5,000 RUに対して3パーティション、または1パーティションだけを持つ可能性があります。これらの物理パーティションが一定ではないことを、覚えておいてください。データが増加し続けるにつれて、物理パーティションは自動的に分割され続けます。

ユーザーは、しばしばこの間違いを犯し、5,000 RUでコレクションをプロビジョニングしたのに、2,000 RUでスロットリングされることに、不満を言います。このシナリオでは、主な問題は、データが適切にパーティション分割されておらず、1つのパーティションに2,000 RUの挿入を行おうとしていることです。これが、すべてのパーティションにわたって均等にデータを分散できる、優れたパーティション キーを持つ必要がある理由です。

「CustomerId」が優れたパーティション キーでないなら、他のどんなキーを使えるでしょうか? 「DateTime」でデータをパーティション分割したくもないでしょう。なぜなら、これは、ホット パーティションを生み出すからです。時間でデータをパーティション分割していると仮定すると、特定の1分間の間、すべての呼び出しが1つのパーティションに到達します。顧客のデータを取得する必要がある場合、データがすべてのパーティションに分散されている可能性があるので、ファン アウト クエリになります。

適切なパーティション キーを選択するには、考えて、読み取りシナリオ、または書き込みシナリオに最適化しなければなりません。より単純なシナリオでは、読み取り、書き込み両方のシナリオ向けのパーティション キーを選択することが可能です。しかし、そうではない場合は、妥協して、1つのシナリオに最適化しなければなりません。

この記事では、読み取り、書き込み両方に適したパーティション キーがないシナリオを検討していきます。読み取り、書き込み両方の要件を満たすためにできることを、見てみましょう。

このシナリオでは、データを送信するデバイスの数が多いので、書き込みに最適化しなければなりません。高速なインジェストのためのパーティション キーとして「DeviceId」を使ってコレクションを定義するのが、最善です。「DeviceId」は、一意であるだけでなく、「CustomerId」より粒度が細かいものです。すべてのパーティションにわたってデータが分散されるように、常により高いカーディナリティや一意性を備えたキーを探してください。ですが、レポートのために「CustomerId」で集約を行いたい場合は、どうなるのでしょうか?

これが、このブログ ポストの核心です。挿入シナリオのためにデータをパーティション分割したく、また、レポート シナリオのために異なるパーティション キーでデータをグループ化したいのです。残念ながら、これらは整合しない要件です。

パーティション キーとして「DeviceId」を使ってデータを挿入していると、仮定します。温度と「CustomerId」でグループ化したい場合、クエリはクロス パーティション クエリになります。クロス パーティション クエリは、頻度が少ないシナリオであれば、問題ありません。Azure Cosmos DBでは、既定ですべてのデータでインデックスが作成されているので、クロス パーティション クエリは必ずしも悪いものではありませんが、高価になる可能性があります。クロス パーティション クエリには、ポイント ルックアップよりもずっと多くのRU/sがかかります。

この問題を解決するには、2つの選択肢があります。最初の選択肢は、1時間ごとにデータを集約し、それから、集約されたデータを (「CustomerId」がパーティション キーである) 別のコレクションに格納するために、Azure Cosmos DBの変更フィードとAzure Functionsを使うことです。

変更フィード

変更フィード

さらに、1日ごとにデータを集約するために、1時間ごとのレポートのコレクションの変更フィードをリッスンし、その集約を、1日ごとのレポートのための別のAzure Cosmos DBコレクションに格納することができます。IoTデバイスは、Azure Cosmos DBにデータを直接送信します。変更フィードのおかげで、このパターンが可能になります。変更フィードは、Azure Cosmos DBのログを公開します。変更フィードは、コレクション内のドキュメントに対して行われた挿入/更新操作を含んでいます。変更フィードについて、さらに読んでください。変更フィードは、すべてのアカウント、すべてのコレクションに対して既定で有効化されていることを理解しておいてください。

変更フィードとAzure Functionsの使い方についてさらに学ぶには、このスクリーン キャストを確認してください。

2つ目の選択肢は、集約を行うためにSparkを使い、集約された値を、Azure SQL Data Warehouse、または、パーティション キーが「CustomerId」である2つ目のコレクションに保持することです。

変更フィードとSpark

この選択肢でも、変更フィードを使います。Sparkから変更フィードに直接接続し、Spark内でリアル タイムにすべての変更を取得します。データがSpark内に入ったら、集約を行い、それから、データをAzure Cosmos DB、またはSQL Data Warehouseに書き戻すことができます。

Azure Cosmos DBからデータを読み取り、集約を行い、データを書き戻すための、Spark向けのコード スニペットを示します。

# Base Configuration
iotConfig = {
"Endpoint" : "https://xx.documents.azure.com:443/",
"Masterkey" : "E0wCMaBIz==",
"Database" : "IoT",
"preferredRegions" : "Central US;East US2",
"Collection" : "IoT",
"checkpointLocation" : "dbfs://checkpointPath"
}
# Connect via Spark connector to create Spark DataFrame
iot_df = spark.read.format("com.microsoft.azure.cosmosdb.spark").options(**iotConfig).load()
iot_df.createOrReplaceTempView("c")
psql = spark.sql ("select DeviceId, CustomerId, Temp from c")

writeConfig = {
"Endpoint" : "https://xx.documents.azure.com:443/",  
"Masterkey" : "E0wCMaBKdlALwwMhg==",
"Database" : "IoT",
"preferredRegions" : "Central US;East US2",
"Collection" : "MV",
"Upsert" : "true"
     }
iot_df.createOrReplaceTempView("c")
psql = spark.sql ("select CustomerId, avg(temp) as Temp_Avg from c group by c.CustomerId ")
psql.write.format("com.microsoft.azure.cosmosdb.spark").mode('append').options(**writeConfig).save()

Azure Cosmos DBでのSparkの使い方を学ぶには、このスクリーン キャストを確認してください。

どちらの選択肢も、ライブの変更フィードをリッスンすることで、1分ごとの集約を可能にします。レポートの要件によって、別のコレクション、または同じコレクション内の別のレベルに別の集約を保持できます。これは、これらの集約値をAzure SQL Data Warehouseに保持するために持てる別の選択肢です。


広告
コメント
  1. […] translation – S/N Ratio (by SATO Naoki (Neo)) > デザイン パターン – IoTと集約 (Design patterns – IoT and aggregation)https://satonaoki.wordpress.com/2018/11/14/design-patterns-iot-and-aggregation/ […]

  2. […] デザイン パターン – IoTと集約 (Design patterns – IoT and aggregation) […]

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中