テーブルの設計パターンTable design patterns

この記事では、Table service ソリューションで使用するのに適したパターンをいくつか紹介します。This article describes some patterns appropriate for use with Table service solutions. また、他のテーブル ストレージ設計の記事で説明されている問題やトレードオフの一部に実際に対処する方法についても説明します。Also, you will see how you can practically address some of the issues and trade-offs discussed in other Table storage design articles. 次の図は、さまざまなパターンの関係をまとめたものです。The following diagram summarizes the relationships between the different patterns:

関連するデータを検索する

上記のパターン マップには、このガイドに記載されているパターン (青) とアンチパターン (オレンジ) の関係の一部が示されています。The pattern map above highlights some relationships between patterns (blue) and anti-patterns (orange) that are documented in this guide. 検討する価値があるパターンは他にもたくさんあります。There are of many other patterns that are worth considering. たとえば、Table サービス向けの主なシナリオの 1 つに、コマンド クエリ責務分離 (CQRS) パターンからの具体化されたビュー パターンの使用があります。For example, one of the key scenarios for Table Service is to use the Materialized View Pattern from the Command Query Responsibility Segregation (CQRS) pattern.

パーティション内のセカンダリ インデックス パターンIntra-partition secondary index pattern

異なる RowKey 値 (同じパーティション内) を使用して各エンティティの複数のコピーを格納し、異なる RowKey 値を使用した高速で効率的な参照と代替の並べ替え順序を可能にします。Store multiple copies of each entity using different RowKey values (in the same partition) to enable fast and efficient lookups and alternate sort orders by using different RowKey values. コピー間の更新の一貫性は、EGT を使用して保つことができます。Updates between copies can be kept consistent using EGTs.

コンテキストと問題Context and problem

Table service は PartitionKeyRowKey 値を使用して自動的にインデックスを作成します。The Table service automatically indexes entities using the PartitionKey and RowKey values. そのため、クライアント アプリケーションでは、これらの値を使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently using these values. たとえば、次のようなテーブル構造を使用することにより、クライアント アプリケーションでは、ポイント クエリを使用して部署名と従業員 ID (PartitionKeyRowKey 値) から、個々の従業員エンティティを取得できます。For example, using the table structure shown below, a client application can use a point query to retrieve an individual employee entity by using the department name and the employee ID (the PartitionKey and RowKey values). また、各部署内の従業員 ID で並べ替えたエンティティを取得することも可能です。A client can also retrieve entities sorted by employee ID within each department.

Image06

また、電子メール アドレスなど、他のプロパティの値に基づいて従業員エンティティを検索できるようにする場合は、効率の劣るパーティション スキャンを使用して、一致するエンティティを検索する必要があります。If you also want to be able to find an employee entity based on the value of another property, such as email address, you must use a less efficient partition scan to find a match. これは、Table サービスではセカンダリ インデックスが提供されないためです。This is because the table service does not provide secondary indexes. さらに、 RowKey 順以外の順序で並べ替えられた従業員の一覧を要求するオプションはありません。In addition, there is no option to request a list of employees sorted in a different order than RowKey order.

解決策Solution

セカンダリ インデックスの不足を回避するには、異なる RowKey 値を使用して各コピーの複数コピーを格納します 。To work around the lack of secondary indexes, you can store multiple copies of each entity with each copy using a different RowKey value. 次のような構造体を持つエンティティを格納する場合は、電子メール アドレスや従業員 ID に基づく複数の従業員エンティティを効率的に取得できます。If you store an entity with the structures shown below, you can efficiently retrieve employee entities based on email address or employee ID. RowKey のプレフィックス値 ("empid_" と "email_") によって、電子メール アドレスまたは従業員 ID の範囲を使用することで、1 人の従業員をクエリするかある特定の範囲の従業員をクエリできます。The prefix values for the RowKey, "empid_" and "email_" enable you to query for a single employee or a range of employees by using a range of email addresses or employee IDs.

従業員エンティティ

次の 2 つのフィルター条件 (従業員 ID で検索するフィルター条件とメール アドレスで検索するフィルター条件) ではどちらもポイント クエリを指定しています。The following two filter criteria (one looking up by employee ID and one looking up by email address) both specify point queries:

  • $filter = (PartitionKey eq 'Sales') と (RowKey eq 'empid_000223')$filter=(PartitionKey eq 'Sales') and (RowKey eq 'empid_000223')
  • $filter=(PartitionKey eq 'Sales') と (RowKey eq 'email_jonesj@contoso.com')$filter=(PartitionKey eq 'Sales') and (RowKey eq 'email_jonesj@contoso.com')

一連の複数の従業員エンティティの範囲をクエリする場合は、従業員 ID の順に並べ替えられた範囲を指定するか、適切なプレフィックスを持つエンティティのクエリを実行して、RowKeyでメール アドレスの順序で格納されている範囲を指定できます。If you query for a range of employee entities, you can specify a range sorted in employee ID order, or a range sorted in email address order by querying for entities with the appropriate prefix in the RowKey.

  • Sales 部署において、従業員 ID、000100 から 000199 を指定して、すべての従業員を検索するには: $filter=(PartitionKey eq 'Sales') and (RowKey ge 'empid_000100') and (RowKey le 'empid_000199') を使用します。To find all the employees in the Sales department with an employee ID in the range 000100 to 000199 use: $filter=(PartitionKey eq 'Sales') and (RowKey ge 'empid_000100') and (RowKey le 'empid_000199')

  • Sales 部署において、'a' で始まる電子メール アドレスを持つすべての従業員を検索するには:$filter=(PartitionKey eq 'Sales') and (RowKey ge 'email_a') and (RowKey lt 'email_b') を使用します。To find all the employees in the Sales department with an email address starting with the letter 'a' use: $filter=(PartitionKey eq 'Sales') and (RowKey ge 'email_a') and (RowKey lt 'email_b')

    上記の例で使用しているフィルター構文は、Table service REST API の構文です。詳細については、エンティティのクエリに関するページを参照してください。The filter syntax used in the examples above is from the Table service REST API, for more information, see Query Entities.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • テーブル ストレージは比較的低コストで利用できるため、重複するデータを格納してもコストは大きな問題になりません。Table storage is relatively cheap to use so the cost overhead of storing duplicate data should not be a major concern. ただし、必ず、予想されるストレージ要件に基づいて設計のコストを見積もり、クライアント アプリケーションが実行するクエリで使用するエンティティのみを複製する必要があります。However, you should always evaluate the cost of your design based on your anticipated storage requirements and only add duplicate entities to support the queries your client application will execute.

  • セカンダリ インデックス エンティティは元のエンティティと同じパーティションに格納されるため、個々のパーティションのスケーラビリティ ターゲットを超えないようにする必要があります。Because the secondary index entities are stored in the same partition as the original entities, you should ensure that you do not exceed the scalability targets for an individual partition.

  • EGT を使用してエンティティの 2 つのコピーをアトミックに更新することで、重複するエンティティどうしの一貫性を保つことができます。You can keep your duplicate entities consistent with each other by using EGTs to update the two copies of the entity atomically. そのためには、エンティティのすべてのコピーを同じパーティションに格納する必要があります。This implies that you should store all copies of an entity in the same partition. 詳細については、 エンティティ グループ トランザクションを使用するセクションを参照してください。For more information, see the section Using Entity Group Transactions.

  • RowKey に使用される値は 各エンティティに対して一意である必要があります。The value used for the RowKey must be unique for each entity. 複合キー値の使用を検討してください。Consider using compound key values.

  • 数値を RowKey (たとえば、従業員 ID 000223) にパディングすると、上限と下限に基づき正確な並べ替えとフィルタリングが可能になります。Padding numeric values in the RowKey (for example, the employee ID 000223), enables correct sorting and filtering based on upper and lower bounds.

  • 必ずしもエンティティのすべてのプロパティを複製する必要はありません。You do not necessarily need to duplicate all the properties of your entity. たとえば、電子メールアドレスを使用してエンティティを RowKey で検索するクエリの場合、従業員の年齢は不要です。このようなエンティティは、次のような構造になっている可能性があります。For example, if the queries that look up the entities using the email address in the RowKey never need the employee's age, these entities could have the following structure:

    従業員エンティティ構造

  • 通常は、エンティティの検索と必要なデータの検索にそれぞれ異なるクエリを使用するよりも、重複するデータを格納し、必要なすべてのデータを単一のクエリで取得できるようにすることをお勧めします。It is typically better to store duplicate data and ensure that you can retrieve all the data you need with a single query, than to use one query to locate an entity and another to look up the required data.

このパターンを使用する状況When to use this pattern

クライアント アプリケーションで異なるさまざまなキーを使用してエンティティを取得する必要がある場合、クライアントで異なる順序で並べ替えたエンティティを取得する必要がある場合、さまざまな一意の値を使用して各エンティティを識別できる場合に、このパターンを使用します。Use this pattern when your client application needs to retrieve entities using a variety of different keys, when your client needs to retrieve entities in different sort orders, and where you can identify each entity using a variety of unique values. ただし、異なる RowKey 値を使用してエンティティ参照を実行している場合は、パーティションのスケーラビリティの制限を超えていないことを確認してください。However, you should be sure that you do not exceed the partition scalability limits when you are performing entity lookups using the different RowKey values.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

パーティション内のセカンダリ インデックス パターンInter-partition secondary index pattern

別個のテーブルの別個のパーティションの異なるRowKey 値 (同じパーティション内) を使用して各エンティティの複数のコピーを格納し、異なる RowKey 値を使用した高速で効率的な参照と代替の並べ替え順序を可能にします。Store multiple copies of each entity using different RowKey values in separate partitions or in separate tables to enable fast and efficient lookups and alternate sort orders by using different RowKey values.

コンテキストと問題Context and problem

Table service は PartitionKeyRowKey 値を使用して自動的にインデックスを作成します。The Table service automatically indexes entities using the PartitionKey and RowKey values. そのため、クライアント アプリケーションでは、これらの値を使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently using these values. たとえば、次のようなテーブル構造を使用することにより、クライアント アプリケーションでは、ポイント クエリを使用して部署名と従業員 ID (PartitionKeyRowKey 値) から、個々の従業員エンティティを取得できます。For example, using the table structure shown below, a client application can use a point query to retrieve an individual employee entity by using the department name and the employee ID (the PartitionKey and RowKey values). また、各部署内の従業員 ID で並べ替えたエンティティを取得することも可能です。A client can also retrieve entities sorted by employee ID within each department.

従業員 ID

また、電子メール アドレスなど、他のプロパティの値に基づいて従業員エンティティを検索できるようにする場合は、効率の劣るパーティション スキャンを使用して、一致するエンティティを検索する必要があります。If you also want to be able to find an employee entity based on the value of another property, such as email address, you must use a less efficient partition scan to find a match. これは、Table サービスではセカンダリ インデックスが提供されないためです。This is because the table service does not provide secondary indexes. さらに、 RowKey 順以外の順序で並べ替えられた従業員の一覧を要求するオプションはありません。In addition, there is no option to request a list of employees sorted in a different order than RowKey order.

これらのエンティティに対するトランザクションが大量になることが予想される場合は、Table service によってクライアントが調整されるリスクを最小限に抑える必要があります。You are anticipating a high volume of transactions against these entities and want to minimize the risk of the Table service throttling your client.

解決策Solution

セカンダリ インデックスの不足を回避するには、異なるPartitionKeyRowKey 値を使用して各コピーの複数コピーを格納します。To work around the lack of secondary indexes, you can store multiple copies of each entity with each copy using different PartitionKey and RowKey values. 次のような構造体を持つエンティティを格納する場合は、電子メール アドレスや従業員 ID に基づく複数の従業員エンティティを効率的に取得できます。If you store an entity with the structures shown below, you can efficiently retrieve employee entities based on email address or employee ID. PartitionKey のプレフィックスの値、"empid_" と "email_" で、クエリに使用するインデックスを特定することができます。The prefix values for the PartitionKey, "empid_" and "email_" enable you to identify which index you want to use for a query.

プライマリ インデックスとセカンダリ インデックス

次の 2 つのフィルター条件 (従業員 ID で検索するフィルター条件とメール アドレスで検索するフィルター条件) ではどちらもポイント クエリを指定しています。The following two filter criteria (one looking up by employee ID and one looking up by email address) both specify point queries:

  • $filter=(PartitionKey eq 'empid_Sales') and (RowKey eq '000223')$filter=(PartitionKey eq 'empid_Sales') and (RowKey eq '000223')
  • $filter=(PartitionKey eq 'email_Sales') と (RowKey eq 'jonesj@contoso.com')$filter=(PartitionKey eq 'email_Sales') and (RowKey eq 'jonesj@contoso.com')

一連の複数の従業員エンティティの範囲をクエリする場合は、従業員 ID の順に並べ替えられた範囲を指定するか、適切なプレフィックスを持つエンティティのクエリを実行して、RowKeyでメール アドレスの順序で格納されている範囲を指定できます。If you query for a range of employee entities, you can specify a range sorted in employee ID order, or a range sorted in email address order by querying for entities with the appropriate prefix in the RowKey.

  • 従業員 ID 順で格納された、従業員 ID が 000100 から 000199 の範囲の Sales 部署のすべての従業員を検索するには、$filter=(PartitionKey eq 'empid_Sales') と (RowKey ge '000100') および (RowKey le '000199') を使用します。To find all the employees in the Sales department with an employee ID in the range 000100 to 000199 sorted in employee ID order use: $filter=(PartitionKey eq 'empid_Sales') and (RowKey ge '000100') and (RowKey le '000199')
  • Sales 部署において、電子メール アドレス順で格納された電子メール アドレスで、'a' で始まる電子メール アドレスを持つすべての従業員を検索するには: $filter=(PartitionKey eq 'email_Sales') and (RowKey ge 'a') and (RowKey lt 'b') を使用します。To find all the employees in the Sales department with an email address that starts with 'a' sorted in email address order use: $filter=(PartitionKey eq 'email_Sales') and (RowKey ge 'a') and (RowKey lt 'b')

上記の例で使用しているフィルター構文は、Table service REST API の構文です。詳細については、エンティティのクエリに関するページを参照してください。The filter syntax used in the examples above is from the Table service REST API, for more information, see Query Entities.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • プライマリとセカンダリ インデックスのエンティティを維持するため、重複するエンティティを保持して、 最終的に一貫性のあるトランザクション パターン を使用し、互いに最終的に一貫性を持たせます。You can keep your duplicate entities eventually consistent with each other by using the Eventually consistent transactions pattern to maintain the primary and secondary index entities.

  • テーブル ストレージは比較的低コストで利用できるため、重複するデータを格納してもコストは大きな問題になりません。Table storage is relatively cheap to use so the cost overhead of storing duplicate data should not be a major concern. ただし、必ず、予想されるストレージ要件に基づいて設計のコストを見積もり、クライアント アプリケーションが実行するクエリで使用するエンティティのみを複製する必要があります。However, you should always evaluate the cost of your design based on your anticipated storage requirements and only add duplicate entities to support the queries your client application will execute.

  • RowKey に使用される値は 各エンティティに対して一意である必要があります。The value used for the RowKey must be unique for each entity. 複合キー値の使用を検討してください。Consider using compound key values.

  • 数値を RowKey (たとえば、従業員 ID 000223) にパディングすると、上限と下限に基づき正確な並べ替えとフィルタリングが可能になります。Padding numeric values in the RowKey (for example, the employee ID 000223), enables correct sorting and filtering based on upper and lower bounds.

  • 必ずしもエンティティのすべてのプロパティを複製する必要はありません。You do not necessarily need to duplicate all the properties of your entity. たとえば、電子メールアドレスを使用してエンティティを RowKey で検索するクエリの場合、従業員の年齢は不要です。このようなエンティティは、次のような構造になっている可能性があります。For example, if the queries that look up the entities using the email address in the RowKey never need the employee's age, these entities could have the following structure:

    従業員エンティティ (セカンダリ インデックス)

  • 通常は、セカンダリ インデックスを使用したエンティティの検索とプライマリ インデックス内の必要なデータの検索にそれぞれ異なるクエリを使用するよりも、重複するデータを格納し、必要なすべてのデータを単一のクエリで取得できるようにすることをお勧めします。It is typically better to store duplicate data and ensure that you can retrieve all the data you need with a single query than to use one query to locate an entity using the secondary index and another to look up the required data in the primary index.

このパターンを使用する状況When to use this pattern

クライアント アプリケーションで異なるさまざまなキーを使用してエンティティを取得する必要がある場合、クライアントで異なる順序で並べ替えたエンティティを取得する必要がある場合、さまざまな一意の値を使用して各エンティティを識別できる場合に、このパターンを使用します。Use this pattern when your client application needs to retrieve entities using a variety of different keys, when your client needs to retrieve entities in different sort orders, and where you can identify each entity using a variety of unique values. 異なる RowKey 値を使用してエンティティ参照を実行しており、パーティションのスケーラビリティの制限を超えないようにしたい場合、このパターンを使用します 。Use this pattern when you want to avoid exceeding the partition scalability limits when you are performing entity lookups using the different RowKey values.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

最終的に一貫性のあるトランザクション パターンEventually consistent transactions pattern

Azure キューを使用して、パーティションやストレージ システムの境界を越えて、最終的に一貫した動作を実現します。Enable eventually consistent behavior across partition boundaries or storage system boundaries by using Azure queues.

コンテキストと問題Context and problem

EGT を使用すると、同じパーティション キーを共有する複数のエンティティに対してアトミックなトランザクションを実行できます。EGTs enable atomic transactions across multiple entities that share the same partition key. パフォーマンスやスケーラビリティの関係で、一貫性が必要なエンティティを別々のパーティションや別のストレージ システムに格納する場合があります。そのような場合は、EGT を使用して一貫性を保つことはできません。For performance and scalability reasons, you might decide to store entities that have consistency requirements in separate partitions or in a separate storage system: in such a scenario, you cannot use EGTs to maintain consistency. たとえば、次の一貫性を最終的に確保する必要があるとします。For example, you might have a requirement to maintain eventual consistency between:

  • 同じテーブル内の 2 つの異なるパーティション、異なるテーブル、異なるストレージ アカウントに格納されているエンティティ。Entities stored in two different partitions in the same table, in different tables, or in different storage accounts.
  • Table service に格納されているエンティティと Blob service に格納されている BLOB。An entity stored in the Table service and a blob stored in the Blob service.
  • Table service に格納されているエンティティとファイル システム内のファイル。An entity stored in the Table service and a file in a file system.
  • Table service に格納されているにもかかわらず、Azure Cognitive Search サービスを使用してインデックスが作成されているエンティティ。An entity stored in the Table service yet indexed using the Azure Cognitive Search service.

解決策Solution

Azure キューを使用すると、2 つ以上のパーティションまたはストレージ システム間で最終的に一貫性を確保するソリューションを実装できます。By using Azure queues, you can implement a solution that delivers eventual consistency across two or more partitions or storage systems. この方法を説明するために、退職した従業員エンティティをアーカイブできるようにする必要があるという場合を考えてみましょう。To illustrate this approach, assume you have a requirement to be able to archive old employee entities. 退職した従業員エンティティはめったに照会されず、現在の従業員を対象にしたすべてのアクティビティから除外する必要があります。Old employee entities are rarely queried and should be excluded from any activities that deal with current employees. この要件を実装するには、現在テーブルにいる現在の従業員と、アーカイブ テーブルにいる退職した従業員を格納します。To implement this requirement, you store active employees in the Current table and old employees in the Archive table. 従業員をアーカイブするには、現在テーブルからのエンティティを削除し、アーカイブ テーブルにエンティティを追加する必要がありますが、これら 2 つの操作を実行する EGT は使用できません。Archiving an employee requires you to delete the entity from the Current table and add the entity to the Archive table, but you cannot use an EGT to perform these two operations. エンティティが両方のテーブルに表示されることや、どちらのテーブルにも表示されないことがないように、アーカイブ操作は最終的に一貫性が確保される必要があります。To avoid the risk that a failure causes an entity to appear in both or neither tables, the archive operation must be eventually consistent. 次のシーケンス図は、この操作の大まかな手順を示しています。The following sequence diagram outlines the steps in this operation. その下のテキストには、例外パスの詳細が示されています。More detail is provided for exception paths in the text following.

Azure キュー ソリューション

クライアントは、Azure キューにメッセージを配置することによって、アーカイブ操作を開始します。この例では、ID が 456 の従業員をアーカイブします。A client initiates the archive operation by placing a message on an Azure queue, in this example to archive employee #456. worker ロールは、キューをポーリングして新しいメッセージの有無を確認します。メッセージを見つけると、そのメッセージを読み取り、隠しコピーをキューに残します。A worker role polls the queue for new messages; when it finds one, it reads the message and leaves a hidden copy on the queue. worker ロールは、次に、現在テーブルからコピーをフェッチし、アーカイブ テーブルにコピーを挿入し、その後、元のデータを現在テーブルから削除します。The worker role next fetches a copy of the entity from the Current table, inserts a copy in the Archive table, and then deletes the original from the Current table. 最後に、前の手順でエラーが発生しなければ、worker ロールはキューから隠しメッセージを削除します。Finally, if there were no errors from the previous steps, the worker role deletes the hidden message from the queue.

この例では、ステップ 4 で従業員を アーカイブ テーブルに挿入しています。In this example, step 4 inserts the employee into the Archive table. Blob service の BLOB またはファイル システム内のファイルに従業員を加えることもできます。It could add the employee to a blob in the Blob service or a file in a file system.

エラーからの回復Recovering from failures

worker ロールがアーカイブ操作を再開する必要がある場合、手順 45 の操作がべき等になっていることが重要です。It is important that the operations in steps 4 and 5 must be idempotent in case the worker role needs to restart the archive operation. Table service を使用している場合、手順 4 で「挿入または置換」操作を使用する必要があります。手順 5 では、使用しているクライアント ライブラリ で "存在する場合は削除" 操作を使用する必要があります。If you are using the Table service, for step 4 you should use an "insert or replace" operation; for step 5 you should use a "delete if exists" operation in the client library you are using. 他のストレージ システムを使用する場合は、適切なべき等操作を使用する必要があります。If you are using another storage system, you must use an appropriate idempotent operation.

worker ロールが手順 6を完了しない場合は、タイムアウトの後、メッセージが worker ロール準備完了のキューに表示され再処理を試みます。If the worker role never completes step 6, then after a timeout the message reappears on the queue ready for the worker role to try to reprocess it. worker ロールは、キュー上のメッセージを読み取った回数を確認し、必要に応じて、別のキューに送信することで、調査のために "有害" メッセージとしてフラグを設定できます。The worker role can check how many times a message on the queue has been read and, if necessary, flag it is a "poison" message for investigation by sending it to a separate queue. キュー メッセージの読み取りとデキューカウントのチェックに関する詳細については、 メッセージを取得を参照してください。For more information about reading queue messages and checking the dequeue count, see Get Messages.

Table サービスと Queue サービスのエラーには一時的なエラーもあります。クライアント アプリケーションには、そうしたエラーに対処する適切な再試行ロジックを組み込む必要があります。Some errors from the Table and Queue services are transient errors, and your client application should include suitable retry logic to handle them.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • このソリューションは、トランザクションを分離するためのソリューションではありません。This solution does not provide for transaction isolation. たとえば、worker ロールが手順 45 の間で、クライアントは現在アーカイブ テーブルを読み込むことができます。また、データの非一貫性表示を参照できます。For example, a client could read the Current and Archive tables when the worker role was between steps 4 and 5, and see an inconsistent view of the data. データは最終的に一貫性が確保されます。The data will be consistent eventually.
  • 最終的に一貫性を確保するために、手順 4 と 5 がべき等になっていることを確認する必要があります。You must be sure that steps 4 and 5 are idempotent in order to ensure eventual consistency.
  • 複数のキューと worker ロール インスタンスを使用して、ソリューションを拡張できます。You can scale the solution by using multiple queues and worker role instances.

このパターンを使用する状況When to use this pattern

別のパーティションまたはテーブルに存在するエンティティ間の一貫性を最終的に確保する必要がある場合に、このパターンを使用します。Use this pattern when you want to guarantee eventual consistency between entities that exist in different partitions or tables. このパターンを拡張して、Table service と Blob service のほかにも、データベースやファイル システムなどの Azure 以外のストレージ データ ソース間の操作で最終的な一貫性を確保できます。You can extend this pattern to ensure eventual consistency for operations across the Table service and the Blob service and other non-Azure Storage data sources such as database or the file system.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

注意

ソリューションにとってトランザクションの分離が重要な場合は、EGT を使用できるようにテーブルを再設計することを検討する必要があります。If transaction isolation is important to your solution, you should consider redesigning your tables to enable you to use EGTs.

インデックス エンティティのパターンIndex entities pattern

エンティティ一覧を返す、効率的な検索を有効にするインデックスのエンティティを管理します。Maintain index entities to enable efficient searches that return lists of entities.

コンテキストと問題Context and problem

Table service は PartitionKeyRowKey 値を使用して自動的にインデックスを作成します。The Table service automatically indexes entities using the PartitionKey and RowKey values. そうすると、クライアント アプリケーションでポイント クエリを使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently using a point query. たとえば、次のようなテーブル構造を使用することにより、クライアント アプリケーションでは、部署名と従業員 ID (PartitionKeyRowKey 値) から、個々の従業員エンティティを効率的に取得できます。For example, using the table structure shown below, a client application can efficiently retrieve an individual employee entity by using the department name and the employee ID (the PartitionKey and RowKey).

従業員エンティティ

また、姓など、一意ではない他のプロパティの値に基づいて従業員エンティティの一覧を取得できるようにする場合は、インデックスを使用して直接一致するエンティティを検索せずに、効率の劣るパーティション スキャンを使用して検索する必要があります。If you also want to be able to retrieve a list of employee entities based on the value of another non-unique property, such as their last name, you must use a less efficient partition scan to find matches rather than using an index to look them up directly. これは、Table サービスではセカンダリ インデックスが提供されないためです。This is because the table service does not provide secondary indexes.

解決策Solution

上のエンティティ構造の場合、姓で検索できるようにするには、従業員 ID の一覧を保持する必要があります。To enable lookup by last name with the entity structure shown above, you must maintain lists of employee IDs. Jones など、特定の姓を持つ従業員エンティティを取得するには、まず姓が Jones である従業員の従業員 ID の一覧を検索してから、それらの従業員エンティティを取得する必要があります。If you want to retrieve the employee entities with a particular last name, such as Jones, you must first locate the list of employee IDs for employees with Jones as their last name, and then retrieve those employee entities. 従業員 ID の一覧を格納する方法は主に次の 3 つがあります。There are three main options for storing the lists of employee IDs:

  • BLOB ストレージを使用する。Use blob storage.
  • 従業員エンティティと同じパーティションにインデックス エンティティを作成する。Create index entities in the same partition as the employee entities.
  • 別のパーティションまたはテーブルにインデックス エンティティを作成する。Create index entities in a separate partition or table.

オプション 1: BLOB ストレージの使用Option #1: Use blob storage

最初のオプションでは、すべての一意の姓について Blob を作成し、その姓の従業員用の各 Blob には PartitionKey (部署) と RowKey (従業員 ID) 値が格納されます。For the first option, you create a blob for every unique last name, and in each blob store a list of the PartitionKey (department) and RowKey (employee ID) values for employees that have that last name. 従業員を追加または削除した場合は、関連する BLOB の内容と従業員エンティティの一貫性が最終的に確保されていることを確認する必要があります。When you add or delete an employee, you should ensure that the content of the relevant blob is eventually consistent with the employee entities.

オプション 2: 同じパーティション内のインデックス エンティティの作成Option #2: Create index entities in the same partition

2 番目の方法では、以下のデータを格納するインデックス エンティティを使用します。For the second option, use index entities that store the following data:

従業員インデックス エンティティ

EmployeeIDs プロパティには、RowKey に格納されている姓を持つ従業員の従業員 ID リストが含まれています。The EmployeeIDs property contains a list of employee IDs for employees with the last name stored in the RowKey.

次の手順は、2 番目の方法を使用した場合に、新しい従業員を追加するときに従う必要がある手順の概要を示しています。The following steps outline the process you should follow when you are adding a new employee if you are using the second option. この例では、ID が 000152、姓が Jones の従業員を Sales 部署に追加します。In this example, we are adding an employee with ID 000152 and a last name Jones in the Sales department:

  1. PartitionKey 値 "Sales" と RowKey 値 "Jones"を持つインデックスのエンティティを取得します。Retrieve the index entity with a PartitionKey value "Sales" and the RowKey value "Jones." このエンティティの ETag を、手順 2. で使用するために保存します。Save the ETag of this entity to use in step 2.
  2. 新しい従業員エンティティを挿入するエンティティ グループ トランザクション、つまり、バッチ操作 (PartitionKey 値 "Sales" と RowKey 値 "000152") を作成し、新しい従業員 ID を従業員 ID フィールドに追加することでインデックス エンティティ (PartitionKey 値 "Sales" と RowKey 値 "Jones") を更新します。Create an entity group transaction (that is, a batch operation) that inserts the new employee entity (PartitionKey value "Sales" and RowKey value "000152"), and updates the index entity (PartitionKey value "Sales" and RowKey value "Jones") by adding the new employee ID to the list in the EmployeeIDs field. エンティティ グループ トランザクションの詳細については、「エンティティ グループ トランザクション」をご覧ください。For more information about entity group transactions, see Entity Group Transactions.
  3. オプティミスティック コンカレンシー エラー (他のユーザーがインデックス エンティティを変更したこと) が原因でエンティティ グループ トランザクションが失敗した場合は、手順 1. からまたやり直す必要があります。If the entity group transaction fails because of an optimistic concurrency error (someone else has just modified the index entity), then you need to start over at step 1 again.

2 番目の方法を使用する場合は、同じような方法で従業員を削除できます。You can use a similar approach to deleting an employee if you are using the second option. 従業員の姓を変更するのは、3 つのエンティティ (従業員エンティティ、元の姓のインデックス エンティティ、新しい姓のインデックス エンティティ) を更新するエンティティ グループ トランザクションを実行する必要があるため、少し複雑です。Changing an employee's last name is slightly more complex because you will need to execute an entity group transaction that updates three entities: the employee entity, the index entity for the old last name, and the index entity for the new last name. 変更を加える前に、各エンティティを取得して、ETag 値を取得する必要があります。その ETag 値を使用して、オプティミスティック コンカレンシーで更新を実行できます。You must retrieve each entity before making any changes in order to retrieve the ETag values that you can then use to perform the updates using optimistic concurrency.

次の手順は、2 番目の方法を使用した場合に、ある部署で特定の姓を持つすべての従業員を検索する必要があるときに従う必要がある手順の概要を示しています。The following steps outline the process you should follow when you need to look up all the employees with a given last name in a department if you are using the second option. この例では、Sales 部署で姓が Jones のすべての従業員を検索します。In this example, we are looking up all the employees with last name Jones in the Sales department:

  1. PartitionKey 値 "Sales" と RowKey 値 "Jones"を持つインデックスのエンティティを取得します。Retrieve the index entity with a PartitionKey value "Sales" and the RowKey value "Jones."
  2. EmployeeIDs フィールドで従業員 ID の一覧を解析します。Parse the list of employee Ids in the EmployeeIDs field.
  3. 各従業員に関する追加情報 (電子メール アドレスなど) が必要な場合は、手順 2 で取得した従業員リストから PartitionKey 値 "Sales" と RowKey 値を使用して各従業員のエンティティを取得します。If you need additional information about each of these employees (such as their email addresses), retrieve each of the employee entities using PartitionKey value "Sales" and RowKey values from the list of employees you obtained in step 2.

オプション 3: 別のパーティションまたはテーブルにインデックス エンティティを作成するOption #3: Create index entities in a separate partition or table

3 番目の方法では、以下のデータを格納するインデックス エンティティを使用します。For the third option, use index entities that store the following data:

別のパーティションにある従業員インデックス エンティティ

EmployeeIDs プロパティには、RowKey に格納されている姓を持つ従業員の従業員 ID リストが含まれています。The EmployeeIDs property contains a list of employee IDs for employees with the last name stored in the RowKey.

3 番目の方法では、インデックス エンティティが従業員エンティティとは別のパーティションにあるため、EGT を使用して一貫性を保つことはできません。With the third option, you cannot use EGTs to maintain consistency because the index entities are in a separate partition from the employee entities. インデックス エンティティが従業員エンティティと最終的に一貫していることを確認してください。Ensure that the index entities are eventually consistent with the employee entities.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • このソリューションでは、一致するエンティティを取得するために、少なくとも 2 つのクエリが必要です。1 つはインデックス エンティティを照会して RowKey 値の一覧を取得するクエリです。もう 1 つはその一覧内の各エンティティを取得するクエリです。This solution requires at least two queries to retrieve matching entities: one to query the index entities to obtain the list of RowKey values, and then queries to retrieve each entity in the list.
  • 個々のエンティティの最大サイズは 1 MB であるため、ソリューションの方法 2 と方法 3 では、特定の姓の従業員 ID の一覧が 1 MB を超えることがないと仮定しています。Given that an individual entity has a maximum size of 1 MB, option #2 and option #3 in the solution assume that the list of employee IDs for any given last name is never greater than 1 MB. 従業員 ID の一覧のサイズが 1 MB を超える可能性がある場合は、方法 1 を使用して、BLOB ストレージにインデックス データを格納します。If the list of employee IDs is likely to be greater than 1 MB in size, use option #1 and store the index data in blob storage.
  • 方法 2 を使用する (EGT を使用して、従業員の追加と削除、従業員の姓の変更を処理する) 場合は、トランザクションの量が特定のパーティションのスケーラビリティの限界に近づくかどうかを確認する必要があります。If you use option #2 (using EGTs to handle adding and deleting employees, and changing an employee's last name) you must evaluate if the volume of transactions will approach the scalability limits in a given partition. 限界に近づく場合は、キューを使用して更新要求を処理し、従業員エンティティとは別のパーティションにインデックス エンティティを格納でき、最終的に一貫性が確保されるソリューション (方法 1 または方法 3) を検討する必要があります。If this is the case, you should consider an eventually consistent solution (option #1 or option #3) that uses queues to handle the update requests and enables you to store your index entities in a separate partition from the employee entities.
  • このソリューションの方法 2 では、部署内を姓で検索する (たとえば、Sales 部署で姓が Jones の従業員の一覧を取得する) 必要があると想定しています。Option #2 in this solution assumes that you want to look up by last name within a department: for example, you want to retrieve a list of employees with a last name Jones in the Sales department. 組織全体で姓が Jones のすべての従業員を検索できる必要がある場合は、方法 1 と方法 3 のどちらかを使用します。If you want to be able to look up all the employees with a last name Jones across the whole organization, use either option #1 or option #3.
  • 最終的に一貫性が確保されるキューベースのソリューションを実装できます (詳細については、「 最終的に一貫性のあるトランザクション パターン 」を参照してください)。You can implement a queue-based solution that delivers eventual consistency (see the Eventually consistent transactions pattern for more details).

このパターンを使用する状況When to use this pattern

姓が Jones のすべての従業員など、特定のプロパティ値がすべて共通している一連のエンティティを検索する場合に、このパターンを使用します。Use this pattern when you want to look up a set of entities that all share a common property value, such as all employees with the last name Jones.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

非正規化パターンDenormalization pattern

関連するデータを 1 つのエンティティに結合し、1 回のポイント クエリで必要なデータをすべて取得できるようにします。Combine related data together in a single entity to enable you to retrieve all the data you need with a single point query.

コンテキストと問題Context and problem

リレーショナル データベースでは、通常、重複を排除するためにデータを正規化します。その結果、クエリで複数のテーブルからデータを取得することになります。In a relational database, you typically normalize data to remove duplication resulting in queries that retrieve data from multiple tables. Azure テーブルのデータを正規化した場合、関連するデータを取得するには、クライアント アプリケーションとサーバー間のラウンド トリップを複数回行う必要があります。If you normalize your data in Azure tables, you must make multiple round trips from the client to the server to retrieve your related data. たとえば、以下のテーブル構造を使用した場合、部署の詳細を取得するためには、2 回ラウンド トリップを行う必要があります。1 回目のラウンド トリップでマネージャーの ID を含む部署エンティティをフェッチし、2 回目に従業員エンティティからマネージャーの詳細をフェッチします。For example, with the table structure shown below you need two round trips to retrieve the details for a department: one to fetch the department entity that includes the manager's ID, and then another request to fetch the manager's details in an employee entity.

部署エンティティと従業員エンティティ

解決策Solution

データを 2 つのエンティティに格納する代わりに、データを非正規化し、部署エンティティにマネージャーの詳細のコピーを保持します。Instead of storing the data in two separate entities, denormalize the data and keep a copy of the manager's details in the department entity. 次に例を示します。For example:

部署エンティティ

格納されている部署エンティティにはこれらのプロパティがあるため、ポイント クエリを使用して、部署に関して必要なすべての詳細を取得できます。With department entities stored with these properties, you can now retrieve all the details you need about a department using a point query.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 一部のデータを重複して格納するため、多少コストがかかります。There is some cost overhead associated with storing some data twice. 通常、ストレージ コストの増加はわずかなため、(ストレージ サービスへの要求が減少することによる) パフォーマンス上のメリットが勝ります (このコストの一部は、部署の詳細をフェッチするために必要なトランザクションの数が減少することで相殺されます)。The performance benefit (resulting from fewer requests to the storage service) typically outweighs the marginal increase in storage costs (and this cost is partially offset by a reduction in the number of transactions you require to fetch the details of a department).
  • マネージャーに関する情報を格納する 2 つのエンティティの一貫性を維持する必要があります。You must maintain the consistency of the two entities that store information about managers. 一貫性の問題は、EGT を使用して単一のアトミックなトランザクションで複数のエンティティを更新することで対処できます。この例では、部署エンティティと、部署マネージャーの従業員エンティティが同じパーティションに格納されています。You can handle the consistency issue by using EGTs to update multiple entities in a single atomic transaction: in this case, the department entity, and the employee entity for the department manager are stored in the same partition.

このパターンを使用する状況When to use this pattern

関連情報を頻繁に検索する必要がある場合に、このパターンを使用します。Use this pattern when you frequently need to look up related information. このパターンを使用すると、クライアントが必要なデータを取得するために実行する必要があるクエリの数が減少します。This pattern reduces the number of queries your client must make to retrieve the data it requires.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

複合キー パターンCompound key pattern

クライアントで単一のポイント クエリを使用して関連するデータを検索できるようにするには、複合 RowKey 値を使用します。Use compound RowKey values to enable a client to look up related data with a single point query.

コンテキストと問題Context and problem

リレーショナル データベースでは、単一のクエリで関連するデータをクライアントに返すために、クエリでよく結合を使用します。In a relational database, it is natural to use joins in queries to return related pieces of data to the client in a single query. たとえば、従業員 ID を使用して、その従業員の業績と評価データが含まれている関連エンティティの一覧を検索する場合があります。For example, you might use the employee ID to look up a list of related entities that contain performance and review data for that employee.

次の構造を使用し、Table service に従業員エンティティを格納しているとします。Assume you are storing employee entities in the Table service using the following structure:

従業員エンティティ構造

また、各年度の従業員の評価と業績に関する履歴データを格納し、この情報に年度別でアクセスできる必要もあります。You also need to store historical data relating to reviews and performance for each year the employee has worked for your organization and you need to be able to access this information by year. それには、次の構造でエンティティを格納する別のテーブルを作成するという方法があります。One option is to create another table that stores entities with the following structure:

代替の従業員エンティティ構造

この方法では、単一の要求でデータを取得できるようにするには、一部の情報 (姓や名など) を新しいエンティティに複製する必要があります。Notice that with this approach you may decide to duplicate some information (such as first name and last name) in the new entity to enable you to retrieve your data with a single request. ただし、EGT を使用しても 2 つのエンティティをアトミックには更新できないため、強力な整合性を保つことはできません。However, you cannot maintain strong consistency because you cannot use an EGT to update the two entities atomically.

解決策Solution

次の構造のエンティティを使用して、元のテーブルに新しい種類のエンティティを格納します。Store a new entity type in your original table using entities with the following structure:

従業員エンティティ構造のソリューション

RowKey が従業員 ID と評価データの年度から構成された複合キーとなり、1 つのエンティティに対する 1 つのリクエストで、従業員の業績と評価データを取得できるようになっていることにご注意ください。Notice how the RowKey is now a compound key made up of the employee ID and the year of the review data that enables you to retrieve the employee's performance and review data with a single request for a single entity.

次の例では、Sales 部署の従業員 000123 など、特定の従業員のすべての評価データを取得する方法を示しています。The following example outlines how you can retrieve all the review data for a particular employee (such as employee 000123 in the Sales department):

$filter=(PartitionKey eq 'Sales')、(RowKey ge 'empid_000123')、(RowKey lt 'empid_000124')&$select=RowKey,Manager Rating,Peer Rating,Comments$filter=(PartitionKey eq 'Sales') and (RowKey ge 'empid_000123') and (RowKey lt 'empid_000124')&$select=RowKey,Manager Rating,Peer Rating,Comments

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 000123_2012 のような、RowKey 値の解析を容易にする適切な区切り文字を使用する必要があります。You should use a suitable separator character that makes it easy to parse the RowKey value: for example, 000123_2012.
  • また、このエンティティは、同じ従業員の関連データを含む他のエンティティと同じパーティションに格納します。そうすると、EGT を使用して、強い整合性を維持できます。You are also storing this entity in the same partition as other entities that contain related data for the same employee, which means you can use EGTs to maintain strong consistency.
  • このパターンが適切であるかどうかを判断するには、データを照会する頻度を考慮する必要があります。You should consider how frequently you will query the data to determine whether this pattern is appropriate. たとえば、評価データにはあまり頻度にアクセスせず、メインの従業員データには頻度にアクセスする場合は、それらのデータを別々のエンティティとして保持する必要があります。For example, if you will access the review data infrequently and the main employee data often you should keep them as separate entities.

このパターンを使用する状況When to use this pattern

頻繁に照会する関連エンティティを 1 つ以上格納する必要がある場合に、このパターンを使用します。Use this pattern when you need to store one or more related entities that you query frequently.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

ログ テール パターンLog tail pattern

逆の日付と時間順でソートする RowKey 値を 使用して最も最近追加された n を取得します。Retrieve the n entities most recently added to a partition by using a RowKey value that sorts in reverse date and time order.

コンテキストと問題Context and problem

一般的な要件は、最近作成されたエンティティ (たとえば、従業員から最近提出された 10 件の経費請求) を取得できるようにすることです。A common requirement is been able to retrieve the most recently created entities, for example the 10 most recent expense claims submitted by an employee. Table クエリは $top クエリ操作をサポートして、最初の n 件のエンティティをセットから返します。セットの最終 n 件のエンティティを返す同等のクエリの操作はありません。Table queries support a $top query operation to return the first n entities from a set: there is no equivalent query operation to return the last n entities in a set.

解決策Solution

最新のエントリを逆の日付と時刻の順で自然に並べ替える RowKey を使用したエンティティを保存することで、表の一番上に直近のエントリが表示されます。Store the entities using a RowKey that naturally sorts in reverse date/time order by using so the most recent entry is always the first one in the table.

たとえば、従業員から最近提出された10 件の経費請求を取得できるようにするには、現在の日時から算出された逆順のティック値を使用できます。For example, to be able to retrieve the 10 most recent expense claims submitted by an employee, you can use a reverse tick value derived from the current date/time. 次の c# のコード サンプルは、最新から最古の順で並べ替える RowKey 用の適切な「逆タイマー刻み」値を作成する方法の 1 つを示しています。The following C# code sample shows one way to create a suitable "inverted ticks" value for a RowKey that sorts from the most recent to the oldest:

string invertedTicks = string.Format("{0:D19}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);

次のコードを使用すると、日時値に戻すことができます。You can get back to the date time value using the following code:

DateTime dt = new DateTime(DateTime.MaxValue.Ticks - Int64.Parse(invertedTicks));

テーブル クエリは次のようになります。The table query looks like this:

https://myaccount.table.core.windows.net/EmployeeExpense(PartitionKey='empid')?$top=10

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 文字列値が正しく並び替わるように、逆順のティック値の先頭にゼロをパディングする必要があります。You must pad the reverse tick value with leading zeroes to ensure the string value sorts as expected.
  • パーティション レベルのスケーラビリティ ターゲットに注意する必要があります。You must be aware of the scalability targets at the level of a partition. ホット スポット パーティションが発生しないように注意してください。Be careful not create hot spot partitions.

このパターンを使用する状況When to use this pattern

日時の逆順でエンティティにアクセスする必要がある場合、または追加日時の新しい順にエンティティにアクセスする必要がある場合に、このパターンを使用します。Use this pattern when you need to access entities in reverse date/time order or when you need to access the most recently added entities.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

大量削除パターンHigh volume delete pattern

すべてのエンティティを同時削除用に独立したテーブルに格納することで、大量のエンティティを削除できるようにします。エンティティを削除するときは、テーブル自体を削除することになります。Enable the deletion of a high volume of entities by storing all the entities for simultaneous deletion in their own separate table; you delete the entities by deleting the table.

コンテキストと問題Context and problem

多くのアプリケーションでは、クライアント アプリケーションで使用する必要がなくなった古いデータや、他の記憶域メディアにアーカイブした古いデータを削除します。Many applications delete old data that no longer needs to be available to a client application, or that the application has archived to another storage medium. 通常は、日付でそうしたデータを特定します。たとえば、60 日以上前のすべてのログイン要求のレコードを削除する必要があるとします。You typically identify such data by a date: for example, you have a requirement to delete records of all login requests that are more than 60 days old.

利用可能な設計の 1 つは、 RowKeyでのログイン リクエストの日付と時刻の使用です:One possible design is to use the date and time of the login request in the RowKey:

ログイン試行の日付と時刻

この方法では、アプリケーションが別のパーティションで各ユーザーのログイン エンティティを挿入したり削除したりできるため、パーティションのホット スポットを回避できます。This approach avoids partition hotspots because the application can insert and delete login entities for each user in a separate partition. ただし、まず削除するすべてのエンティティを特定するためにテーブル スキャンを実行し、その後、古い各エンティティを削除する必要があるため、エンティティの数が多い場合、この方法ではコストと時間がかかる可能性があります。However, this approach may be costly and time consuming if you have a large number of entities because first you need to perform a table scan in order to identify all the entities to delete, and then you must delete each old entity. 複数の削除要求をバッチ処理として EGT にまとめることで、古いエンティティを削除するのに必要なサーバーへのラウンド トリップの回数を減らすことができます。You can reduce the number of round trips to the server required to delete the old entities by batching multiple delete requests into EGTs.

解決策Solution

ログイン試行の日付ごとに異なるテーブルを使用します。Use a separate table for each day of login attempts. 上のエンティティのデザインを使用すると、エンティティを挿入する際にホットスポットを回避できます。毎日数百や数千もの個々のログイン エンティティを検索して削除する代わりに、毎日テーブルを 1 つ削除する (単一のストレージ操作) だけで古いエンティティを削除できます。You can use the entity design above to avoid hotspots when you are inserting entities, and deleting old entities is now simply a question of deleting one table every day (a single storage operation) instead of finding and deleting hundreds and thousands of individual login entities every day.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 特定のエンティティの検索、他のデータとのリンク、集計情報の生成など、データの他の用途もサポートするように設計していますか。Does your design support other ways your application will use the data such as looking up specific entities, linking with other data, or generating aggregate information?
  • 新しいエンティティを挿入する際にホットスポットを回避するように設計していますか。Does your design avoid hot spots when you are inserting new entities?
  • テーブル名を削除した後に同じテーブル名を再利用する場合に待ち時間が必要になります。Expect a delay if you want to reuse the same table name after deleting it. 常に一意のテーブル名を使用することをお勧めします。It's better to always use unique table names.
  • Table service ではアクセス パターンを学習して、ノード全体にパーティションを分散しますが、最初に新しいテーブルを使用するときは何らかの調整が行われます。Expect some throttling when you first use a new table while the Table service learns the access patterns and distributes the partitions across nodes. 新しいテーブルを作成する必要がある頻度を検討する必要があります。You should consider how frequently you need to create new tables.

このパターンを使用する状況When to use this pattern

同時に削除する必要があるエンティティが大量にある場合に、このパターンを使用します。Use this pattern when you have a high volume of entities that you must delete at the same time.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

データ系列のパターンData series pattern

データ系列全体を単一のエンティティに格納し、要求の数を最小限に抑えます。Store complete data series in a single entity to minimize the number of requests you make.

コンテキストと問題Context and problem

一般的なシナリオとして、通常、アプリケーションで一度にすべて取得する必要があるデータ系列を格納するというものがあります。A common scenario is for an application to store a series of data that it typically needs to retrieve all at once. たとえば、アプリケーションで 1 時間ごとに各従業員が送信した IM メッセージの数を記録し、後でその情報を使用して、各ユーザーが過去 24 時間以内に送信したメッセージの数をプロットするとします。For example, your application might record how many IM messages each employee sends every hour, and then use this information to plot how many messages each user sent over the preceding 24 hours. 設計の 1 つとして、従業員ごとに 24 個のエンティティを格納します。One design might be to store 24 entities for each employee:

従業員ごとに 24 個のエンティティを格納する

この設計では、アプリケーションでメッセージのカウント値を更新する必要があるときに、各従業員の更新するエンティティを簡単に検索して更新できます。With this design, you can easily locate and update the entity to update for each employee whenever the application needs to update the message count value. ただし、情報を取得して、過去 24 時間の活動のグラフをプロットするためには、24 個のエンティティを取得する必要があります。However, to retrieve the information to plot a chart of the activity for the preceding 24 hours, you must retrieve 24 entities.

解決策Solution

次の設計を使用し、各時間のメッセージ数をそれぞれ別のプロパティに格納します。Use the following design with a separate property to store the message count for each hour:

メッセージ統計情報エンティティ

この設計では、マージ操作を使用して、特定の時間の従業員のメッセージ数を更新できます。With this design, you can use a merge operation to update the message count for an employee for a specific hour. これで、単一のエンティティに対する単一の要求を使用して、チャートをプロットするために必要なすべての情報を取得できます。Now, you can retrieve all the information you need to plot the chart using a request for a single entity.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • データ系列全体が単一のエンティティに収まらない場合 (エンティティは最大 252 個のプロパティを持つことができます)、BLOB などの代わりのデータ ストアを使用します。If your complete data series does not fit into a single entity (an entity can have up to 252 properties), use an alternative data store such as a blob.
  • 複数のクライアントが同時にエンティティを更新する場合は、 ETag を使用して、オプティミスティック コンカレンシーを実装する必要があります。If you have multiple clients updating an entity simultaneously, you will need to use the ETag to implement optimistic concurrency. クライアントがたくさんある場合は、競合が大量に発生する可能性があります。If you have many clients, you may experience high contention.

このパターンを使用する状況When to use this pattern

個々のエンティティに関連付けられているデータ系列を更新したり取得したりする必要がある場合に、このパターンを使用します。Use this pattern when you need to update and retrieve a data series associated with an individual entity.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

ワイド エンティティ パターンWide entities pattern

複数の物理エンティティを使用して、252 を超えるプロパティを持つ論理エンティティを格納します。Use multiple physical entities to store logical entities with more than 252 properties.

コンテキストと問題Context and problem

個々のエンティティが持つことができるプロパティは、(必須のシステム プロパティを除き) 252 個までです。また、格納できるデータは合計で 1 MB までです。An individual entity can have no more than 252 properties (excluding the mandatory system properties) and cannot store more than 1 MB of data in total. リレーショナル データベースでは、通常、新しいテーブルを追加し、その新しいテーブルと 1 対 1 のリレーションシップを作成することによって、行のサイズに関するさまざまな制限を回避します。In a relational database, you would typically get round any limits on the size of a row by adding a new table and enforcing a 1-to-1 relationship between them.

解決策Solution

Table service を使用すると、複数のエンティティを格納して、252 を超えるプロパティを持つ単一の大きなビジネス オブジェクトを作成できます。Using the Table service, you can store multiple entities to represent a single large business object with more than 252 properties. たとえば、過去 365 日の間に各従業員が送信した IM メッセージの数を格納する場合は、スキーマの異なる 2 つのエンティティを使用する次のデザインを使用できます。For example, if you want to store a count of the number of IM messages sent by each employee for the last 365 days, you could use the following design that uses two entities with different schemas:

複数のエンティティ

両方のエンティティを更新しないとエンティティどうしの同期が維持されない変更を加える必要がある場合は、EGT を使用できます。If you need to make a change that requires updating both entities to keep them synchronized with each other, you can use an EGT. それ以外の場合は、単一のマージ操作を使用して、特定の日のメッセージ数を更新できます。Otherwise, you can use a single merge operation to update the message count for a specific day. 個々 の従業員のすべてのデータを取得するには、PartitionKeyRowKey 値の両方を使用する 2 つの効率的なリクエストを取得する必要があります。To retrieve all the data for an individual employee you must retrieve both entities, which you can do with two efficient requests that use both a PartitionKey and a RowKey value.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 論理エンティティ全体を取得するには、少なくとも 2 つのストレージ トランザクション (各物理エンティティを取得するトランザクション) が必要です。Retrieving a complete logical entity involves at least two storage transactions: one to retrieve each physical entity.

このパターンを使用する状況When to use this pattern

サイズやプロパティの数が Table service の個々のエンティティの制限を超えるエンティティを格納する必要がある場合に、このパターンを使用します。Use this pattern when need to store entities whose size or number of properties exceeds the limits for an individual entity in the Table service.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

大型エンティティ パターンLarge entities pattern

大規模なプロパティの値を格納する BLOB ストレージを使用します。Use blob storage to store large property values.

コンテキストと問題Context and problem

個々のエンティティに格納できるデータは合計で 1 MB までです。An individual entity cannot store more than 1 MB of data in total. 1 つまたは複数のプロパティに格納される値でエンティティの合計サイズが 1 MB を超える場合は、Table service にエンティティ全体は格納できません。If one or several of your properties store values that cause the total size of your entity to exceed this value, you cannot store the entire entity in the Table service.

解決策Solution

1 つ以上のプロパティに大量のデータが含まれているためにエンティティのサイズが 1 MB を超える場合は、Blob service にデータを格納し、エンティティのプロパティに BLOB のアドレスを格納できます。If your entity exceeds 1 MB in size because one or more properties contain a large amount of data, you can store data in the Blob service and then store the address of the blob in a property in the entity. たとえば、従業員の写真を Blob ストレージに格納し、写真へのリンクを従業員エンティティの フォト プロパティに格納できます。For example, you can store the photo of an employee in blob storage and store a link to the photo in the Photo property of your employee entity:

写真のプロパティ

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • Table service 内のエンティティと、Blob service 内のデータの間の最終的な一貫性を保つには、最終的に一貫性のあるトランザクション パターン を使用してエンティティを維持します。To maintain eventual consistency between the entity in the Table service and the data in the Blob service, use the Eventually consistent transactions pattern to maintain your entities.
  • エンティティ全体を取得するには、少なくとも 2 つのストレージ トランザクション (エンティティを取得するトランザクションと BLOB データを取得するトランザクション) が必要です。Retrieving a complete entity involves at least two storage transactions: one to retrieve the entity and one to retrieve the blob data.

このパターンを使用する状況When to use this pattern

サイズが Table service の個々のエンティティの制限を超えるエンティティを格納する必要がある場合に、このパターンを使用します。Use this pattern when you need to store entities whose size exceeds the limits for an individual entity in the Table service.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

先頭または末尾に追加するアンチパターンPrepend/append anti-pattern

大量に挿入する場合に、挿入を複数のパーティションに分散させることで、スケーラビリティを向上させます。Increase scalability when you have a high volume of inserts by spreading the inserts across multiple partitions.

コンテキストと問題Context and problem

格納されているエンティティの先頭または末尾にエンティティを追加すると、通常は、連続するパーティションの最初または最後のパーティションに新しいエンティティが追加されます。Prepending or appending entities to your stored entities typically results in the application adding new entities to the first or last partition of a sequence of partitions. この場合、常に挿入はすべて同じパーティション内で行われるため、ホットスポットが発生し、Table service が複数のノードに挿入の負荷を分散できず、場合によっては、パーティションのスケーラビリティ ターゲットに達する可能性があります。In this case, all of the inserts at any given time are taking place in the same partition, creating a hotspot that prevents the table service from load-balancing inserts across multiple nodes, and possibly causing your application to hit the scalability targets for partition. たとえば、従業員によるネットワークやリソースへのアクセスをログに記録するアプリケーションで、以下に示すエンティティ構造を使用した場合、トランザクションの量が個々のパーティションのスケーラビリティ ターゲットに達すると、現在処理が行われているパーティションがホットスポットになる可能性があります。For example, if you have an application that logs network and resource access by employees, then an entity structure as shown below could result in the current hour's partition becoming a hotspot if the volume of transactions reaches the scalability target for an individual partition:

エンティティ構造

解決策Solution

代わりに次のエンティティ構造を使用すると、アプリケーションでイベントをログに記録する際に特定のパーティションのホットスポットを回避できます。The following alternative entity structure avoids a hotspot on any particular partition as the application logs events:

代替のエンティティ構造

次の例では 2 つのキー PartitionKeyRowKey がどのように複合キーになっているか注意してください。Notice with this example how both the PartitionKey and RowKey are compound keys. PartitionKey は部署と従業員の両方の ID を使用して複数のパーティションにログを配布します。The PartitionKey uses both the department and employee ID to distribute the logging across multiple partitions.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、以下の点に注意してください。Consider the following points when deciding how to implement this pattern:

  • 挿入時のホット パーティションの発生を回避する代わりのキー構造でクライアント アプリケーションが実行するクエリを効率的にサポートしていますか。Does the alternative key structure that avoids creating hot partitions on inserts efficiently support the queries your client application makes?
  • 予想されるトランザクションの量から判断して、個々のパーティションのスケーラビリティ ターゲットに達し、ストレージ サービスによって調整される可能性がありますか。Does your anticipated volume of transactions mean that you are likely to reach the scalability targets for an individual partition and be throttled by the storage service?

このパターンを使用する状況When to use this pattern

トランザクションの量により、ホット パーティションにアクセスするとストレージ サービスによって調整される可能性がある場合は、先頭または末尾に追加するアンチパターンを使用しないでください。Avoid the prepend/append anti-pattern when your volume of transactions is likely to result in throttling by the storage service when you access a hot partition.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance may also be relevant when implementing this pattern:

ログ データのアンチパターンLog data anti-pattern

ログ データの格納には通常、Table service ではなく Blob service を使用します。Typically, you should use the Blob service instead of the Table service to store log data.

コンテキストと問題Context and problem

ログ データを使用する局面として最も一般的なのが、特定の日付範囲または時間範囲のログ エントリを選択して取得するというものです。たとえば、特定の日の 15:04 から 15:06 までの間にアプリケーションが記録したエラー メッセージや重要なメッセージをすべて取得するなどの局面が挙げられます。A common use case for log data is to retrieve a selection of log entries for a specific date/time range: for example, you want to find all the error and critical messages that your application logged between 15:04 and 15:06 on a specific date. ログ メッセージの日時を使用して、ログのエンティティを保存するパーティションを特定したくない場合は、ホット パーティションになります。任意の時点で、すべてのログ エンティティが同じ PartitionKey 値を共有します (先頭または末尾に追加するアンチ パターンのセクションをご覧ください)。You do not want to use the date and time of the log message to determine the partition you save log entities to: that results in a hot partition because at any given time, all the log entities will share the same PartitionKey value (see the section Prepend/append anti-pattern). たとえば、ログ メッセージに関する以下のエンティティ スキーマでは、アプリケーションが現在の日付や時刻についてパーティションにあらゆるログ メッセージを書き込むことになるため、ホット パーティションの問題が発生します。For example, the following entity schema for a log message results in a hot partition because the application writes all log messages to the partition for the current date and hour:

ログ メッセージ エンティティ

この例では、RowKey にはログ メッセージが日時順で格納されていることを確認するため、ログメッセージの日時が含まれており、複数のメッセージが同じ日時を共有する複数のログ メッセージの場合にはメッセージ ID も含まれています。In this example, the RowKey includes the date and time of the log message to ensure that log messages are stored sorted in date/time order, and includes a message ID in case multiple log messages share the same date and time.

別の方法は、アプリケーションに確実に パーティション範囲にわたってメッセージを書き込ませる PartitionKey の使用です。Another approach is to use a PartitionKey that ensures that the application writes messages across a range of partitions. たとえば、ログ メッセージのソースで多数のパーティションにメッセージを配信できるようになっている場合には、以下のエンティティ スキーマを使用できます。For example, if the source of the log message provides a way to distribute messages across many partitions, you could use the following entity schema:

代替のログ メッセージ エンティティ

ただし、このスキーマには問題があります。特定のタイム スパンに記録されたログ メッセージをすべて取得するときには、テーブル内のパーティションを逐一検索する必要があるからです。However, the problem with this schema is that to retrieve all the log messages for a specific time span you must search every partition in the table.

解決策Solution

前のセクションでは、ログ エントリの保存先として Table service を使用した場合に生じる問題について説明し、その解決策として、完璧とは言いがたいものの 2 つの設計を紹介しました。The previous section highlighted the problem of trying to use the Table service to store log entries and suggested two, unsatisfactory, designs. 1 つ目に紹介した方法には、ホット パーティションが発生し、ログ メッセージの書き込みのパフォーマンスが低下するリスクがあります。これに対して 2 つ目の方法は、特定のタイム スパンについてログ メッセージを取得しようとした場合に、テーブル内のパーティションを逐一スキャンしなければならないため、クエリのパフォーマンスが低下するという問題がありました。One solution led to a hot partition with the risk of poor performance writing log messages; the other solution resulted in poor query performance because of the requirement to scan every partition in the table to retrieve log messages for a specific time span. BLOB ストレージなら、ここで取り上げたシナリオについて前の 2 つよりも優れたソリューションとなることができます。このため、Azure Storage Analytics が収集したログ データを保存するときにも、この BLOB ストレージが使用されています。Blob storage offers a better solution for this type of scenario and this is how Azure Storage Analytics stores the log data it collects.

このセクションでは、Storage Analytics が BLOB ストレージにログ データを格納する流れの概要を説明し、範囲を指定してクエリを実行することが多いデータを保存する際にこのアプローチがどのように役立つかを見ていきます。This section outlines how Storage Analytics stores log data in blob storage as an illustration of this approach to storing data that you typically query by range.

Storage Analytics では、ログ メッセージを一定の形式で区切ったものを、複数の BLOB に格納します。Storage Analytics stores log messages in a delimited format in multiple blobs. 区切りに使用する形式は、クライアント アプリケーション側でログ メッセージのデータ解析を円滑に完了できるものになっています。The delimited format makes it easy for a client application to parse the data in the log message.

Storage Analytics が BLOB に対して使用している名前付け規則は、検索対象のログ メッセージが含まれる BLOB の場所を特定できるようなものになっています。Storage Analytics uses a naming convention for blobs that enables you to locate the blob (or blobs) that contain the log messages for which you are searching. たとえば、"queue/2014/07/31/1800/000001.log" という名前の BLOB であれば、2014 年 7 月 31 日の 18:00 から始まる時間の Queue サービスと関係があるログ メッセージが格納されています。For example, a blob named "queue/2014/07/31/1800/000001.log" contains log messages that relate to the queue service for the hour starting at 18:00 on 31 July 2014. "000001" という部分は、この期間の最初のログ ファイルであることを示しています。The "000001" indicates that this is the first log file for this period. このほか、Storage Analytics では BLOB のメタデータの一環として、ファイルに保存されている最初と最後のログ メッセージのタイムスタンプを記録します。Storage Analytics also records the timestamps of the first and last log messages stored in the file as part of the blob's metadata. BLOB ストレージの API では、一定の名前プレフィックスに基づいてコンテナー内の BLOB の場所を特定できるようになっています。18:00 から始まる時間についてキューのログ データを格納している BLOB をすべて検索する場合には、"queue/2014/07/31/1800" というプレフィックスを使用します。The API for blob storage enables you locate blobs in a container based on a name prefix: to locate all the blobs that contain queue log data for the hour starting at 18:00, you can use the prefix "queue/2014/07/31/1800."

Storage Analytics は内部のバッファーにログ メッセージを保管したうえで、ログ エントリのバッチの最新版を使って定期的に BLOB を更新したり、新しい BLOB を作成したりします。Storage Analytics buffers log messages internally and then periodically updates the appropriate blob or creates a new one with the latest batch of log entries. これによって、BLOB サービスに書き込みを実行する回数が少なくなります。This reduces the number of writes it must perform to the blob service.

アプリケーションにこれと似たソリューションを実装するときには、信頼性 (ログ エントリが発生するたびに BLOB ストレージに書き込む) と、コストとスケーラビリティ (アプリケーションに更新内容を一時的に保管し、バッチとして BLOB ストレージに書き込む) との間のトレードオフをどのようにするかについて、検討が必要になります。If you are implementing a similar solution in your own application, you must consider how to manage the trade-off between reliability (writing every log entry to blob storage as it happens) and cost and scalability (buffering updates in your application and writing them to blob storage in batches).

問題と注意事項Issues and considerations

ログ データの保存方法を決めるときには、以下の点に注意する必要があります。Consider the following points when deciding how to store log data:

  • ホット パーティションが発生しないような設計のテーブルを作成すると、ログ データに対するアクセス効率が低下することがあります。If you create a table design that avoids potential hot partitions, you may find that you cannot access your log data efficiently.
  • ログ データを処理するときには多くの場合、クライアント側で多くのレコードを読み込む必要があります。To process log data, a client often needs to load many records.
  • ログ データは構造化されていることが多いものの、BLOB ストレージの方が優れたソリューションになることがあります。Although log data is often structured, blob storage may be a better solution.

実装時の注意事項Implementation considerations

このセクションでは、ここまでのセクションで説明したパターンを実装する際に念頭に置く必要がある点をいくつか説明します。This section discusses some of the considerations to bear in mind when you implement the patterns described in the previous sections. このセクションで示したコード例は、ほとんどが C# で書かれ、ストレージ クライアント ライブラリ (本稿執筆時点のバージョンは 4.3.0) を使用しています。Most of this section uses examples written in C# that use the Storage Client Library (version 4.3.0 at the time of writing).

エンティティの取得Retrieving entities

「クエリに対応した設計」で説明したように、最も効率的なクエリはポイント クエリです。As discussed in the section Design for querying, the most efficient query is a point query. ただ、時として多数のエンティティを同時に取得することも必要になります。However, in some scenarios you may need to retrieve multiple entities. このセクションでは、ストレージ クライアント ライブラリを使ってエンティティを取得するときによく使用される方法をいくつか紹介します。This section describes some common approaches to retrieving entities using the Storage Client Library.

ストレージ クライアント ライブラリを使ってポイント クエリを実行するExecuting a point query using the Storage Client Library

ポイント クエリを実行する最も簡単な方法は、次の c# コード スニペットに表示された、PartitionKey 値 "Sales" と RowKey 値 "212" を持つエンティティを取得する取得 テーブル操作の使用です。The easiest way to execute a point query is to use the Retrieve table operation as shown in the following C# code snippet that retrieves an entity with a PartitionKey of value "Sales" and a RowKey of value "212":

TableOperation retrieveOperation = TableOperation.Retrieve<EmployeeEntity>("Sales", "212");
var retrieveResult = employeeTable.Execute(retrieveOperation);
if (retrieveResult.Result != null)
{
    EmployeeEntity employee = (EmployeeEntity)retrieveResult.Result;
    ...
}  

この例では、 従業員エンティティ取得についてどのように予想しているかに注意してください。Notice how this example expects the entity it retrieves to be of type EmployeeEntity.

LINQ を使用して複数のエンティティを取得する。Retrieving multiple entities using LINQ

LINQ を使用すると、Microsoft Azure Cosmos Table Standard Library を使用するときに、Table service から複数のエンティティを取得できます。You can use LINQ to retrieve multiple entities from the Table service when working with Microsoft Azure Cosmos Table Standard Library.

dotnet add package Microsoft.Azure.Cosmos.Table

以下の例を機能させるには、名前空間を含める必要があります。To make the below examples work, you'll need to include namespaces:

using System.Linq;
using Microsoft.Azure.Cosmos.Table;
using Microsoft.Azure.Cosmos.Table.Queryable;

employeeTable は、CreateQuery<ITableEntity>() メソッドを実装する CloudTable オブジェクトであり、TableQuery<ITableEntity> を返します。The employeeTable is a CloudTable object that implements a CreateQuery<ITableEntity>() method, which returns a TableQuery<ITableEntity>. この種類のオブジェクトは、IQueryable を実装し、LINQ クエリ式とドット表記の構文を使用できるようにします。Objects of this type implement an IQueryable and allow using both LINQ Query Expressions and dot notation syntax.

複数のエンティティの取得は、where 句でクエリを指定することで実現します。Retrieving multiple entities and be achieved by specifying a query with a where clause. テーブル スキャンを回避するには、where 句の PartitionKey 値と、可能であれば RowKey 値をインクルードし、テーブルとパーティションのスキャンを避けます。To avoid a table scan, you should always include the PartitionKey value in the where clause, and if possible the RowKey value to avoid table and partition scans. Table サービスは、where 句で一部の比較演算子 (より大きい、以上、より小さい、以下、等しい、等しくない) のみサポートしています。The table service supports a limited set of comparison operators (greater than, greater than or equal, less than, less than or equal, equal, and not equal) to use in the where clause.

次の c# のコード スニペットは、Sales 部署 (PartitionKey が部署名を格納していると仮定) の中で、姓が "B" (RowKey が姓を格納していると仮定) で始まるすべての従業員を検索 します。The following C# code snippet finds all the employees whose last name starts with "B" (assuming that the RowKey stores the last name) in the sales department (assuming the PartitionKey stores the department name):

TableQuery<EmployeeEntity> employeeQuery = employeeTable.CreateQuery<EmployeeEntity>();
var query = (from employee in employeeQuery
            where employee.PartitionKey == "Sales" &&
            employee.RowKey.CompareTo("B") >= 0 &&
            employee.RowKey.CompareTo("C") < 0
            select employee).AsTableQuery();
            
var employees = query.Execute();  

パフォーマンスを確保するため、クエリで RowKeyPartitionKey の両方をどのように指定するか注意してください。Notice how the query specifies both a RowKey and a PartitionKey to ensure better performance.

次のコード サンプルは、LINQ 構文を使用しない同等の機能を示しています。The following code sample shows equivalent functionality without using LINQ syntax:

TableQuery<EmployeeEntity> employeeQuery = 
    new TableQuery<EmployeeEntity>().Where(
        TableQuery.CombineFilters(
            TableQuery.CombineFilters(
                TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales"),
                TableOperators.And,
                TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, "B")),
            TableOperators.And,
            TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, "C")));
            
var employees = employeeTable.ExecuteQuery(employeeQuery);  

注意

このサンプルでは、 3 つのフィルター条件を含んだ複数の コンバイン フィルター メソッドをネストしています。The sample nests multiple CombineFilters methods to include the three filter conditions.

1 件のクエリで大量のエンティティを取得するRetrieving large numbers of entities from a query

最適なクエリは PartitionKey 値と RowKey 値に基づいて個別のエンティティを返します。An optimal query returns an individual entity based on a PartitionKey value and a RowKey value. ところが、場合によっては同じパーティション、ときには多数のパーティションから、多数のエンティティを返すことが必要になります。However, in some scenarios you may have a requirement to return many entities from the same partition or even from many partitions.

そのようなときには必ず、アプリケーションのパフォーマンスを綿密にテストする必要があります。You should always fully test the performance of your application in such scenarios.

Table サービスに対してクエリを実行した場合、一度に返されるエンティティの数は最大 1,000 件、クエリの実行時間は最大 5 秒間です。A query against the table service may return a maximum of 1,000 entities at one time and may execute for a maximum of five seconds. 結果として返されるエンティティが 1,000 件を超える場合、クエリが 5 秒以内に完了しなかった場合、またはクエリがパーティションの境界をまたいで実行される場合には、Table service によって継続トークンが返されます。クライアント アプリケーションはこのトークンを使って、続きとなるエンティティを要求します。If the result set contains more than 1,000 entities, if the query did not complete within five seconds, or if the query crosses the partition boundary, the Table service returns a continuation token to enable the client application to request the next set of entities. 継続トークンの詳細については、「クエリのタイムアウトと改ページ」をご覧ください。For more information about how continuation tokens work, see Query Timeout and Pagination.

ストレージ クライアント ライブラリを使用している場合には、Table service からエンティティが返されるたびに継続トークンが自動で処理されます。If you are using the Storage Client Library, it can automatically handle continuation tokens for you as it returns entities from the Table service. 以下の C# コード サンプルではストレージ クライアント ライブラリを使用しているため、Table サービスが応答で返した継続トークンが自動的に処理されます。The following C# code sample using the Storage Client Library automatically handles continuation tokens if the table service returns them in a response:

string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales");
TableQuery<EmployeeEntity> employeeQuery = new TableQuery<EmployeeEntity>().Where(filter);

var employees = employeeTable.ExecuteQuery(employeeQuery);
foreach (var emp in employees)
{
    // ...
}  

以下の C# コード サンプルでは、継続トークンの処理を明示的に記述しています。The following C# code handles continuation tokens explicitly:

string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales");
TableQuery<EmployeeEntity> employeeQuery = new TableQuery<EmployeeEntity>().Where(filter);

TableContinuationToken continuationToken = null;
do
{
    var employees = employeeTable.ExecuteQuerySegmented(employeeQuery, continuationToken);
    foreach (var emp in employees)
    {
        // ...
    }
    
    continuationToken = employees.ContinuationToken;
} while (continuationToken != null);  

継続トークンを明示的に使用すると、アプリケーションが次のセグメントに相当するデータを取得するタイミングを制御できます。By using continuation tokens explicitly, you can control when your application retrieves the next segment of data. たとえば、テーブルに格納されたエンティティをクライアント アプリケーションでページとして表示できるようにしている場合に、クエリで取得したエンティティをユーザーが最後まで見ないことがあります。そのようなとき、継続トークンを明示的に使用していれば、ユーザーが現在のセグメントのエンティティの最後のページに達した時点で、アプリケーションが継続トークンだけを使って次のセグメントを取得できます。For example, if your client application enables users to page through the entities stored in a table, a user may decide not to page through all the entities retrieved by the query so your application would only use a continuation token to retrieve the next segment when the user had finished paging through all the entities in the current segment. この方法には、いくつかの利点があります。This approach has several benefits:

  • Table service から取得するデータの量を制限したり、ユーザーがネットワークを移動したりできるようになります。It enables you to limit the amount of data to retrieve from the Table service and that you move over the network.
  • .NET の非同期 IO を実行できるようになります。It enables you to perform asynchronous IO in .NET.
  • 継続トークンをシリアル化して永続記憶装置に保存できるため、アプリケーションがクラッシュした場合でも処理を継続できるようになります。It enables you to serialize the continuation token to persistent storage so you can continue in the event of an application crash.

注意

継続トークンは通常、エンティティ 1,000 件を 1 つのセグメントにして返しますが、この数が少なくなることもあります。A continuation token typically returns a segment containing 1,000 entities, although it may be fewer. これも、取得を使用してクエリが返すをエントリの数を制限する場合は、検索条件に一致する最初の n 件のエンティティを返します。テーブル サービスは、残りのエンティティを取得するために、継続トークンと合わせて n 件より少ないエンティティを含むセグメントを返す可能性があります。This is also the case if you limit the number of entries a query returns by using Take to return the first n entities that match your lookup criteria: the table service may return a segment containing fewer than n entities along with a continuation token to enable you to retrieve the remaining entities.

以下の C# コードは、1 つのセグメントで返されるエンティティの数を変更するためのものです。The following C# code shows how to modify the number of entities returned inside a segment:

employeeQuery.TakeCount = 50;  

サーバー側のプロジェクションServer-side projection

1 つのエンティティには最大で 255 個のプロパティを格納でき、エンティティの最大サイズは 1 MB です。A single entity can have up to 255 properties and be up to 1 MB in size. テーブルに対してクエリを実行してエンティティを取得する際、すべてのプロパティが必要ない場合は、データの不要な転送を避けることができます (遅延とコストの削減につながります)。When you query the table and retrieve entities, you may not need all the properties and can avoid transferring data unnecessarily (to help reduce latency and cost). サーバー側のプロジェクションを使えば、必要なプロパティのみを転送できます。You can use server-side projection to transfer just the properties you need. 次の例は、クエリによって選択されたエンティティから電子メール プロパティ (PartitionKeyRowKeyタイムスタンプETag と連動) を取得しています。The following example is retrieves just the Email property (along with PartitionKey, RowKey, Timestamp, and ETag) from the entities selected by the query.

string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales");
List<string> columns = new List<string>() { "Email" };
TableQuery<EmployeeEntity> employeeQuery =
    new TableQuery<EmployeeEntity>().Where(filter).Select(columns);

var entities = employeeTable.ExecuteQuery(employeeQuery);
foreach (var e in entities)
{
    Console.WriteLine("RowKey: {0}, EmployeeEmail: {1}", e.RowKey, e.Email);
}  

RowKey 値が取得するプロパティのリストに含まれていなくても、どのように利用できるか注意してください。Notice how the RowKey value is available even though it was not included in the list of properties to retrieve.

エンティティの変更Modifying entities

ストレージ クライアント ライブラリを使えば、Table サービスに格納されたエンティティを、挿入、削除、更新の各操作によって変更できます。The Storage Client Library enables you to modify your entities stored in the table service by inserting, deleting, and updating entities. また、EGT を使えば複数の挿入、更新、削除の操作をバッチ処理で行えるため、必要なラウンド トリップの回数が減り、ソリューションのパフォーマンスが高まります。You can use EGTs to batch multiple inserts, update, and delete operations together to reduce the number of round trips required and improve the performance of your solution.

ストレージ クライアント ライブラリが EGT を実行したときにスローされるケースの例外として、通常、バッチ処理の失敗を招いたエンティティのインデックスが含まれます。Exceptions thrown when the Storage Client Library executes an EGT typically include the index of the entity that caused the batch to fail. これは EGT を使うコードをデバッグする際に役立ちます。This is helpful when you are debugging code that uses EGTs.

クライアント アプリケーションでのコンカレンシーと更新操作の処理方法に設計が及ぼす影響についても考慮が必要です。You should also consider how your design affects how your client application handles concurrency and update operations.

コンカレンシーを管理するManaging concurrency

既定では、テーブル サービスは個々 のエンティティのレベルで挿入マージ削除操作に対しオプティミスティック コンカレンシー チェックを実行しますが、クライアントは、テーブル サービスがこれらのチェックをバイパスするよう強制することもできます。By default, the table service implements optimistic concurrency checks at the level of individual entities for Insert, Merge, and Delete operations, although it is possible for a client to force the table service to bypass these checks. Table service でのコンカレンシーの管理方法については、「Microsoft Azure Storage でのコンカレンシー制御の管理」をご覧ください。For more information about how the table service manages concurrency, see Managing Concurrency in Microsoft Azure Storage.

マージまたは置換Merge or replace

TableOperation クラスの置換のメソッドは、常に、Table service の完全なエンティティを置換します。The Replace method of the TableOperation class always replaces the complete entity in the Table service. 格納されたエンティティに存在するプロパティを要求に含めない場合、要求により、格納されたエンティティからそのプロパティが削除されます。If you do not include a property in the request when that property exists in the stored entity, the request removes that property from the stored entity. 格納されたエンティティからプロパティを明示的に削除しない場合は、すべてのプロパティを要求に含める必要があります。Unless you want to remove a property explicitly from a stored entity, you must include every property in the request.

TableOperation クラスのマージ メソッドを使用して エンティティを更新するときの Table service に送信するデータの量を削減します。You can use the Merge method of the TableOperation class to reduce the amount of data that you send to the Table service when you want to update an entity. マージ メソッドはプロパティ値を持った格納エンティティのプロパティをリクエストに含まれるエンティティと置換しますが、リクエストに含まれない格納エンティティ内の無傷のプロパティはそのまま残します。The Merge method replaces any properties in the stored entity with property values from the entity included in the request, but leaves intact any properties in the stored entity that are not included in the request. ラージ エンティティがあり、要求で少数のプロパティのみを更新する必要があるときに便利な処理です。This is useful if you have large entities and only need to update a small number of properties in a request.

注意

エンティティが存在しない場合、置換マージ メソッドが失敗します。The Replace and Merge methods fail if the entity does not exist. 存在しない場合は、代わりに、InsertOrReplaceInsertOrMerge メソッドを使用して新しいエンティティを作成します。As an alternative, you can use the InsertOrReplace and InsertOrMerge methods that create a new entity if it doesn't exist.

異種のエンティティ種類の使用Working with heterogeneous entity types

Table service とは、スキーマのない テーブル ストアであり、 1 つのテーブルが複数の種類のエンティティを格納できるため、非常に柔軟な設計が提供できることを意味します。The Table service is a schema-less table store that means that a single table can store entities of multiple types providing great flexibility in your design. 次の例は、従業員エンティティと部署エンティティの両方を格納したテーブルを示しています。The following example illustrates a table storing both employee and department entities:

パーティション キーPartitionKey 行キーRowKey TimestampTimestamp
FirstNameFirstName LastNameLastName AgeAge EmailEmail
FirstNameFirstName LastNameLastName AgeAge EmailEmail
DepartmentNameDepartmentName EmployeeCountEmployeeCount
FirstNameFirstName LastNameLastName AgeAge EmailEmail

各エンティティはなお、PartitionKeyRowKeyTimestamp 値を持つ必要がありますが、プロパティ セットを持つ場合もあります。Each entity must still have PartitionKey, RowKey, and Timestamp values, but may have any set of properties. さらに、エンティティの種類を示すものがありません (エンティティの種類に関する情報を格納していない場合)。Furthermore, there is nothing to indicate the type of an entity unless you choose to store that information somewhere. エンティティの種類を識別する方法は 2 とおりあります。There are two options for identifying the entity type:

  • RowKey (または PartitionKey) にエンティティ型を追加。Prepend the entity type to the RowKey (or possibly the PartitionKey). 例えば、RowKey 値としてのEMPLOYEE_000123 または DEPARTMENT_SALESFor example, EMPLOYEE_000123 or DEPARTMENT_SALES as RowKey values.
  • 以下の表に示すように、個別のプロパティを使用してエンティティの種類を記録します。Use a separate property to record the entity type as shown in the table below.
パーティション キーPartitionKey 行キーRowKey TimestampTimestamp
EntityTypeEntityType FirstNameFirstName LastNameLastName AgeAge EmailEmail
EmployeeEmployee
EntityTypeEntityType FirstNameFirstName LastNameLastName AgeAge EmailEmail
EmployeeEmployee
EntityTypeEntityType DepartmentNameDepartmentName EmployeeCountEmployeeCount
部署Department
EntityTypeEntityType FirstNameFirstName LastNameLastName AgeAge EmailEmail
EmployeeEmployee

最初のオプションでは、エンティティ型を RowKeyの先頭に着けると、異なる種類の 2 つのエンティティが同じキー値にある可能性がある場合に便利です。The first option, prepending the entity type to the RowKey, is useful if there is a possibility that two entities of different types might have the same key value. この方法なら、パーティションに同じ種類のエンティティのグループ化もできます。It also groups entities of the same type together in the partition.

このセクションで説明した手法は、特に、本ガイドに前述のリレーションシップのモデル化に関する記事内の継承リレーションシップで説明した内容と関連があります。The techniques discussed in this section are especially relevant to the discussion Inheritance relationships earlier in this guide in the article Modeling relationships.

注意

エンティティの種類の値にバージョン番号を追加して、クライアント アプリケーションで POCO オブジェクトを発展させ、さまざまなバージョンを操作できるようにすることを検討してください。You should consider including a version number in the entity type value to enable client applications to evolve POCO objects and work with different versions.

このセクションの残りの部分では、同じテーブル内の異なる種類のエンティティを操作しやすくするストレージ クライアント ライブラリの機能について説明します。The remainder of this section describes some of the features in the Storage Client Library that facilitate working with multiple entity types in the same table.

異なる種類のエンティティを取得するRetrieving heterogeneous entity types

ストレージ クライアント ライブラリを使えば、3 とおりの方法で複数の種類のエンティティを操作できます。If you are using the Storage Client Library, you have three options for working with multiple entity types.

特定の RowKey および PartitionKey 値とともに格納されているエンティティの型がわかっている場合は、EmployeeEntity 型のエンティティを取得する前述の 2 つの例 (「ストレージ クライアント ライブラリを使ってポイント クエリを実行する」および「LINQ を使って複数のエンティティを取得する」) に示すように、エンティティを取得する際にエンティティの型を指定できます。If you know the type of the entity stored with a specific RowKey and PartitionKey values, then you can specify the entity type when you retrieve the entity as shown in the previous two examples that retrieve entities of type EmployeeEntity: Executing a point query using the Storage Client Library and Retrieving multiple entities using LINQ.

2 番目のオプションは、具体的な POCO エンティティ型ではなく、DynamicTableEntity 型 (プロパティ バッグ) の使用です(エンティティを .NET 型にシリアル化および非シリアル化する必要がないため、このオプションはパフォーマンスも向上させます) 。The second option is to use the DynamicTableEntity type (a property bag) instead of a concrete POCO entity type (this option may also improve performance because there is no need to serialize and deserialize the entity to .NET types). 次の c# コードは、テーブルから潜在的にさまざまな種類の複数のエンティティを取得しますが、すべてのエンティティを DynamicTableEntity インスタンスとして返します。The following C# code potentially retrieves multiple entities of different types from the table, but returns all entities as DynamicTableEntity instances. EntityType プロパティを使用して各エンティティの種類を決定します。It then uses the EntityType property to determine the type of each entity:

string filter =
    TableQuery.CombineFilters(
        TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales"),
        TableOperators.And,
        TableQuery.CombineFilters(
            TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, "B"),
            TableOperators.And,
            TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, "F")));
        
TableQuery<DynamicTableEntity> entityQuery =
    new TableQuery<DynamicTableEntity>().Where(filter);
    
var employees = employeeTable.ExecuteQuery(entityQuery);

IEnumerable<DynamicTableEntity> entities = employeeTable.ExecuteQuery(entityQuery);
foreach (var e in entities)
{
    EntityProperty entityTypeProperty;
    if (e.Properties.TryGetValue("EntityType", out entityTypeProperty))
    {
        if (entityTypeProperty.StringValue == "Employee")
        {
            // use entityTypeProperty, RowKey, PartitionKey, Etag, and Timestamp
        }
    }
}  

その他のプロパティを取得するには、DynamicTableEntity クラスの Properties プロパティに対して TryGetValue メソッドを使用する必要があります。To retrieve other properties you must use the TryGetValue method on the Properties property of the DynamicTableEntity class.

3 番目のオプションは、DynamicTableEntity 型と EntityResolver インスタンスの組み合わせの使用です。A third option is to combine using the DynamicTableEntity type and an EntityResolver instance. この方法なら、同じクエリで複数の POCO 型を解決できます。This enables you to resolve to multiple POCO types in the same query. この例では、EntityResolver デリゲートは EntityType プロパティを使用して、クエリによって返されるエンティティの 2 つの種類を区別します。In this example, the EntityResolver delegate is using the EntityType property to distinguish between the two types of entity that the query returns. Resolve メソッドは、resolver デリゲートを使用して DynamicTableEntity インスタンスを TableEntity インスタンスに解決します。The Resolve method uses the resolver delegate to resolve DynamicTableEntity instances to TableEntity instances.

EntityResolver<TableEntity> resolver = (pk, rk, ts, props, etag) =>
{
    TableEntity resolvedEntity = null;
    if (props["EntityType"].StringValue == "Department")
    {
        resolvedEntity = new DepartmentEntity();
    }
    else if (props["EntityType"].StringValue == "Employee")
    {
        resolvedEntity = new EmployeeEntity();
    }
    else 
    {
        throw new ArgumentException("Unrecognized entity", "props");
    }

    resolvedEntity.PartitionKey = pk;
    resolvedEntity.RowKey = rk;
    resolvedEntity.Timestamp = ts;
    resolvedEntity.ETag = etag;
    resolvedEntity.ReadEntity(props, null);
    return resolvedEntity;
};

string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "Sales");
        
TableQuery<DynamicTableEntity> entityQuery = new TableQuery<DynamicTableEntity>().Where(filter);

var entities = employeeTable.ExecuteQuery(entityQuery, resolver);
foreach (var e in entities)
{
    if (e is DepartmentEntity)
    {
        // ...
    }
    else if (e is EmployeeEntity)
    {
        // ...
    }
}  

異なる種類のエンティティを変更するModifying heterogeneous entity types

エンティティの種類がわからなくても削除はできますが、挿入はできません。You do not need to know the type of an entity to delete it, and you always know the type of an entity when you insert it. ただし、エンティティの種類がわからない場合は、POCO エンティティ クラスを使用せず、 DynamicTableEntity 型を使用して エンティティを更新します。However, you can use DynamicTableEntity type to update an entity without knowing its type and without using a POCO entity class. 次のコード サンプルでは、1 つのエンティティを取得し、 EmployeeCount プロパティが存在するか、更新前に確認しています。The following code sample retrieves a single entity, and checks the EmployeeCount property exists before updating it.

TableResult result = employeeTable.Execute(TableOperation.Retrieve(partitionKey, rowKey));
DynamicTableEntity department = (DynamicTableEntity)result.Result;

EntityProperty countProperty;
if (!department.Properties.TryGetValue("EmployeeCount", out countProperty))
{
    throw new InvalidOperationException("Invalid entity, EmployeeCount property not found.");
}

countProperty.Int32Value += 1;
employeeTable.Execute(TableOperation.Merge(department));

共有アクセス署名でのアクセスを制御するControlling access with Shared Access Signatures

Shared Access Signature (SAS) トークンを使うと、ストレージ アカウント キーをコードに追加しなくても、クライアント アプリケーションからテーブル エンティティに対して変更を加えたり、クエリを実行したりすることができます。You can use Shared Access Signature (SAS) tokens to enable client applications to modify (and query) table entities without the need to include your storage account key in your code. 通常、アプリケーションで SAS を使うと、次の 3 つのメリットが得られます。Typically, there are three main benefits to using SAS in your application:

  • デバイスで Table service のエンティティにアクセスして変更できるようにするために、安全ではないプラットフォーム (モバイル デバイスなど) にストレージ アカウント キーを配布する必要がない。You do not need to distribute your storage account key to an insecure platform (such as a mobile device) in order to allow that device to access and modify entities in the Table service.
  • Web ロールまたは worker ロールがエンティティを管理する際に実行する処理の一部を、エンド ユーザー コンピューターやモバイル デバイスなどのクライアント デバイスにオフロードできる。You can offload some of the work that web and worker roles perform in managing your entities to client devices such as end-user computers and mobile devices.
  • 制約と時間制限のあるアクセス許可のセットをクライアントに割り当てることができる (読み取り専用アクセスを特定のリソースに許可するなど)。You can assign a constrained and time limited set of permissions to a client (such as allowing read-only access to specific resources).

Table service での SAS トークンの使用について詳しくは、「 Shared Access Signatures (SAS) の使用」をご覧ください。For more information about using SAS tokens with the Table service, see Using Shared Access Signatures (SAS).

ただし、Table サービスのエンティティへのアクセスをクライアント アプリケーションに付与する SAS トークンを生成する必要はあります。これは、ストレージ アカウント キーに安全にアクセスできる環境で行うようにしてください。However, you must still generate the SAS tokens that grant a client application to the entities in the table service: you should do this in an environment that has secure access to your storage account keys. 通常は、Web ロールまたは worker ロールを使って SAS トークンを生成し、エンティティへのアクセスを必要とするクライアント アプリケーションに配布します。Typically, you use a web or worker role to generate the SAS tokens and deliver them to the client applications that need access to your entities. SAS トークンの生成とクライアントへの配布にもやはりオーバーヘッドが伴うため、特に大量に扱うシナリオでは、このオーバーヘッドを減らす最適な方法を検討する必要があります。Because there is still an overhead involved in generating and delivering SAS tokens to clients, you should consider how best to reduce this overhead, especially in high-volume scenarios.

テーブル内のエンティティのサブセットへのアクセスを付与する SAS トークンを生成できます。It is possible to generate a SAS token that grants access to a subset of the entities in a table. 既定では、テーブル全体に対し SAS トークンを作成しますが、SAS トークンへのアクセス許可を PartitionKey 値の範囲か、PartitionKeyRowKey 値の範囲のいずれかに指定することも可能です。By default, you create a SAS token for an entire table, but it is also possible to specify that the SAS token grant access to either a range of PartitionKey values, or a range of PartitionKey and RowKey values. システムの個々のユーザーに SAS トークンが生成されるようにすれば、各ユーザーの SAS トークンによってアクセスが許可されるのは、Table サービス内にあるユーザー独自のエンティティだけになります。You might choose to generate SAS tokens for individual users of your system such that each user's SAS token only allows them access to their own entities in the table service.

非同期と並列操作Asynchronous and parallel operations

要求を複数のパーティションに分散させている場合は、非同期または並列クエリを使ってスループットとクライアントの応答性を向上させることができます。Provided you are spreading your requests across multiple partitions, you can improve throughput and client responsiveness by using asynchronous or parallel queries. たとえば、テーブルに並列的にアクセスする複数の worker ロール インスタンスを使用する場合などです。For example, you might have two or more worker role instances accessing your tables in parallel. 個別の worker ロールで特定のパーティション セットのみを処理することも可能であるほか、テーブル内のすべてのパーティションにアクセスできる worker ロール インスタンスを複数実装することも可能です。You could have individual worker roles responsible for particular sets of partitions, or simply have multiple worker role instances, each able to access all the partitions in a table.

クライアント インスタンスでは、ストレージ操作を非同期的に実行することでスループットを高めることができます。Within a client instance, you can improve throughput by executing storage operations asynchronously. ストレージ クライアント ライブラリを使えば、非同期クエリと変更を簡単に記述できます。The Storage Client Library makes it easy to write asynchronous queries and modifications. たとえば、次の C# コードに示すように、パーティション内のすべてのエントリを取得する同期メソッドをベースとして利用できます。For example, you might start with the synchronous method that retrieves all the entities in a partition as shown in the following C# code:

private static void ManyEntitiesQuery(CloudTable employeeTable, string department)
{
    string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, department);
    TableQuery<EmployeeEntity> employeeQuery = new TableQuery<EmployeeEntity>().Where(filter);

    TableContinuationToken continuationToken = null;
    do
    {
        var employees = employeeTable.ExecuteQuerySegmented(employeeQuery, continuationToken);
        foreach (var emp in employees)
        {
            // ...
        }
        
        continuationToken = employees.ContinuationToken;
    } while (continuationToken != null);
}  

このコードを次のように少し変更して、クエリが非同期的に実行されるようにします。You can easily modify this code so that the query runs asynchronously as follows:

private static async Task ManyEntitiesQueryAsync(CloudTable employeeTable, string department)
{
    string filter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, department);
    TableQuery<EmployeeEntity> employeeQuery = new TableQuery<EmployeeEntity>().Where(filter);
    
    TableContinuationToken continuationToken = null;
    do
    {
        var employees = await employeeTable.ExecuteQuerySegmentedAsync(employeeQuery, continuationToken);
        foreach (var emp in employees)
        {
            // ...
        }
    
        continuationToken = employees.ContinuationToken;
    } while (continuationToken != null);
}  

この非同期の例は、同期のバージョンに次の変更が加えられたものです。In this asynchronous example, you can see the following changes from the synchronous version:

  • メソッドのシグネチャは async 修飾子を含み、Task インスタンスを返します。The method signature now includes the async modifier and returns a Task instance.
  • ExecuteSegmented メソッドを呼び出して結果を取得する代わりに、ExecuteSegmentedAsync メソッドを呼び出し、await 修飾子を使用して非同期的に結果を取得します。Instead of calling the ExecuteSegmented method to retrieve results, the method now calls the ExecuteSegmentedAsync method and uses the await modifier to retrieve results asynchronously.

クライアント アプリケーションが複数回このメソッドを呼び出し ( 部署 パラメーターの異なる値のため)、各クエリは個別のスレッドで実行されます。The client application can call this method multiple times (with different values for the department parameter), and each query will run on a separate thread.

IEnumerable インターフェイスは非同期列挙型をサポートしていないため、TableQuery クラス内のExecute メソッドには非同期バージョンはありません。There is no asynchronous version of the Execute method in the TableQuery class because the IEnumerable interface does not support asynchronous enumeration.

エンティティを非同期的に挿入、更新、削除できます。You can also insert, update, and delete entities asynchronously. 次の C# の例は、従業員エンティティを挿入または置換する単純な同期メソッドです。The following C# example shows a simple, synchronous method to insert or replace an employee entity:

private static void SimpleEmployeeUpsert(
    CloudTable employeeTable,
    EmployeeEntity employee)
{
    TableResult result = employeeTable.Execute(TableOperation.InsertOrReplace(employee));
    Console.WriteLine("HTTP Status: {0}", result.HttpStatusCode);
}  

このコードを次のように少し変更して、更新が非同期的に実行されるようにすることができます。You can easily modify this code so that the update runs asynchronously as follows:

private static async Task SimpleEmployeeUpsertAsync(
    CloudTable employeeTable,
    EmployeeEntity employee)
{
    TableResult result = await employeeTable.ExecuteAsync(TableOperation.InsertOrReplace(employee));
    Console.WriteLine("HTTP Status: {0}", result.HttpStatusCode);
}  

この非同期の例は、同期のバージョンに次の変更が加えられたものです。In this asynchronous example, you can see the following changes from the synchronous version:

  • メソッドのシグネチャは async 修飾子を含み、Task インスタンスを返します。The method signature now includes the async modifier and returns a Task instance.
  • Execute メソッドを呼び出してエンティティを更新する代わりに、ExecuteAsync メソッドを呼び出し、await 修飾子を使用して非同期的に結果を取得します。Instead of calling the Execute method to update the entity, the method now calls the ExecuteAsync method and uses the await modifier to retrieve results asynchronously.

クライアント アプリケーションは、これと同じように非同期メソッドを複数回呼び出すことができます。各メソッドの呼び出しは別々のスレッドで実行されます。The client application can call multiple asynchronous methods like this one, and each method invocation will run on a separate thread.

次のステップNext steps