Azure Table storage のテーブル設計ガイド:スケーラビリティとパフォーマンスに優れたテーブルAzure Table storage table design guide: Scalable and performant tables

ヒント

この記事の内容は、従来の Azure Table Storage を対象としています。The content in this article applies to the original Azure Table storage. ただし現在は、Table Storage の Premium プランである Azure Cosmos DB Table API が存在します。However, there is now a premium offering for table storage: the Azure Cosmos DB Table API. この API には、スループットが最適化されたテーブル、グローバル分散、自動のセカンダリ インデックスが用意されています。This API offers throughput-optimized tables, global distribution, and automatic secondary indexes. Azure Cosmos DB の Table API と Azure Table Storage との間には機能の相違がいくつか存在します。There are some feature differences between Table API in Azure Cosmos DB and Azure table storage. 詳細を確認し、Premium エクスペリエンスを使ってみるには、Azure Cosmos DB Table API に関するページを参照してください。For more information, and to try out the premium experience, see Azure Cosmos DB Table API.

スケーラビリティとパフォーマンスに優れたテーブルを設計するには、コストを含むさまざまな要因を考慮する必要があります。To design scalable and performant tables, you must consider a variety of factors, including cost. 過去にリレーショナル データベースのスキーマを設計した経験がある方なら、こうした考慮事項はご存じであると思われます。If you've previously designed schemas for relational databases, these considerations will be familiar to you. しかし、Azure Table storage とリレーショナル モデルにはいくつかの類似点がある一方で、重要な違いも多数あります。But while there are some similarities between Azure Table storage and relational models, there are also many important differences. こうした相違点は、リレーショナル データベースの扱いに慣れた方には直感的にわかりづらかったり、扱いづらかったりする設計につながりがちですが、Table storage などの NoSQL キー/値ストアを設計する場合には好都合です。These differences typically lead to different designs that might look counter-intuitive or wrong to someone familiar with relational databases, but that do make sense if you're designing for a NoSQL key/value store, such as Table storage.

Table storage は、何十億ものデータ エンティティ (リレーショナル データベースの用語では "行") が含まれることがあるクラウド規模のアプリケーションをサポートするために、あるいは、大量のトランザクションをサポートしなければならないデータセットのために設計されています。Table storage is designed to support cloud-scale applications that can contain billions of entities ("rows" in relational database terminology) of data, or for datasets that must support high transaction volumes. したがって、データの格納方法について考え方を変え、Table storage の動作方法を理解する必要があります。You therefore need to think differently about how you store your data, and understand how Table storage works. NoSQL データ ストアを適切に設計すれば、リレーショナル データベースを使うソリューションよりも、ソリューションのスケーラビリティが大幅に高まります (また、コストも抑えられます)。A well-designed NoSQL data store can enable your solution to scale much further (and at a lower cost) than a solution that uses a relational database. このガイドでは、これらのトピックについて説明します。This guide helps you with these topics.

Azure Table storage についてAbout Azure Table storage

このセクションでは、パフォーマンスとスケーラビリティを重視した設計に関連する Table storage の主要機能を取り上げます。This section highlights some of the key features of Table storage that are especially relevant to designing for performance and scalability. 初めて Azure Storage と Table storage を利用する場合は、Microsoft Azure Storage の概要および .NET を使用した Azure Table storage の概要に関する記事を参照してから、この記事を読み進めてください。If you're new to Azure Storage and Table storage, see Introduction to Microsoft Azure Storage and Get started with Azure Table storage by using .NET before reading the remainder of this article. このガイドで主に取り上げるのは Table storage ですが、Azure Queue storage と Azure BLOB ストレージについて、また、それらをソリューションで Table storage と共に使用する方法についても説明します。Although the focus of this guide is on Table storage, it does include some discussion of Azure Queue storage and Azure Blob storage, and how you might use them along with Table storage in a solution.

Table storage では、表形式を使用してデータを格納します。Table storage uses a tabular format to store data. 各エンティティは、それ自体を一意に識別するためのキーのペアと、Table サービスがエンティティの最終更新日時をトラッキングするためのタイムスタンプ列を持ちます (エンティティの更新は自動的に行われます。In the standard terminology, each row of the table represents an entity, and the columns store the various properties of that entity. すべてのエンティティには、エンティティを一意に識別するキーと、エンティティの最終更新時を追跡するために Table storage が使用するタイムスタンプ列のペアがあります。Every entity has a pair of keys to uniquely identify it, and a timestamp column that Table storage uses to track when the entity was last updated. タイムスタンプ フィールドは自動的に追加されます。タイムスタンプを手動で任意の値に上書きすることはできません。The timestamp field is added automatically, and you can't manually overwrite the timestamp with an arbitrary value. Table storage では、この最終更新日時のタイムスタンプ (LMT) を使ってオプティミスティック コンカレンシーを管理します。Table storage uses this last-modified timestamp (LMT) to manage optimistic concurrency.

注意

Table storage REST API 操作により、LMT から派生する ETag 値も返されます。Table storage REST API operations also return an ETag value that it derives from the LMT. このドキュメントでは、ETag と LMT という用語が区別なく使用されます。基となる同じデータを表しているためです。In this document, the terms ETag and LMT are used interchangeably, because they refer to the same underlying data.

次の例は、従業員と部署のエンティティを格納する、シンプルなテーブル設計を示しています。The following example shows a simple table design to store employee and department entities. このガイドで後述する例の多くは、このシンプルな設計が基になっています。Many of the examples shown later in this guide are based on this simple design.

パーティション キーPartitionKey 行キーRowKey TimestampTimestamp
マーケティングMarketing 0000100001 2014-08-22T00:50:32Z2014-08-22T00:50:32Z
FirstNameFirstName LastNameLastName AgeAge EmailEmail
DonDon HallHall 3434 donh@contoso.com
マーケティングMarketing 0000200002 2014-08-22T00:50:34Z2014-08-22T00:50:34Z
FirstNameFirstName LastNameLastName AgeAge EmailEmail
6 月Jun CaoCao 4747 junc@contoso.com
マーケティングMarketing 部署Department 2014-08-22T00:50:30Z2014-08-22T00:50:30Z
DepartmentNameDepartmentName EmployeeCountEmployeeCount
マーケティングMarketing 153153
売上Sales 0001000010 2014-08-22T00:50:44Z2014-08-22T00:50:44Z
FirstNameFirstName LastNameLastName AgeAge EmailEmail
KenKen KwokKwok 2323 kenk@contoso.com

これまでは、この設計はリレーショナル データベースのテーブルに似ています。So far, this design looks similar to a table in a relational database. 重要な違いは、必須の列と、同じテーブルに複数のエンティティの種類を格納できることです。The key differences are the mandatory columns and the ability to store multiple entity types in the same table. さらに、FirstNameAge などのユーザー定義プロパティには、リレーショナル データベース内の列のように、整数や文字列などのデータ型があります。In addition, each of the user-defined properties, such as FirstName or Age, has a data type, such as integer or string, just like a column in a relational database. ただし、リレーショナル データベースとは違って、Table storage にはスキーマがないため、エンティティごとにプロパティのデータ型は同じである必要はありません。Unlike in a relational database, however, the schema-less nature of Table storage means that a property need not have the same data type on each entity. 1 つのプロパティに複雑なデータ型を格納するには、JSON や XML などのシリアル化された形式を使う必要があります。To store complex data types in a single property, you must use a serialized format such as JSON or XML. 詳細については、Table storage のデータ モデルの理解に関する記事を参照してください。For more information, see Understanding Table storage data model.

PartitionKeyRowKey の選択は優れたテーブル設計の基礎です。Your choice of PartitionKey and RowKey is fundamental to good table design. テーブルに格納されている各エンティティは、PartitionKeyRowKey の一意の組み合わせである必要があります。Every entity stored in a table must have a unique combination of PartitionKey and RowKey. リレーショナル データベース テーブル内のキーと同様に、PartitionKeyRowKey の値には、高速検索を可能にするクラスター化インデックスを作成するためのインデックスが作成されています。As with keys in a relational database table, the PartitionKey and RowKey values are indexed to create a clustered index that enables fast look-ups. ただし、Table storage ではセカンダリ インデックスは作成されないため、インデックスが作成されるプロパティはこれら 2 つだけです (後で説明するパターンの一部は、この明白な制限を回避する方法を示しています)。Table storage, however, doesn't create any secondary indexes, so these are the only two indexed properties (some of the patterns described later show how you can work around this apparent limitation).

テーブルは 1 つ以上のパーティション から構成されており、設計に関する決定事項の多くは、ソリューションを最適化するために適切な PartitionKeyRowKey を選択することになります。A table is made up of one or more partitions, and many of the design decisions you make will be around choosing a suitable PartitionKey and RowKey to optimize your solution. ソリューションは、すべてのエンティティがパーティションを使って整理された 1 つのテーブルだけでも構成できますが、通常は複数のテーブルが含まれます。A solution can consist of just a single table that contains all your entities organized into partitions, but typically a solution has multiple tables. テーブルは、エンティティを論理的に整理するために、またアクセス制御リストを使用してデータへのアクセスを管理するために役立ちます。Tables help you to logically organize your entities, and help you manage access to the data by using access control lists. 1 つのストレージ操作を使用してテーブル全体を削除できます。You can drop an entire table by using a single storage operation.

テーブルのパーティションTable partitions

アカウント名、テーブル名、PartitionKey を組み合わせて、Table storage がエンティティを格納するストレージ サービス内のパーティションを特定します。The account name, table name, and PartitionKey together identify the partition within the storage service where Table storage stores the entity. パーティションは、エンティティのアドレス指定スキームの一部であると同時に、トランザクションのスコープを定義し (この記事内の「エンティティ グループ トランザクション」を参照)、Table storage のスケーリング方法の基礎を形成します。As well as being part of the addressing scheme for entities, partitions define a scope for transactions (see the section later in this article, Entity group transactions), and form the basis of how Table storage scales. テーブル パーティションの詳細については、「Table Storage のパフォーマンスとスケーラビリティのチェックリスト」を参照してください。For more information on table partitions, see Performance and scalability checklist for Table storage.

Table storage では、個々のノードが 1 つ以上の完全なパーティションを提供し、サービスのスケーリングはノード間でパーティションの負荷を動的に分散させることで行われます。In Table storage, an individual node services one or more complete partitions, and the service scales by dynamically load-balancing partitions across nodes. ノードに負荷がかかっている場合、Table storage は、そのノードによって処理されるパーティションの範囲を別のノードに分割できます。If a node is under load, Table storage can split the range of partitions serviced by that node onto different nodes. トラフィックが減少したら、Table storage は、複数の低負荷ノードから 1 つのノードにパーティション範囲をマージできます。When traffic subsides, Table storage can merge the partition ranges from quiet nodes back onto a single node.

Table storage の内部詳細、特に、パーティションの管理方法については、Microsoft Azure Storage: 強力な一貫性を備えた高可用性クラウド ストレージ サービスに関する記事を参照してください。For more information about the internal details of Table storage, and in particular how it manages partitions, see Microsoft Azure Storage: A highly available cloud storage service with strong consistency.

エンティティ グループ トランザクションEntity group transactions

エンティティ グループ トランザクション (EGT) は、Table storage で複数のエンティティ間でアトミックな更新を行うための唯一の組み込みのメカニズムです。In Table storage, entity group transactions (EGTs) are the only built-in mechanism for performing atomic updates across multiple entities. EGT はバッチ トランザクションとも呼ばれています。EGTs are also referred to as batch transactions. EGT では、同じパーティションに格納されたエンティティしか処理できないため (特定のテーブルで同じパーティション キーを共有)、複数のエンティティにまたがるアトミックなトランザクション動作が必要な場合は、それらのエンティティを同じパーティションに格納してください。EGTs can only operate on entities stored in the same partition (sharing the same partition key in a particular table), so anytime you need atomic transactional behavior across multiple entities, ensure that those entities are in the same partition. これが、異なる種類のエンティティに複数のテーブルを使わずに、異なる種類のエンティティを同じテーブル (とパーティション) に格納する主な理由です。This is often a reason for keeping multiple entity types in the same table (and partition), and not using multiple tables for different entity types. 単一の EGT で最大 100 個のエンティティを処理できます。A single EGT can operate on at most 100 entities. 複数の並行処理 EGT を送信する場合は、それらの EGT が EGT 間の共通であるエンティティには動作しないことを確認することが重要です。If you submit multiple concurrent EGTs for processing, it's important to ensure that those EGTs don't operate on entities that are common across EGTs. そのようにしないと、処理が遅延するおそれがあります。Otherwise, you risk delaying processing.

また、EGT によって、設計で評価が必要なトレードオフが生じる可能性があります。EGTs also introduce a potential trade-off for you to evaluate in your design. 使用するパーティションが増えると、ノード間で要求を負荷分散しやすくなるため、アプリケーションのスケーラビリティが向上します。Using more partitions increases the scalability of your application, because Azure has more opportunities for load-balancing requests across nodes. 一方、これによって、アプリケーションでアトミックなトランザクションを実行し、データの強力な一貫性を維持する能力が制限される可能性があります。But this might limit the ability of your application to perform atomic transactions and maintain strong consistency for your data. さらに、1 つのノードの予想されるトランザクションのスループットを制限する可能性がある、パーティション レベルの特定のスケーラビリティ ターゲットがあります。Furthermore, there are specific scalability targets at the level of a partition that might limit the throughput of transactions you can expect for a single node.

Azure ストレージ アカウントのスケーラビリティ ターゲットの詳細については、Standard ストレージ アカウントのスケーラビリティ ターゲットに関する記事を参照してください。For more information about scalability targets for Azure storage accounts, see Scalability targets for standard storage accounts. Table storage のスケーラビリティ ターゲットの詳細については、Table storage のスケーラビリティとパフォーマンスのターゲットに関する記事を参照してください。For more information about scalability targets for Table storage, see Scalability and performance targets for Table storage. このガイドの後のセクションでは、このようなトレードオフを管理しやすくするさまざまな設計戦略を紹介すると共に、クライアント アプリケーションの固有の要件に基づいてパーティション キーを選択する最適な方法についても説明します。Later sections of this guide discuss various design strategies that help you manage trade-offs such as this one, and discuss how best to choose your partition key based on the specific requirements of your client application.

容量に関する考慮事項Capacity considerations

次の表に、Table storage ソリューションの設計時に考慮する必要のある主要な値をまとめます。The following table includes some of the key values to be aware of when you're designing a Table storage solution:

Azure ストレージ アカウントの合計容量Total capacity of an Azure storage account 500 TB500 TB
Azure のストレージ アカウントのテーブルの数Number of tables in an Azure storage account ストレージ アカウントの容量のみによる制限。Limited only by the capacity of the storage account.
テーブルのパーティションの数Number of partitions in a table ストレージ アカウントの容量のみによる制限。Limited only by the capacity of the storage account.
パーティション内のエンティティの数Number of entities in a partition ストレージ アカウントの容量のみによる制限。Limited only by the capacity of the storage account.
個別のエンティティのサイズSize of an individual entity 1 MB までの最大 255 個のプロパティ (PartitionKeyRowKeyTimestamp を含む)Up to 1 MB, with a maximum of 255 properties (including the PartitionKey, RowKey, and Timestamp).
PartitionKey のサイズSize of the PartitionKey 最大 1 KB の文字列。A string up to 1 KB in size.
RowKey のサイズSize of the RowKey 最大 1 KB の文字列。A string up to 1 KB in size.
エンティティ グループ トランザクションのサイズSize of an entity group transaction トランザクションには最大で 100 個のエンティティを含めることができ、ペイロードは 4 MB 未満にする必要があります。A transaction can include at most 100 entities, and the payload must be less than 4 MB in size. EGT では 1 回に 1 つのエンティティしか更新できません。An EGT can only update an entity once.

詳細については、Table service のデータ モデルの理解に関する記事を参照してください。For more information, see Understanding the Table service data model.

コストに関する考慮事項Cost considerations

Table storage は比較的安価ですが、Table storage を使うソリューションの評価の一環として、容量の使用とトランザクションの量を踏まえてコストを見積もる必要があります。Table storage is relatively inexpensive, but you should include cost estimates for both capacity usage and the quantity of transactions as part of your evaluation of any solution that uses Table storage. ただし、多くのシナリオでは、ソリューションのパフォーマンスとスケーラビリティを向上させるために、非正規化されたデータまたは重複するデータを格納するのも有効です。In many scenarios, however, storing denormalized or duplicate data in order to improve the performance or scalability of your solution is a valid approach to take. 価格の詳細については、Azure Storage の価格に関するページを参照してください。For more information about pricing, see Azure Storage pricing.

テーブル設計のガイドラインGuidelines for table design

これらの一覧は、テーブルを設計するときに注意する必要がある重要なガイドラインの一部をまとめたものです。These lists summarize some of the key guidelines you should keep in mind when you're designing your tables. これらのすべてについて、このガイドの残りの部分で詳しく説明します。This guide addresses them all in more detail later on. これらのガイドラインは、リレーショナル データベースの設計のために通常参照するガイドラインとは異なります。These guidelines are different from the guidelines you'd typically follow for relational database design.

読み取りの効率を高める Table storage の設計は次のとおりです。Designing your Table storage to be read efficient:

  • 読み込みが多いアプリケーションでクエリを実行するための設計Design for querying in read-heavy applications. テーブルを設計するときは、エンティティの更新方法について考える前に、実行するクエリ (特に、待機時間に影響を受けやすいもの) について検討してください。When you're designing your tables, think about the queries (especially the latency-sensitive ones) you'll run before you think about how you'll update your entities. これは通常、効率的でパフォーマンスの高いソリューションになります。This typically results in an efficient and performant solution.
  • クエリで PartitionKeyRowKey の両方を指定する。Specify both PartitionKey and RowKey in your queries. これらのようなポイント クエリは、最も効率的な Table storage のクエリです。Point queries such as these are the most efficient Table storage queries.
  • エンティティの重複コピーを格納するか検討します。Consider storing duplicate copies of entities. テーブル ストレージは安価であるため、クエリの効率を上げるために、(異なるキーを持つ) 同じエンティティを複数回格納することをご検討ください。Table storage is cheap, so consider storing the same entity multiple times (with different keys), to enable more efficient queries.
  • データの非正規化を検討します。Consider denormalizing your data. テーブル ストレージは安価であるため、データの非正規化を検討してください。Table storage is cheap, so consider denormalizing your data. たとえば、概要エンティティを格納すると、統計データ用クエリは単一のエンティティにアクセスするだけで済みます。For example, store summary entities so that queries for aggregate data only need to access a single entity.
  • 複合キーの値を使用します。Use compound key values. 存在するキーは PartitionKeyRowKey だけです。The only keys you have are PartitionKey and RowKey. たとえば、複合キーの値を使用してエンティティへの代替キー付きアクセス パスを有効にします。For example, use compound key values to enable alternate keyed access paths to entities.
  • クエリ プロジェクションを使用します。Use query projection. 必要なフィールドだけを選択するクエリを使用して、ネットワーク経由で転送するデータ量を削減できます。You can reduce the amount of data that you transfer over the network by using queries that select just the fields you need.

書き込みの効率を高める Table storage の設計は次のとおりです。Designing your Table storage to be write efficient:

  • ホット パーティションは作成しません。Don't create hot partitions. 任意の時点で、複数のパーティションで要求を分散できるキーを選択します。Choose keys that enable you to spread your requests across multiple partitions at any point of time.
  • トラフィックの急増を回避します。Avoid spikes in traffic. 適切な期間にわたってトラフィックを分散させ、トラフィックの急増を回避します。Distribute the traffic over a reasonable period of time, and avoid spikes in traffic.
  • 必ずしもエンティティの種類ごとに個別のテーブルを作成する必要はありません。Don't necessarily create a separate table for each type of entity. 複数のエンティティ種類でアトミック トランザクションが必要なときに、同じテーブル内の同じパーティションにこれら複数のエンティティ種類を格納できます。When you require atomic transactions across entity types, you can store these multiple entity types in the same partition in the same table.
  • 実現する必要のある最大スループットを検討します。Consider the maximum throughput you must achieve. Table storage のスケーラビリティ ターゲットを確認し、それを超えない設計にする必要があります。You must be aware of the scalability targets for Table storage, and ensure that your design won't cause you to exceed them.

このガイドの後半では、これらの原則をすべて実践した例を紹介します。Later in this guide, you'll see examples that put all of these principles into practice.

クエリに対応した設計Design for querying

Table storage では、読み取り、書き込み、またはその両方の負荷が高くなることがあります。Table storage can be read intensive, write intensive, or a mix of the two. ここでは、読み取り操作を効率的にサポートするための設計について検討します。This section considers designing to support read operations efficiently. 通常は、読み取り操作を効率的にサポートする設計は、書き込み操作についても効率が高くなります。Typically, a design that supports read operations efficiently is also efficient for write operations. ただし、書き込み操作をサポートするための設計にあたっては、追加の検討事項があります。However, there are additional considerations when designing to support write operations. これらについては、次の「データの変更に対応した設計」で説明します。These are discussed in the next section, Design for data modification.

データを効率的に読み取るためには、まず「必要なデータを取得するには、アプリケーションでどのようなクエリを実行する必要があるか」を考えてみてください。A good starting point to enable you to read data efficiently is to ask "What queries will my application need to run to retrieve the data it needs?"

注意

Table storage を使う場合は、事前に正しく設計することが重要です。後で設計を変更するのは難しいだけでなく、コストも高くなるためです。With Table storage, it's important to get the design correct up front, because it's difficult and expensive to change it later. たとえば、リレーショナル データベースなら、多くの場合、既存のデータベースにインデックスを追加するだけでパフォーマンスの問題に対処できます。For example, in a relational database, it's often possible to address performance issues simply by adding indexes to an existing database. Table storage ではそうはいきません。This isn't an option with Table storage.

PartitionKeyRowKey の選択がクエリのパフォーマンスに与える影響How your choice of PartitionKey and RowKey affects query performance

次の例では、以下の構造を持った複数の従業員エンティティを Table storage が格納すると想定しています (明確にするためほとんどの例で Timestamp プロパティを省略)。The following examples assume Table storage is storing employee entities with the following structure (most of the examples omit the Timestamp property for clarity):

列名Column name データ型Data type
PartitionKey (部署名)PartitionKey (Department name) StringString
RowKey (従業員 ID)RowKey (Employee ID) StringString
FirstName StringString
LastName StringString
Age 整数Integer
EmailAddress StringString

ここでは、Table storage のクエリの設計に関する一般的なガイドラインを示します。Here are some general guidelines for designing Table storage queries. 以下の例で使用しているフィルター構文は、Table storage REST API のものです。The filter syntax used in the following examples is from the Table storage REST API. 詳細については、クエリ エンティティに関する記事を参照してください。For more information, see Query entities.

  • ポイント クエリは使用できる中で最も効率的な検索であり、大量の検索や、最も短い待機時間が求められる検索にお勧めします。A point query is the most efficient lookup to use, and is recommended for high-volume lookups or lookups requiring the lowest latency. このようなクエリでは、PartitionKeyRowKey 値の両方を指定することでインデックスを使用し、個別のエンティティを効率よく検索することができます。Such a query can use the indexes to locate an individual entity efficiently by specifying both the PartitionKey and RowKey values. (例: $filter=(PartitionKey eq 'Sales') and (RowKey eq '2'))。For example: $filter=(PartitionKey eq 'Sales') and (RowKey eq '2').
  • 2 番目に良いのは範囲クエリです。Second best is a range query. PartitionKey を使用し、RowKey 値の範囲でフィルター処理して複数のエンティティを返します。It uses the PartitionKey, and filters on a range of RowKey values to return more than one entity. PartitionKey 値は特定のパーティションを識別し、RowKey 値はそのパーティション内のエンティティのサブセットを識別します。The PartitionKey value identifies a specific partition, and the RowKey values identify a subset of the entities in that partition. (例: $filter=PartitionKey eq 'Sales' and RowKey ge 'S' and RowKey lt 'T')。For example: $filter=PartitionKey eq 'Sales' and RowKey ge 'S' and RowKey lt 'T'.
  • 3 番目に良いのはパーティション スキャンです。Third best is a partition scan. PartitionKey を使用し、キーでない別のプロパティでフィルター処理し、複数のエンティティを返す場合があります。It uses the PartitionKey, and filters on another non-key property and might return more than one entity. PartitionKey 値は特定のパーティションを識別し、プロパティ値はそのパーティション内のエンティティのサブセットを選択します。The PartitionKey value identifies a specific partition, and the property values select for a subset of the entities in that partition. (例: $filter=PartitionKey eq 'Sales' and LastName eq 'Smith')。For example: $filter=PartitionKey eq 'Sales' and LastName eq 'Smith'.
  • テーブル スキャンPartitionKey は含まれません。また、一致するエンティティのテーブルを構成するパーティションのすべてを検索するため、非効率的です。A table scan doesn't include the PartitionKey, and is inefficient because it searches all of the partitions that make up your table for any matching entities. フィルターが RowKey を使用するかどうかにかかわらず、テーブル スキャンを実行します。It performs a table scan regardless of whether or not your filter uses the RowKey. (例: $filter=LastName eq 'Jones')。For example: $filter=LastName eq 'Jones'.
  • 複数のエンティティを返す Azure Table storage クエリは、それらを PartitionKey および RowKey の順序で並べ替えます。Azure Table storage queries that return multiple entities sort them in PartitionKey and RowKey order. クライアント内でエンティティを再度並べ替えるのを防ぐため、最も一般的な並べ替え順序を定義する RowKey を選択します。To avoid resorting the entities in the client, choose a RowKey that defines the most common sort order. Azure Cosmos DB で Azure Table API によって返されるクエリ結果は、パーティション キーや行キーの順序にはなりません。Query results returned by the Azure Table API in Azure Cosmos DB aren't sorted by partition key or row key. 機能の相違に関する詳細なリストについては、Azure Cosmos DB の Table API と Azure Table Storage の間の相違に関するページを参照してください。For a detailed list of feature differences, see differences between Table API in Azure Cosmos DB and Azure Table storage.

"or" を使用して RowKey 値に基づいてフィルターを指定した場合はパーティション スキャンが行われます。範囲クエリとしては扱われません。Using an "or" to specify a filter based on RowKey values results in a partition scan, and isn't treated as a range query. したがって、$filter=PartitionKey eq 'Sales' and (RowKey eq '121' or RowKey eq '322') などのフィルターを使用するクエリは避けてください。Therefore, avoid queries that use filters such as: $filter=PartitionKey eq 'Sales' and (RowKey eq '121' or RowKey eq '322').

ストレージ クライアント ライブラリを使って効率的なクエリを実行するクライアント側コードの例については、以下を参照してください。For examples of client-side code that use the Storage Client Library to run efficient queries, see:

同じテーブルに格納された複数の種類のエンティティを処理できるクライアント側コードの例については、For examples of client-side code that can handle multiple entity types stored in the same table, see:

適切な PartitionKey を選択するChoose an appropriate PartitionKey

PartitionKey の選択においては、(一貫性の確保に必要となる) EGT の使用を可能にするため、複数のパーティションでエンティティを配布するための要件に対し、(スケーラブルなソリューションの確保のため) 必要性のバランスを取ることが求められます。Your choice of PartitionKey should balance the need to enable the use of EGTs (to ensure consistency) against the requirement to distribute your entities across multiple partitions (to ensure a scalable solution).

極端な例としては、すべてのエンティティを 1 つのパーティションに格納できます。At one extreme, you can store all your entities in a single partition. ただし、これによってソリューションのスケーラビリティが制限され、Table storage が要求を負荷分散できなくなる可能性があります。But this might limit the scalability of your solution, and would prevent Table storage from being able to load-balance requests. 反対の極端な例としては、パーティションごとに 1 つのエンティティを格納できます。At the other extreme, you can store one entity per partition. これは拡張性が高く、Table storage が要求を負荷分散できるようにしますが、エンティティ グループ トランザクションは使用できなくなります。This is highly scalable and enables Table storage to load-balance requests, but prevents you from using entity group transactions.

理想的な PartitionKey は、効率的なクエリの使用を可能にし、かつソリューションのスケーラビリティを確保するために十分なパーティションを備えています。An ideal PartitionKey enables you to use efficient queries, and has sufficient partitions to ensure your solution is scalable. 通常、エンティティには、十分な数のパーティションにエンティティを分散できるだけのプロパティがあります。Typically, you'll find that your entities will have a suitable property that distributes your entities across sufficient partitions.

注意

たとえば、ユーザーまたは従業員に関する情報を格納するシステムでは、UserIDPartitionKey として使うと便利です。For example, in a system that stores information about users or employees, UserID can be a good PartitionKey. パーティション分割キーとして特定の UserID を使用する複数のエンティティを使用できます。You might have several entities that use a particular UserID as the partition key. ユーザーに関するデータを格納する個々のエンティティは、1 つのパーティションにグループ化されます。Each entity that stores data about a user is grouped into a single partition. これらのエンティティは EGT を介してアクセスできる一方で、スケーラビリティにも優れています。These entities are accessible via EGTs, while still being highly scalable.

選択した PartitionKey についてエンティティの挿入、更新、削除方法に関連する追加の検討事項があります。There are additional considerations in your choice of PartitionKey that relate to how you insert, update, and delete entities. 詳細については、この記事の後半の「データの変更に対応した設計」を参照してください。For more information, see Design for data modification later in this article.

Table storage のクエリを最適化するOptimize queries for Table storage

Table storage では、PartitionKeyRowKey 値を使用して、1 つのクラスター化インデックスにエンティティのインデックスを自動的に作成します。Table storage automatically indexes your entities by using the PartitionKey and RowKey values in a single clustered index. これが、ポイント クエリを使用するのが最も効率が良い理由です。This is the reason that point queries are the most efficient to use. ただし、PartitionKeyRowKey のクラスター化インデックス以外にはインデックスはありません。However, there are no indexes other than that on the clustered index on the PartitionKey and RowKey.

多くの設計で、複数の条件に基づいてエンティティを参照できるようにするという要件を満たす必要があります。Many designs must meet requirements to enable lookup of entities based on multiple criteria. たとえば、電子メール、従業員 ID、姓に基づいて従業員エンティティを特定する場合などです。For example, locating employee entities based on email, employee ID, or last name. テーブルの設計パターン」にある以下のパターンは、これらの種類の要件を扱っています。The following patterns in the section Table design patterns address these types of requirements. これらのパターンは、Table storage がセカンダリ インデックスを提供しないという事実への対処方法についても説明しています。The patterns also describe ways of working around the fact that Table storage doesn't provide secondary indexes.

  • パーティション内のセカンダリ インデックス パターン:(同じパーティション内の) 異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Intra-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values (in the same partition). これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • パーティション間のセカンダリ インデックス パターン:別個のパーティション内または別個のテーブル内の異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Inter-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values in separate partitions or in separate tables. これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • インデックス エンティティのパターン:エンティティ一覧を返す、効率的な検索を有効にするインデックスのエンティティを管理します。Index entities pattern: Maintain index entities to enable efficient searches that return lists of entities.

Table storage でのデータの並べ替えSort data in Table storage

Table storage は、PartitionKey に基づいて、次に RowKey によって昇順で並べ替えたクエリ結果を返します。Table storage returns query results sorted in ascending order, based on PartitionKey and then by RowKey.

注意

Azure Cosmos DB で Azure Table API によって返されるクエリ結果は、パーティション キーや行キーの順序にはなりません。Query results returned by the Azure Table API in Azure Cosmos DB aren't sorted by partition key or row key. 機能の相違に関する詳細なリストについては、Azure Cosmos DB の Table API と Azure Table Storage の間の相違に関するページを参照してください。For a detailed list of feature differences, see differences between Table API in Azure Cosmos DB and Azure Table storage.

Table storage でのキーは文字列値です。Keys in Table storage are string values. 数値が正しく並べ替えられるようにするには、固定長の値に変換し、ゼロ パディングを施す必要があります。To ensure that numeric values sort correctly, you should convert them to a fixed length, and pad them with zeroes. たとえば、従業員 ID 値を整数値の RowKey として使用する場合、従業員 ID を 123 から 00000123 に変換する必要があります。For example, if the employee ID value you use as the RowKey is an integer value, you should convert employee ID 123 to 00000123.

さまざまな順序 (名前、入社日など) で並べ替えられたデータを使う必要のあるアプリケーションは多数あります。Many applications have requirements to use data sorted in different orders: for example, sorting employees by name, or by joining date. テーブルの設計パターン」にある以下のパターンは、エンティティの並べ替え順序を代替する方法を扱っています。The following patterns in the section Table design patterns address how to alternate sort orders for your entities:

  • パーティション内のセカンダリ インデックス パターン:(同じパーティション内の) 異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Intra-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values (in the same partition). これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • パーティション間のセカンダリ インデックス パターン:別個のパーティション内または別個のテーブル内の異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Inter-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values in separate partitions in separate tables. これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • ログ テール パターン:逆の日付と時間順でソートする RowKey 値を 使用して、最も新しく追加された n 件のエンティティを取得します。Log tail pattern: Retrieve the n entities most recently added to a partition, by using a RowKey value that sorts in reverse date and time order.

データの変更に対応した設計Design for data modification

このセクションでは、挿入、更新、削除の操作を最適化するための設計上の考慮事項を示します。This section focuses on the design considerations for optimizing inserts, updates, and deletes. 場合によっては、クエリのために最適化する設計と、データ変更のために最適化する設計の間でトレードオフを評価する必要があります。In some cases, you'll need to evaluate the trade-off between designs that optimize for querying against designs that optimize for data modification. この評価は、リレーショナル データベース向けの設計で行うものに似ています (ただし、リレーショナル データベースでは、設計のトレードオフを管理するための手法が異なります)。This evaluation is similar to what you do in designs for relational databases (although the techniques for managing the design trade-offs are different in a relational database). テーブルの設計パターン」では、Table storage 用の詳細な設計パターンをいくつか説明し、これらのトレードオフに注目します。The section Table design patterns describes some detailed design patterns for Table storage, and highlights some of these trade-offs. 実際のところ、クエリ向けに最適化された設計の多くは、エンティティの変更にも適していることがおわかりになると思います。In practice, you'll find that many designs optimized for querying entities also work well for modifying entities.

挿入、更新、削除の操作のパフォーマンスを最適化するOptimize the performance of insert, update, and delete operations

エンティティを更新または削除するには、PartitionKeyRowKey 値を使用してエンティティを識別する必要があります。To update or delete an entity, you must be able to identify it by using the PartitionKey and RowKey values. この点では、エンティティを変更するための PartitionKeyRowKey を選択する際は、ポイント クエリをサポートするための選択と同様の基準に従う必要があります。In this respect, your choice of PartitionKey and RowKey for modifying entities should follow similar criteria to your choice to support point queries. エンティティはできるだけ効率的に識別したいものです。You want to identify entities as efficiently as possible. PartitionKeyRowKey 値の検出のためにエンティティを特定する非効率的なパーティションまたはテーブル スキャンを使用したくない場合は、更新または削除する必要があります。You don't want to use an inefficient partition or table scan to locate an entity in order to discover the PartitionKey and RowKey values you need to update or delete it.

テーブルの設計パターン」にある以下のパターンは、挿入、更新、削除の各操作のパフォーマンスの最適化を扱っています。The following patterns in the section Table design patterns address optimizing the performance of your insert, update, and delete operations:

  • 大量削除パターン: すべてのエンティティを同時削除用に独立したテーブルに格納することで、大量のエンティティを削除できるようにします。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.
  • データ系列のパターン: データ系列全体を単一のエンティティに格納し、要求の数を最小限に抑えます。Data series pattern: Store complete data series in a single entity to minimize the number of requests you make.
  • ワイド エンティティ パターン: 複数の物理エンティティを使用して、252 を超えるプロパティを持つ論理エンティティを格納します。Wide entities pattern: Use multiple physical entities to store logical entities with more than 252 properties.
  • 大型エンティティ パターン: 大規模なプロパティの値を格納する BLOB ストレージを使用します。Large entities pattern: Use blob storage to store large property values.

格納されたエンティティの一貫性を確保するEnsure consistency in your stored entities

データの変更を最適化するためのキーの選択を左右する要因として、アトミックなトランザクションを使って一貫性を確保する方法も挙げられます。The other key factor that influences your choice of keys for optimizing data modifications is how to ensure consistency by using atomic transactions. 同じパーティションに格納されたエンティティを操作する場合は、EGT しか使用できません。You can only use an EGT to operate on entities stored in the same partition.

テーブルの設計パターン」にある以下のパターンは、一貫性の管理を扱っています。The following patterns in the section Table design patterns address managing consistency:

  • パーティション内のセカンダリ インデックス パターン:(同じパーティション内の) 異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Intra-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values (in the same partition). これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • パーティション間のセカンダリ インデックス パターン:別個のパーティション内または別個のテーブル内の異なる RowKey 値を使用して、各エンティティの複数のコピーを格納します。Inter-partition secondary index pattern: Store multiple copies of each entity by using different RowKey values in separate partitions or in separate tables. これにより、異なる RowKey 値を使用することで、高速で効率的な検索と、代替の並べ替え順序が可能になります。This enables fast and efficient lookups, and alternate sort orders by using different RowKey values.
  • 最終的に一貫性のあるトランザクション パターン:Azure キューを使用して、パーティションやストレージ システムの境界を越えて、最終的に一貫した動作を実現します。Eventually consistent transactions pattern: Enable eventually consistent behavior across partition boundaries or storage system boundaries by using Azure queues.
  • インデックス エンティティのパターン:エンティティ一覧を返す、効率的な検索を有効にするインデックスのエンティティを管理します。Index entities pattern: Maintain index entities to enable efficient searches that return lists of entities.
  • 非正規化パターン:関連するデータを 1 つのエンティティに結合し、1 回のポイント クエリで必要なデータをすべて取得できるようにします。Denormalization pattern: Combine related data together in a single entity, to enable you to retrieve all the data you need with a single point query.
  • データ系列のパターン: データ系列全体を単一のエンティティに格納し、要求の数を最小限に抑えます。Data series pattern: Store complete data series in a single entity, to minimize the number of requests you make.

詳細については、この記事内の「エンティティ グループ トランザクション」を参照してください。For more information, see Entity group transactions later in this article.

効率的な変更に対応した設計によるクエリの効率化Ensure your design for efficient modifications facilitates efficient queries

効率的なクエリに適した設計は変更の効率も高いのが普通ですが、自分のシナリオにもそれが当てはまるかどうかは必ず評価する必要があります。In many cases, a design for efficient querying results in efficient modifications, but you should always evaluate whether this is the case for your specific scenario. テーブルの設計パターン セクションのパターンの中には、エンティティのクエリと変更との間のトレードオフを明示的に評価するものがあります。常に各操作数を考慮しておく必要があります。Some of the patterns in the section Table design patterns explicitly evaluate trade-offs between querying and modifying entities, and you should always take into account the number of each type of operation.

テーブルの設計パターン セクションの以下のパターンでは、 効率的なクエリの設計と効率的なデータ変更の設計の間のトレードオフについて説明します。The following patterns in the section Table design patterns address trade-offs between designing for efficient queries and designing for efficient data modification:

  • 複合キー パターン:クライアントで単一のポイント クエリを使用して関連するデータを検索できるようにするには、複合 RowKey 値を使用します。Compound key pattern: Use compound RowKey values to enable a client to look up related data with a single point query.
  • ログ テール パターン:逆の日付と時間順でソートする RowKey 値を 使用して、最も新しく追加された n 件のエンティティを取得します。Log tail pattern: Retrieve the n entities most recently added to a partition, by using a RowKey value that sorts in reverse date and time order.

テーブル データを暗号化するEncrypt table data

.NET Azure Storage クライアント ライブラリでは、挿入および置換操作の文字列エンティティ プロパティの暗号化がサポートされます。The .NET Azure Storage client library supports encryption of string entity properties for insert and replace operations. 暗号化された文字列はバイナリ プロパティとしてサービスで保存され、復号化された後、解読された後、文字列に再度変換されます。The encrypted strings are stored on the service as binary properties, and they're converted back to strings after decryption.

テーブルの場合、暗号化ポリシーに加え、ユーザーは暗号化するプロパティを指定する必要があります。For tables, in addition to the encryption policy, users must specify the properties to be encrypted. EncryptProperty 属性を指定する (TableEntity から派生した POCO エンティティの場合) か、要求オプションで暗号化リゾルバーを指定します。Either specify an EncryptProperty attribute (for POCO entities that derive from TableEntity), or specify an encryption resolver in request options. 暗号化リゾルバーは、パーティション キー、行キー、プロパティ名を取得するデリゲートで、プロパティを暗号化するかどうかを示すブール値を返します。An encryption resolver is a delegate that takes a partition key, row key, and property name, and returns a Boolean that indicates whether that property should be encrypted. 暗号化時、クライアント ライブラリはこの情報を使用して、ネットワークへの書き込み時にプロパティを暗号化するかどうかを決定します。During encryption, the client library uses this information to decide whether a property should be encrypted while writing to the wire. また、デリゲートは、プロパティの暗号化方法に関するロジックを使用する可能性にも備えます。The delegate also provides for the possibility of logic around how properties are encrypted. (X の場合、プロパティ A を暗号化し、それ以外の場合はプロパティ A および B を暗号化するなど。)エンティティの読み込み中、またはクエリの実行中は、この情報を指定する必要はありません。(For example, if X, then encrypt property A; otherwise encrypt properties A and B.) It's not necessary to provide this information while reading or querying entities.

マージは現在サポートされていません。Merge isn't currently supported. 以前に別のキーを使用してプロパティのサブセットが暗号化されている可能性があるため、新しいプロパティをマージしたり、メタデータを更新したりするとデータ損失が発生します。Because a subset of properties might have been encrypted previously by using a different key, simply merging the new properties and updating the metadata will result in data loss. マージでは、追加のサービス呼び出しをして既存のエンティティをサービスから読み込むか、プロパティごとに新しいキーを使用する必要があります。Merging either requires making extra service calls to read the pre-existing entity from the service, or using a new key per property. いずれの方法も、パフォーマンス上の理由でお勧めできません。Neither of these are suitable for performance reasons.

テーブル データの暗号化については、「Microsoft Azure Storage のクライアント側の暗号化と Azure Key Vault」を参照してください。For information about encrypting table data, see Client-side encryption and Azure Key Vault for Microsoft Azure Storage.

リレーションシップのモデル化Model relationships

複雑なシステムの設計において、ドメイン モデルの作成は重要なステップです。Building domain models is a key step in the design of complex systems. 通常は、モデル化プロセスを使用して各種エンティティとそれらの間のリレーションシップを特定します。これはビジネス ドメインについて理解し、システムの設計を伝えるための手段となります。Typically, you use the modeling process to identify entities and the relationships between them, as a way to understand the business domain and inform the design of your system. このセクションでは、ドメイン モデル内の一般的なリレーションシップの種類を Table storage 向けの設計に変換する方法を中心に説明します。This section focuses on how you can translate some of the common relationship types found in domain models to designs for Table storage. 論理データ モデルから物理的な NoSQL ベースのデータ モデルへのマッピング プロセスは、リレーショナル データベースの設計時に使われるプロセスとは異なります。The process of mapping from a logical data model to a physical NoSQL-based data model is different from that used when designing a relational database. リレーショナル データベースの設計では、通常、冗長性を最小限に抑えるために最適化されたデータ正規化プロセスを想定しています。Relational databases design typically assumes a data normalization process optimized for minimizing redundancy. また、このような設計では、データベースの動作の実装を抽象化する宣言型のクエリ機能も想定しています。Such design also assumes a declarative querying capability that abstracts the implementation of how the database works.

一対多のリレーションシップOne-to-many relationships

ビジネス ドメイン オブジェクトの間で一対多のリレーションシップが存在することはよくあります。たとえば、1 つの部署に多数の従業員が存在する場合などです。One-to-many relationships between business domain objects occur frequently: for example, one department has many employees. 特定のシナリオにおいて、長短それぞれあるものの、Table storage に一対多のリレーションシップを実装する方法はいくつかあります。There are several ways to implement one-to-many relationships in Table storage, each with pros and cons that might be relevant to the particular scenario.

部署および従業員のエンティティが何万とある、多国籍の大企業の例を考えてみましょう。Consider the example of a large multinational corporation with tens of thousands of departments and employee entities. 各部署には多数の従業員が在籍し、各従業員は 1 つの特定の部署に関連付けられています。Every department has many employees and each employee is associated with one specific department. 次のように、部署のエンティティと従業員のエンティティを分けて格納する方法もあります。One approach is to store separate department and employee entities, such as the following:

部署エンティティと従業員エンティティを示す図

この例は、PartitionKey 値に基づいて、各種類における暗黙の一対多のリレーションシップを示しています。This example shows an implicit one-to-many relationship between the types, based on the PartitionKey value. 各部署に多数の従業員が存在する可能性があります。Each department can have many employees.

この例は、部署エンティティと、同じパーティションに含まれる関連の従業員エンティティも示しています。This example also shows a department entity and its related employee entities in the same partition. 別のエンティティの種類として、別のパーティション、テーブル、またはストレージ アカウントを使うこともできます。You can choose to use different partitions, tables, or even storage accounts for the different entity types.

別の方法として、次の例に示すように、データを非正規化し、非正規化された部署データと共に従業員エンティティのみを格納する方法もあります。An alternative approach is to denormalize your data, and store only employee entities with denormalized department data, as shown in the following example. このシナリオで部署のマネージャーの詳細を変更できるようにする必要がある場合は、この非正規化の方法は最適ではない可能性があります。In this particular scenario, this denormalized approach might not be the best if you have a requirement to be able to change the details of a department manager. これを行うには、部署内のすべての従業員を更新する必要があります。To do this, you would need to update every employee in the department.

従業員エンティティの図

詳細については、 このガイドで後述する 非正規化パターン を参照してください。For more information, see the Denormalization pattern later in this guide.

次の表に、一対多のリレーションシップを持つ従業員エンティティと部署エンティティを格納するアプローチについて、それぞれの長所と短所をまとめます。The following table summarizes the pros and cons of each of the approaches for storing employee and department entities that have a one-to-many relationship. また、さまざまな操作を実行する頻度の予測も考慮する必要があります。You should also consider how often you expect to perform various operations. まれにしか実行しない操作であれば、コストの高い操作を設計に含めることが許容される可能性があります。It might be acceptable to have a design that includes an expensive operation if that operation only happens infrequently.

アプローチApproach 長所Pros 短所Cons
エンティティの種類は別、パーティション、テーブルは同じSeparate entity types, same partition, same table
  • 1 回の操作で部署エンティティを更新できる。You can update a department entity with a single operation.
  • 従業員エンティティを更新、挿入、削除するたびに部署エンティティを変更する必要がある場合は、一貫性を維持するために EGT を使用できます。You can use an EGT to maintain consistency if you have a requirement to modify a department entity whenever you update/insert/delete an employee entity. たとえば、部署ごとの従業員数を管理する場合などです。For example, if you maintain a departmental employee count for each department.
  • 場合によっては、一部のクライアント アクティビティで、従業員エンティティと部署エンティティの両方を取得する必要がある。You might need to retrieve both an employee and a department entity for some client activities.
  • ストレージ操作が同じパーティションで行われる。Storage operations happen in the same partition. トランザクションの量が多いときにホットスポットが生じる可能性がある。At high transaction volumes, this can result in a hotspot.
  • EGT を使用して従業員を新しい部署に移動することはできない。You can't move an employee to a new department by using an EGT.
エンティティの種類は別、パーティション、テーブル、ストレージ アカウントは別Separate entity types, different partitions, or tables or storage accounts
  • 1 回の操作で部署エンティティと従業員エンティティを更新できる。You can update a department entity or employee entity with a single operation.
  • トランザクションの量が多いときに、負荷をより多くのパーティションに分散させることができる。At high transaction volumes, this can help spread the load across more partitions.
  • 場合によっては、一部のクライアント アクティビティで、従業員エンティティと部署エンティティの両方を取得する必要がある。You might need to retrieve both an employee and a department entity for some client activities.
  • 従業員を更新、挿入、削除し、部署を更新するときに、一貫性を維持するために EGT を使用することはできない。You can't use EGTs to maintain consistency when you update/insert/delete an employee and update a department. たとえば、部署エンティティ内の従業員数を更新する場合などです。For example, updating an employee count in a department entity.
  • EGT を使用して従業員を新しい部署に移動することはできない。You can't move an employee to a new department by using an EGT.
単一のエンティティの種類への非正規化Denormalize into single entity type
  • 必要なすべての情報を 1 回の要求で取得できる。You can retrieve all the information you need with a single request.
  • 部署の情報を更新する必要がある場合、一貫性の維持のコストが高くなる可能性がある (部署のすべての従業員を更新する必要があるため)。It can be expensive to maintain consistency if you need to update department information (this would require you to update all the employees in a department).

これらの選択肢のうちのどれを選ぶか、また、どの長所と短所の影響が最も大きいかは、アプリケーションのシナリオによって異なります。How you choose among these options, and which of the pros and cons are most significant, depends on your specific application scenarios. たとえば、部署エンティティを変更する頻度はどのくらいですか。For example, how often do you modify department entities? すべての従業員クエリに追加の部署情報が必要ですか。Do all your employee queries need the additional departmental information? パーティションまたはストレージ アカウントのスケーラビリティの制限まで、どのくらいの余裕がありますか。How close are you to the scalability limits on your partitions or your storage account?

一対一のリレーションシップOne-to-one relationships

ドメイン モデルにはエンティティ間の一対一のリレーションシップが含まれる可能性があります。Domain models can include one-to-one relationships between entities. Table storage で一対一のリレーションシップを実装する必要がある場合は、2 つの関連するエンティティを取得する必要があるときにそれらをリンクする方法も選択する必要があります。If you need to implement a one-to-one relationship in Table storage, you must also choose how to link the two related entities when you need to retrieve them both. このリンクはキー値の規則に基づいて、各エンティティにおいて PartitionKeyRowKey 値形式で関連エンティティへのリンクを格納することで、明示的にも暗黙的にも成り得ます。This link can be either implicit, based on a convention in the key values, or explicit, by storing a link in the form of PartitionKey and RowKey values in each entity to its related entity. 同じパーティションに関連エンティティを格納するかどうかの詳細については、 一対多のリレーションシップセクションを参照してください。For a discussion of whether you should store the related entities in the same partition, see the section One-to-many relationships.

実装上の検討内容に応じて、Table storage で一対一のリレーションシップを実装する必要が生じることもあります。There are also implementation considerations that might lead you to implement one-to-one relationships in Table storage:

クライアントでの結合Join in the client

Table storage でのリレーションシップのモデル化には何とおりかの方法がありますが、Table storage を使う主な理由はスケーラビリティとパフォーマンスの 2 つであることを忘れないでください。Although there are ways to model relationships in Table storage, don't forget that the two prime reasons for using Table storage are scalability and performance. ソリューションのパフォーマンスとスケーラビリティを損なう多数のリレーションシップをモデル化しようとしていることに気が付いた場合は、そのすべてのデータ リレーションシップをテーブル設計に組み込む必要があるかどうかを確認する必要があります。If you find you are modeling many relationships that compromise the performance and scalability of your solution, you should ask yourself if it's necessary to build all the data relationships into your table design. クライアント アプリケーションで必要な結合が実行されるようにすると、設計を簡素化し、ソリューションのスケーラビリティとパフォーマンスを向上させることができます。You might be able to simplify the design, and improve the scalability and performance of your solution, if you let your client application perform any necessary joins.

たとえば、変更頻度の高くないデータが格納された小さなテーブルがある場合は、一度そのデータを取得してクライアント上にキャッシュできます。For example, if you have small tables that contain data that doesn't change often, you can retrieve this data once, and cache it on the client. そうすると、何度も同じデータを取得する必要がなくなります。This can avoid repeated roundtrips to retrieve the same data. このガイドで取り上げた例では、小さな組織の一連の部署はおそらく小規模で、頻繁には変更されません。In the examples we've looked at in this guide, the set of departments in a small organization is likely to be small and change infrequently. そのため、クライアント アプリケーションが一度ダウンロードして検索データとしてキャッシュできるデータの有力な候補です。This makes it a good candidate for data that a client application can download once and cache as lookup data.

継承リレーションシップInheritance relationships

クライアント アプリケーションでビジネス エンティティを表す継承リレーションシップの一部を構成するクラスのセットを使用する場合は、それらのエンティティを Table storage で簡単に保持できます。If your client application uses a set of classes that form part of an inheritance relationship to represent business entities, you can easily persist those entities in Table storage. たとえば、Person が抽象クラスとなっているクライアント アプリケーションにはクラスセットが定義されている可能性があります。For example, you might have the following set of classes defined in your client application, where Person is an abstract class.

継承リレーションシップの図

1 つの Person テーブルを使用して、2 つの具象クラスのインスタンスを Table storage に永続化できます。You can persist instances of the two concrete classes in Table storage by using a single Person table. 次のようなエンティティを使用します。Use entities that look like the following:

顧客エンティティと従業員エンティティを示す図

クライアント コードの同じテーブル内の複数のエンティティ種類の詳細については、このガイド内の「異種のエンティティ種類を使用する」を参照してください。For more information about working with multiple entity types in the same table in client code, see Work with heterogeneous entity types later in this guide. クライアント コードでエンティティの種類を認識する方法の例が示されています。This provides examples of how to recognize the entity type in client code.

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

以前のセクションでは、クエリを使用してエンティティ データを取得する場合と、エンティティ データを挿入、更新、削除する場合の両方のテーブル設計を最適化する方法について説明しました。In previous sections, you learned about how to optimize your table design for both retrieving entity data by using queries, and for inserting, updating, and deleting entity data. このセクションでは、Table storage で使用するのに適したパターンをいくつか紹介します。This section describes some patterns appropriate for use with Table storage. また、このガイドで前に提起された問題とトレードオフの一部に実際に対処する方法を説明しています。In addition, you'll see how you can practically address some of the issues and trade-offs raised previously in this guide. 次の図は、さまざまなパターン間の関係をまとめたものです。The following diagram summarizes the relationships among the different patterns:

テーブル設計パターンの図

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

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

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

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

Table storage では、PartitionKeyRowKey 値を使用してエンティティのインデックスを自動的に作成します。Table storage automatically indexes entities by using the PartitionKey and RowKey values. そのため、クライアント アプリケーションでは、これらの値を使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently by using these values. たとえば、次のテーブル構造を使用することにより、クライアント アプリケーションでは、ポイント クエリを使用して部署名と従業員 ID (PartitionKeyRowKey 値) から、個々の従業員エンティティを取得できます。For example, using the following table structure, 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.

従業員エンティティの図

また、電子メール アドレスなど、他のプロパティの値に基づいて従業員エンティティを検索する場合は、効率の劣るパーティション スキャンを使用して、一致するエンティティを検索する必要があります。If you also want 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 storage ではセカンダリ インデックスが提供されないためです。This is because Table storage doesn't provide secondary indexes. さらに、RowKey 順以外の順序で並べ替えられた従業員の一覧を要求するオプションはありません。In addition, there's 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 following structures, you can efficiently retrieve employee entities based on email address or employee ID. RowKey のプレフィックス値、empid_email_ で、1 人の従業員をクエリするか電子メール アドレスまたは従業員 ID の範囲を使用して、ある特定の範囲の従業員をクエリできます。The prefix values for 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.

RowKey 値が変化する従業員エンティティを示す図

次の 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 の順に並べ替えられた範囲を指定するか、メール アドレス順に並べ替えられた範囲を指定することができます。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. RowKey に適切なプレフィックスが含まれているエンティティのクエリを実行します。Query 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 storage REST API のものです。The filter syntax used in the preceding examples is from the Table storage 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 shouldn't 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 run.

  • セカンダリ インデックス エンティティは元のエンティティと同じパーティションに格納されるため、個々のパーティションのスケーラビリティ ターゲットを超えないようにしてください。Because the secondary index entities are stored in the same partition as the original entities, ensure that you don't 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 Use 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 don't necessarily need to duplicate all the properties of your entity. たとえば、電子メールアドレスを使用してエンティティを RowKey で検索するクエリの場合、従業員の年齢は不要です。このようなエンティティは、次のような構造になっている可能性があります。For example, if the queries that look up the entities by using the email address in the RowKey never need the employee's age, these entities can have the following structure:

    従業員エンティティの図

  • 通常は、エンティティの検索と必要なデータの検索にそれぞれ異なるクエリを使用するよりも、重複するデータを格納し、必要なすべてのデータを単一のクエリで取得できるようにすることをお勧めします。Typically, it's 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 by using a variety of different keys.
  • クライアントが、さまざまな並べ替え順序でエンティティを取得する必要がある。Your client needs to retrieve entities in different sort orders.
  • さまざまな一意の値を使用して各エンティティを識別できる。You can identify each entity by using a variety of unique values.

ただし、異なる RowKey 値を使用してエンティティ参照を実行している場合は、パーティションのスケーラビリティの制限を超えないようにしてください。However, be sure that you don't exceed the partition scalability limits when you're performing entity lookups by using the different RowKey values.

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

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

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

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

Table storage では、PartitionKeyRowKey 値を使用してエンティティのインデックスを自動的に作成します。Table storage automatically indexes entities by using the PartitionKey and RowKey values. そのため、クライアント アプリケーションでは、これらの値を使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently by using these values. たとえば、次のテーブル構造を使用することにより、クライアント アプリケーションでは、ポイント クエリを使用して部署名と従業員 ID (PartitionKeyRowKey 値) から、個々の従業員エンティティを取得できます。For example, using the following table structure, 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.

従業員エンティティの図

また、電子メール アドレスなど、他のプロパティの値に基づいて従業員エンティティを検索できるようにする場合は、効率の劣るパーティション スキャンを使用して、一致するエンティティを検索する必要があります。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 storage ではセカンダリ インデックスが提供されないためです。This is because Table storage doesn't provide secondary indexes. さらに、RowKey 順以外の順序で並べ替えられた従業員の一覧を要求するオプションはありません。In addition, there's no option to request a list of employees sorted in a different order than RowKey order.

これらのエンティティに対するトランザクションの量が膨大になることが予想される場合は、Table storage によってクライアントがレート制限されるリスクを最小限に抑える必要があります。You're anticipating a high volume of transactions against these entities, and want to minimize the risk of the Table storage rate limiting 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 following structures, you can efficiently retrieve employee entities based on email address or employee ID. PartitionKey のプレフィックスの値、empid_email_ で、クエリに使用するインデックスを特定することができます。The prefix values for 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 の順に並べ替えられた範囲を指定するか、メール アドレス順に並べ替えられた範囲を指定することができます。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. RowKey に適切なプレフィックスが含まれているエンティティのクエリを実行します。Query for entities with the appropriate prefix in the RowKey.

  • 従業員 ID 順で格納された、従業員 ID が000100 から 000199 の範囲の Sales 部署のすべての従業員を検索するには、$filter=(PartitionKey eq 'empid_Sales') and (RowKey ge '000100') and (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 storage REST API のものであることに注意してください。Note that the filter syntax used in the preceding examples is from the Table storage 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, 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 run.

  • 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 don't necessarily need to duplicate all the properties of your entity. たとえば、電子メールアドレスを使用してエンティティを RowKey で検索するクエリの場合、従業員の年齢は不要です。このようなエンティティは、次のような構造になっている可能性があります。For example, if the queries that look up the entities by using the email address in the RowKey never need the employee's age, these entities can have the following structure:

    セカンダリ インデックスを持つ従業員エンティティを示す図

  • 通常は、セカンダリ インデックスを使用したエンティティの検索とプライマリ インデックス内の必要なデータの検索にそれぞれ異なるクエリを使用するよりも、重複するデータを格納し、必要なすべてのデータを単一のクエリで取得できるようにすることをお勧めします。Typically, it's 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 by 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 by using a variety of different keys.
  • クライアントが、さまざまな並べ替え順序でエンティティを取得する必要がある。Your client needs to retrieve entities in different sort orders.
  • さまざまな一意の値を使用して各エンティティを識別できる。You can identify each entity by 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 by using the different RowKey values.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance might 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. パフォーマンスやスケーラビリティの関係で、一貫性が必要なエンティティを別々のパーティションや別のストレージ システムに格納する場合があります。For performance and scalability reasons, you might decide to store entities that have consistency requirements in separate partitions or in a separate storage system. そのような場合は、EGT を使用して一貫性を保つことはできません。In such a scenario, you can't 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 storage に格納されているエンティティと、BLOB ストレージに格納されている BLOB。An entity stored in Table storage and a blob stored in Blob storage.
  • Table storage に格納されているエンティティとファイル システム内のファイル。An entity stored in Table storage and a file in a file system.
  • Table storage に格納されているが、Azure Cognitive Search を使用してインデックスが作成されたエンティティ。An entity stored in Table storage, yet indexed by using Azure Cognitive Search.

解決策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 former employee entities. 以前の従業員エンティティはめったに照会されず、現在の従業員を対象にしたすべてのアクティビティから除外する必要があります。Former 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 former employees in the Archive table. 従業員をアーカイブするには、現在テーブルからのエンティティを削除し、アーカイブ テーブルにエンティティを追加する必要があります。Archiving an employee requires you to delete the entity from the Current table, and add the entity to the Archive table.

しかし、EGT を使用してこれら 2 つの操作を実行することはできません。But you can't 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.

最終的整合性を考慮したソリューションの図

クライアントは、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 in the diagram inserts the employee into the Archive table. BLOB ストレージ内の BLOB またはファイル システム内のファイルに従業員を追加できます。It can add the employee to a blob in Blob storage or a file in a file system.

エラーからの復旧Recover from failures

worker ロールがアーカイブ操作を再開する必要がある場合、図中の手順 4 ~ 5 がべき等になっていることが重要です。It's important that the operations in steps 4-5 in the diagram be idempotent in case the worker role needs to restart the archive operation. Table storage を使用している場合、手順 4 で "挿入または置換" 操作を使用する必要があります。手順 5 では、使用しているクライアント ライブラリで "存在する場合は削除" 操作を使用する必要があります。If you're using Table storage, 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're using. 他のストレージ システムを使用する場合は、適切なべき等操作を使用する必要があります。If you're using another storage system, you must use an appropriate idempotent operation.

worker ロールが図の手順 6 を完了しない場合は、タイムアウトの後、メッセージが worker ロール準備完了のキューに表示され再処理を試みます。If the worker role never completes step 6 in the diagram, 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 as 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 storage と Queue storage のエラーには一時的なエラーもあります。クライアント アプリケーションには、そうしたエラーに対処する適切な再試行ロジックを組み込む必要があります。Some errors from Table storage and Queue storage 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 doesn't provide for transaction isolation. たとえば、worker ロールが手順 4 ~ 5 の間にあるとき、クライアントは現在テーブルとアーカイブ テーブルを読み取る可能性があり、クライアントから見たデータのビューには一貫性がない可能性があります。For example, a client might read the Current and Archive tables when the worker role was between steps 4-5 in the diagram, and see an inconsistent view of the data. データは最終的に一貫性が確保されます。The data will be consistent eventually.
  • 最終的に一貫性を確保するために、手順 4 ~ 5 がべき等になっていることを確認する必要があります。You must be sure that steps 4-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 storage と BLOB ストレージのほかにも、データベースやファイル システムなどの Azure 以外のストレージ データ ソース間の操作で最終的な一貫性を確保できます。You can extend this pattern to ensure eventual consistency for operations across Table storage and Blob storage, and other non-Azure Storage data sources, such as a database or the file system.

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

注意

ソリューションにとってトランザクションの分離が重要な場合は、EGT を使用できるようにテーブルを再設計することを検討してください。If transaction isolation is important to your solution, 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 storage では、PartitionKeyRowKey 値を使用してエンティティのインデックスを自動的に作成します。Table storage automatically indexes entities by using the PartitionKey and RowKey values. そうすると、クライアント アプリケーションでポイント クエリを使用してエンティティを効率的に取得できます。This enables a client application to retrieve an entity efficiently by using a point query. たとえば、次のテーブル構造を使用することにより、クライアント アプリケーションでは、部署名と従業員 ID (PartitionKeyRowKey) から、個々の従業員エンティティを効率的に取得できます。For example, using the following table structure, 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 last name, you must use a less efficient partition scan. このスキャンでは、インデックスを使用して直接参照するのではなく、一致を検索します。This scan finds matches, rather than using an index to look them up directly. これは、Table storage ではセカンダリ インデックスが提供されないためです。This is because Table storage doesn't provide secondary indexes.

解決策Solution

前述のエンティティ構造の場合、姓で検索できるようにするには、従業員 ID の一覧を保持する必要があります。To enable lookup by last name with the preceding entity structure, 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) の値の一覧が格納されます。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 who have that last name. 従業員を追加または削除した場合は、関連する BLOB の内容と従業員エンティティの一貫性が最終的に確保されていることを確認してください。When you add or delete an employee, 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

次のデータを格納するインデックス エンティティを使用します。Use index entities that store the following data:

従業員エンティティと、同じ姓を持つ従業員 ID の一覧を含む文字列を示した図

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

次の手順は、新しい従業員を追加するときに従う必要がある手順の概要を示しています。The following steps outline the process you should follow when you're adding a new employee. この例では、ID が 000152、姓が Jones の従業員を Sales 部署に追加します。In this example, we're adding an employee with ID 000152 and 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") を挿入し、インデックス エンティティ (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"). EGT は、新しい従業員 ID を EmployeeIDs フィールドの一覧に追加することによってこれを行います。The EGT does this by adding the new employee ID to the list in the EmployeeIDs field. EGT の詳細については、「エンティティ グループ トランザクション」を参照してください。For more information about EGTs, see Entity group transactions.
  3. オプティミスティック コンカレンシー エラー (他のユーザーがインデックス エンティティを変更したこと) が原因で EGT が失敗した場合は、手順 1 からやり直す必要があります。If the EGT fails because of an optimistic concurrency error (that is, someone else has modified the index entity), then you need to start over at step 1.

2 番目の方法を使用する場合は、同じような方法で従業員を削除できます。You can use a similar approach to deleting an employee if you're using the second option. 従業員の姓を変更するのは、3 つのエンティティ (従業員エンティティ、元の姓のインデックス エンティティ、新しい姓のインデックス エンティティ) を更新する EGT を実行する必要があるため、少し複雑です。Changing an employee's last name is slightly more complex, because you need to run an EGT 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 by using optimistic concurrency.

次の手順は、ある部署で特定の姓を持つすべての従業員を検索する必要があるときに従う必要がある手順の概要を示しています。The following steps outline the process you should follow when you need to look up all the employees with a particular last name in a department. この例では、Sales 部署で姓が Jones のすべての従業員を検索します。In this example, we're 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 by 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

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

従業員エンティティと、同じ姓を持つ従業員 ID の一覧を含む文字列を示した図

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

インデックス エンティティが従業員エンティティとは別のパーティションにあるため、EGT を使用して一貫性を保つことはできません。You can't 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 を超えないと仮定しています。Because 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 particular last name is never more than 1 MB. 従業員 ID の一覧のサイズが 1 MB を超える可能性がある場合は、方法 1 を使用して、BLOB ストレージにインデックス データを格納します。If the list of employee IDs is likely to be more 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 particular partition. この場合は、最終的に一貫性のあるソリューション (方法 1 または方法 3) を検討する必要があります。If this is the case, you should consider an eventually consistent solution (option 1 or option 3). これらはキューを使用して更新要求を処理し、従業員エンティティとは別のパーティションにインデックス エンティティを格納できるようにします。These use queues to handle the update requests, and enable you to store your index entities in a separate partition from the employee entities.
  • このソリューションの方法 2 では、部署内で姓によって検索することを想定しています。Option 2 in this solution assumes that you want to look up by last name within a department. たとえば、姓が Jones で営業部に所属する従業員の一覧を取得するとします。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. 詳細については、「最終的に一貫性のあるトランザクション パターン」を参照してください。For more details, see the Eventually consistent transactions pattern.

このパターンを使用する状況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 might 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 that occurs when queries 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 回のラウンド トリップが必要です。For example, with the following table structure, you need two round trips to retrieve the details for a department. 1 回目でマネージャーの ID を含む部署エンティティをフェッチし、2 回目でマネージャーの詳細を従業員エンティティにフェッチします。One trip fetches the department entity that includes the manager's ID, and the second trip fetches 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 by 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. Table storage への要求が減ることによるパフォーマンスの向上は、通常、ストレージ コストのわずかな増加よりも有益です。The performance benefit resulting from fewer requests to Table storage typically outweighs the marginal increase in storage costs. さらに、このコストは、部署の詳細を取得するために必要なトランザクション数の削減によって部分的に相殺されます。Further, 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 を使用して 1 回のアトミックなトランザクションで複数のエンティティを更新することによって、一貫性の問題に対処できます。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 might 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's 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 storage に従業員エンティティを格納しているとします。Assume you are storing employee entities in Table storage by 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:

従業員レビュー エンティティの図

この方法では、単一の要求でデータを取得できるようにするには、一部の情報 (姓や名など) を新しいエンティティに複製する必要があります。With this approach, you might 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 can't maintain strong consistency because you can't use an EGT to update the two entities atomically.

解決策Solution

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

複合キーを持つ従業員エンティティの図

RowKey が複合キーになっていることに注目してください。これは、従業員 ID とレビュー データの年で構成されています。Notice how the RowKey is now a compound key, made up of the employee ID and the year of the review data. これにより、1 つのエンティティに対する 1 つの要求で、従業員のパフォーマンスを取得し、データを確認することができます。This 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.
  • また、このエンティティは、同じ従業員の関連データを含む他のエンティティと同じパーティションに格納します。You're also storing this entity in the same partition as other entities that contain related data for the same employee. こうすると、EGT を使用して、厳密な一貫性を維持できます。This means you can use EGTs to maintain strong consistency.
  • このパターンが適切であるかどうかを判断するには、データを照会する頻度を考慮する必要があります。You should consider how frequently you'll query the data to determine whether this pattern is appropriate. たとえば、評価データにはあまり頻度にアクセスせず、メインの従業員データには頻度にアクセスする場合は、それらのデータを別々のエンティティとして保持する必要があります。For example, if you 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 might 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.

注意

Azure Cosmos DB で Azure Table API によって返されるクエリ結果は、パーティション キーや行キーの順序にはなりません。Query results returned by the Azure Table API in Azure Cosmos DB aren't sorted by partition key or row key. そのため、このパターンは Table storage には適していますが、Azure Cosmos DB には適していません。Thus, while this pattern is suitable for Table storage, it isn't suitable for Azure Cosmos DB. 機能の相違に関する詳細なリストについては、Azure Cosmos DB の Table API と Azure Table Storage の間の相違に関するページを参照してください。For a detailed list of feature differences, see differences between Table API in Azure Cosmos DB and Azure Table Storage.

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

直近に作成されたエンティティ (従業員が提出した経費請求を日時の新しいものから 10 件など) の取得が必要となる場合がよくあります。A common requirement is to be able to retrieve the most recently created entities, for example the ten most recent expense claims submitted by an employee. テーブル クエリは、セットから最初の n 個のエンティティを返す $top クエリ操作をサポートします。Table queries support a $top query operation to return the first n entities from a set. セット内の最後の n 個のエンティティを返す同等のクエリ操作はありません。There's no equivalent query operation to return the last n entities in a set.

解決策Solution

日付/時刻の逆順で自然に並べ替える RowKey を使用してエンティティを格納し、最新のエントリが常にテーブルの最初のエントリになるようにします。Store the entities by using a RowKey that naturally sorts in reverse date/time order, so the most recent entry is always the first one in the table.

たとえば、従業員が提出した経費請求を日時の新しいものから 10 件取得できるようにする場合は、現在の日時から派生した逆順のティック値を使用できます。For example, to be able to retrieve the ten 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 by 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 to 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 might 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. 通常は、日付でそうしたデータを特定します。You typically identify such data by a date. たとえば、60 日以上前のすべてのサインイン要求のレコードを削除する必要があるとします。For example, you have a requirement to delete records of all sign-in requests that are more than 60 days old.

1 つ候補となるのは、RowKey でサインイン要求の日付と時刻を使用する設計です。One possible design is to use the date and time of the sign-in request in the RowKey:

ログイン試行エンティティの図

この方法では、アプリケーションが別のパーティションで各ユーザーのサインイン エンティティを挿入したり削除したりできるため、パーティションのホット スポットを回避できます。This approach avoids partition hotspots, because the application can insert and delete sign-in entities for each user in a separate partition. ただし、多数のエンティティがある場合、このアプローチはコストが高く、時間がかかる可能性があります。However, this approach can be costly and time consuming if you have a large number of entities. まず、削除するすべてのエンティティを識別するためにテーブル スキャンを実行する必要があり、次に、古いエンティティをそれぞれ削除する必要があります。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 sign-in attempts. 前述のエンティティの設計を使用すると、エンティティを挿入する際にホットスポットを回避できます。You can use the preceding entity design to avoid hotspots when you are inserting entities. 毎日数百や数千もの個々のサインイン エンティティを検索して削除する代わりに、毎日テーブルを 1 つ削除する (単一のストレージ操作) だけで古いエンティティを削除できます。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 sign-in 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 storage ではアクセス パターンを学習して、ノード全体にパーティションを分散しますが、最初に新しいテーブルを使用するときは何らかのレート制限が行われます。Expect some rate limiting when you first use a new table, while Table storage 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 might 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:

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

この設計では、アプリケーションでメッセージのカウント値を更新する必要があるときに、各従業員の更新するエンティティを簡単に検索して更新できます。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 by 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 doesn't 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, use the ETag to implement optimistic concurrency. クライアントがたくさんある場合は、競合が大量に発生する可能性があります。If you have many clients, you might 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 might 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 can't store more than 1 MB of data in total. リレーショナル データベースでは、通常、新しいテーブルを追加し、その新しいテーブルと 1 対 1 のリレーションシップを作成することによって、行のサイズに関するさまざまな制限を回避します。In a relational database, you would typically work around any limits on the size of a row by adding a new table, and enforcing a 1-to-1 relationship between them.

解決策Solution

Table storage を使用することで、複数のエンティティを格納して、252 を超えるプロパティを持つ単一の大きなビジネス オブジェクトを作成できます。By using Table storage, 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 can use the following design that uses two entities with different schemas:

Rowkey 01 のメッセージ統計情報エンティティと Rowkey 02 のメッセージ統計情報エンティティを示す図

両方のエンティティを更新しないとエンティティどうしの同期が維持されない変更を加える必要がある場合は、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. 個々の従業員のすべてのデータを取得するには、両方のエンティティを取得する必要があります。To retrieve all the data for an individual employee, you must retrieve both entities. これは、PartitionKeyRowKey 値の両方を使用する 2 つの効率的な要求で行うことができます。You can do this with two efficient requests that use both a PartitionKey and a RowKey value.

問題と注意事項Issues and considerations

このパターンの実装方法を決めるときには、次の点に注意してください。Consider the following point 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 storage の個々のエンティティの制限を超えるエンティティを格納する必要がある場合に、このパターンを使用します。Use this pattern when you need to store entities whose size or number of properties exceeds the limits for an individual entity in Table storage.

このパターンを実装する場合は、次のパターンとガイダンスも関連している可能性があります。The following patterns and guidance might 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 can't store more than 1 MB of data in total. 1 つまたは複数のプロパティに格納される値でエンティティの合計サイズが 1 MB を超える場合は、Table storage にエンティティ全体は格納できません。If one or several of your properties store values that cause the total size of your entity to exceed this value, you can't store the entire entity in Table storage.

解決策Solution

1 つ以上のプロパティに大量のデータが含まれているためにエンティティのサイズが 1 MB を超える場合は、BLOB ストレージにデータを格納し、エンティティのプロパティに 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 Blob storage, and then store the address of the blob in a property in the entity. たとえば、従業員の写真を BLOB ストレージに格納し、写真へのリンクを従業員エンティティの Photo プロパティに格納できます。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:

Photo が BLOB ストレージを指す文字列の従業員エンティティを示す図

問題と注意事項Issues and considerations

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

  • Table storage 内のエンティティと、BLOB ストレージ内のデータの間の最終的な一貫性を保つには、最終的に一貫性のあるトランザクション パターンを使用してエンティティを維持します。To maintain eventual consistency between the entity in Table storage and the data in Blob storage, 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 storage の個々のエンティティの制限を超えるエンティティを格納する必要がある場合に、このパターンを使用します。Use this pattern when you need to store entities whose size exceeds the limits for an individual entity in Table storage.

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

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

大量に挿入する場合に、挿入を複数のパーティションに分散させることで、スケーラビリティを向上させます。When you have a high volume of inserts, increase scalability 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. この場合、特定の時点におけるすべての挿入が同じパーティションで発生し、ホットスポットになります。In this case, all of the inserts at any particular time are taking place in the same partition, creating a hotspot. これは、Table storage が挿入を複数のノードに負荷分散することを妨げ、アプリケーションがパーティションのスケーラビリティ ターゲットに達する原因となる可能性があります。This prevents Table storage from load-balancing inserts across multiple nodes, and possibly causes your application to hit the scalability targets for partition. たとえば、従業員によるネットワークとリソースへのアクセスをログに記録するアプリケーションの場合を考えてみます。For example, consider the case of an application that logs network and resource access by employees. トランザクションの量が個々のパーティションのスケーラビリティ ターゲットに達した場合、次のようなエンティティ構造が原因で、現在の時間のパーティションがホットスポットになる可能性があります。An entity structure such as the following can 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:

年、月、日、時間、イベント ID から構成される RowKey の従業員エンティティを示す図

次の例では 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?
  • 予想されるトランザクションの量から判断して、個々のパーティションのスケーラビリティ ターゲットに達し、Table storage によって調整される可能性がありますか。Does your anticipated volume of transactions mean that you're likely to reach the scalability targets for an individual partition, and be throttled by Table storage?

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

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

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

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

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

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

ログ データの一般的なユース ケースは、特定の日付/時刻範囲のログ エントリを選択して取得するというものです。A common use case for log data is to retrieve a selection of log entries for a specific date/time range. たとえば、特定の日付の 15:04 から 15:06 までの間にアプリケーションがログに記録したすべてのエラー メッセージと重大なメッセージを検索するとします。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. ログ エンティティを保存するパーティションを決定するために、ログ メッセージの日付と時刻を使用することは望まないものとします。You don't want to use the date and time of the log message to determine the partition you save log entities to. その結果、どの特定の時点においても、すべてのログ エンティティが同じ PartitionKey 値を共有するため、ホット パーティションが発生します (「先頭または末尾に追加するアンチパターン」を参照)。That results in a hot partition because at any particular time, all the log entities will share the same PartitionKey value (see the 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 にはログ メッセージの日付と時刻が含まれています。In this example, the RowKey includes the date and time of the log message to ensure that log messages are sorted in date/time order. 複数のログメッセージが同じ日付と時刻を共有する場合、RowKey にはメッセージ ID も含まれます。The RowKey also 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 can 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 storage を使用した場合に生じる問題について説明し、その解決策として、完璧とは言いがたいものの 2 つの設計を紹介しました。The previous section highlighted the problem of trying to use Table storage to store log entries, and suggested two unsatisfactory designs. 1 つ目に紹介した方法には、ホット パーティションが発生し、ログ メッセージの書き込みのパフォーマンスが低下するリスクがあります。One solution led to a hot partition with the risk of poor performance writing log messages. これに対して 2 つ目の方法は、特定のタイム スパンについてログ メッセージを取得しようとした場合に、テーブル内のパーティションを逐一スキャンしなければならないため、クエリのパフォーマンスが低下するという問題がありました。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 July 31, 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 を探すことができます。The API for Blob storage enables you locate blobs in a container based on a name prefix. 18:00 から始まる 1 時間のキュー ログ データを含むすべての BLOB を探すには、"queue/2014/07/31/1800" というプレフィックスを使用できます。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 Blob storage.

独自のアプリケーションに同様のソリューションを実装する場合は、信頼性とコストとスケーラビリティのトレードオフを管理する方法を検討してください。If you're implementing a similar solution in your own application, consider how to manage the trade-off between reliability and cost and scalability. つまり、すべてのログ エントリを発生次第 BLOB ストレージに書き込む効果を、アプリケーションで更新をバッファリングしてバッチで BLOB ストレージに書き込む場合と比較して評価します。In other words, evaluate the effect of writing every log entry to Blob storage as it happens, compared to 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 might find that you can't 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 might 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).

エンティティの取得Retrieve entities

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

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

ポイント クエリを実行する最も簡単な方法は、Retrieve テーブル操作を使用することです。The easiest way to run a point query is to use the Retrieve table operation. 次の C# コード スニペットに示すように、この操作は、PartitionKey の値が "Sales"、RowKey の値が "212" であるエンティティを取得します。As shown in the following C# code snippet, this operation 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;
    ...
}  

この例では、取得するエンティティが EmployeeEntity 型であることをどのように想定しているかに注意してください。Notice how this example expects the entity it retrieves to be of type EmployeeEntity.

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

複数のエンティティを取得するには、ストレージ クライアント ライブラリとともに LINQ を使用し、where 句のあるクエリを指定します。You can retrieve multiple entities by using LINQ with Storage Client Library, and 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 storage は、where 句で一部の比較演算子 (より大きい、以上、より小さい、以下、等しい、等しくない) のみサポートしています。Table storage 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.

次のコード サンプルは、fluent API を使用したのと同等の機能を示しています (fluent API 全般の詳細については、fluent API を設計するためのベスト プラクティスに関する記事を参照してください)。The following code sample shows equivalent functionality by using the fluent API (for more information about fluent APIs in general, see Best practices for designing a fluent API):

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 つのフィルター条件を含んだ複数の CombineFilters メソッドをネストしています。The sample nests multiple CombineFilters methods to include the three filter conditions.

1 件のクエリで大量のエンティティを取得するRetrieve 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 might 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 storage に対するクエリは、一度に 1,000 件までのエンティティを返すことができ、最大 5 秒間実行できます。A query against Table storage can return a maximum of 1,000 entities at one time, and run for a maximum of five seconds. 次のいずれかに該当する場合、Table storage は継続トークンを返して、クライアント アプリケーションが次のエンティティ セットを要求できるようにします。Table storage returns a continuation token to enable the client application to request the next set of entities, if any of the following are true:

  • 結果セットに 1,000 件を超えるエンティティが含まれている。The result set contains more than 1,000 entities.
  • クエリが 5 秒以内に完了しなかった。The query didn't complete within five seconds.
  • クエリがパーティション境界を越えている。The query crosses the partition boundary.

継続トークンの動作の詳細については、クエリのタイムアウトと改ページに関する記事を参照してください。For more information about how continuation tokens work, see Query timeout and pagination.

ストレージ クライアント ライブラリを使用している場合には、Table storage からエンティティが返されるたびに継続トークンが自動で処理されます。If you're using the Storage Client Library, it can automatically handle continuation tokens for you as it returns entities from Table storage. たとえば、次の C# コード サンプルは、Table storage が応答で継続トークンを返す場合に、継続トークンを自動的に処理します。For example, the following C# code sample automatically handles continuation tokens if Table storage 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 might decide not to page through all the entities retrieved by the query. アプリケーションは、ユーザーが現在のセグメントのすべてのエンティティをページ表示し終えた時点ではじめて、継続トークンを使用して次のセグメントを取得します。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 storage から取得する、またネットワーク経由で移動するデータの量を制限できます。You can limit the amount of data to retrieve from Table storage and that you move over the network.
  • .NET で非同期 I/O を実行できます。You can perform asynchronous I/O in .NET.
  • 継続トークンをシリアル化して永続記憶装置に保存できるため、アプリケーションがクラッシュした場合でも処理を継続できます。You can 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 can contain fewer. これは、検索条件に一致する最初の n 個のエンティティを返すために、Take を使用して、クエリが返すエントリの数を制限する場合にも当てはまります。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. Table storage は、n 個よりも少ないエンティティを含むセグメントを、残りのエンティティを取得するための継続トークンと共に返す場合があります。Table storage might 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 might 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. 次の例は、クエリによって選択されたエンティティから Email プロパティ (PartitionKeyRowKeyTimestampETag と連動) のみを取得しています。The following example 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 isn't included in the list of properties to retrieve.

エンティティの変更Modify entities

ストレージ クライアント ライブラリを使えば、Table storage に格納されたエンティティを、挿入、削除、更新の各操作によって変更できます。The Storage Client Library enables you to modify your entities stored in Table storage 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 runs 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

既定では、Table storage は個々のエンティティのレベルで挿入、マージ、削除の各操作に対してオプティミスティック コンカレンシー チェックを実装しますが、クライアントは、Table storage がこれらのチェックをバイパスするよう強制することもできます。By default, Table storage implements optimistic concurrency checks at the level of individual entities for insert, merge, and delete operations, although it's possible for a client to force Table storage to bypass these checks. 詳細については、「Microsoft Azure Storage でのコンカレンシー制御の管理」を参照してください。For more information, see Managing concurrency in Microsoft Azure Storage.

マージまたは置換Merge or replace

TableOperation クラスの Replace メソッドは、Table storage 内のエンティティ全体を常に置き換えます。The Replace method of the TableOperation class always replaces the complete entity in Table storage. 格納されたエンティティに存在するプロパティを要求に含めない場合、要求により、格納されたエンティティからそのプロパティが削除されます。If you don't 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 クラスの Merge メソッドを使用すると、エンティティを更新するときに Table storage に送信するデータの量を削減できます。You can use the Merge method of the TableOperation class to reduce the amount of data that you send to Table storage when you want to update an entity. Merge メソッドは、格納されているエンティティのプロパティを、要求に含まれているエンティティのプロパティ値に置き換えます。The Merge method replaces any properties in the stored entity with property values from the entity included in the request. このメソッドは、格納されているエンティティのプロパティで、要求に含まれていないものはそのままにします。This method leaves intact any properties in the stored entity that aren't 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.

注意

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

異種のエンティティ種類を使用するWork with heterogeneous entity types

Table storage はスキーマレスのテーブル ストアです。Table storage is a schema-less table store. これは、1 つのテーブルが複数の種類のエンティティを格納できるため、非常に柔軟な設計が提供できることを意味します。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 can have any set of properties. さらに、エンティティの種類を示すものがありません (エンティティの種類に関する情報を格納していない場合)。Furthermore, there's 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 following table.
パーティション キー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 aboutInheritance relationships.

注意

エンティティの種類の値にバージョン番号を追加して、クライアント アプリケーションで POCO オブジェクトを発展させ、さまざまなバージョンを操作できるようにすることを検討してください。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.

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

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

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

2 番目のオプションは、具象 POCO エンティティ型ではなく DynamicTableEntity 型 (プロパティ バッグ) を使用することです。The second option is to use the DynamicTableEntity type (a property bag), instead of a concrete POCO entity type. このオプションを選択すると、エンティティを .NET 型にシリアル化および逆シリアル化する必要がないため、パフォーマンスが向上する場合もあります。This option might also improve performance, because there's 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)
        {
    ...
        }
        if (e is EmployeeEntity)
        {
    ...
        }
}  

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

エンティティの種類がわからなくても削除はできますが、挿入はできません。You don't 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 the 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 that 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));  

共有アクセス署名を使用してアクセスを制御するControl access with shared access signatures

共有アクセス署名 (SAS) トークンを使用すると、クライアント アプリケーションで、Table storage に対して直接認証しなくてもテーブル エンティティを直接変更 (およびクエリ) できるようになります。You can use shared access signature (SAS) tokens to enable client applications to modify (and query) table entities directly, without the need to authenticate directly with Table storage. 通常、アプリケーションで SAS を使うと、次の 3 つのメリットが得られます。Typically, there are three main benefits to using SAS in your application:

  • デバイスで Table storage のエンティティにアクセスして変更できるようにするために、安全ではないプラットフォーム (モバイル デバイスなど) にストレージ アカウント キーを配布する必要がありません。You don't 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 Table storage.
  • エンティティの管理で Web ロールと worker ロールが実行する作業の一部をオフロードできます。You can offload some of the work that web and worker roles perform in managing your entities. エンド ユーザーのコンピューターやモバイル デバイスなどのクライアント デバイスにオフロードできます。You can offload 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 storage での SAS トークンの使用について、詳しくは共有アクセス署名 (SAS) の使用に関するページを参照してください。For more information about using SAS tokens with Table storage, see Using shared access signatures (SAS).

ただし、クライアント アプリケーションを Table storage 内のエンティティに許可する SAS トークンを生成する必要があることは変わりません。However, you must still generate the SAS tokens that grant a client application to the entities in Table storage. これは、ストレージ アカウント キーに安全にアクセスできる環境で行ってください。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's possible to generate a SAS token that grants access to a subset of the entities in a table. 既定では、テーブル全体に対する SAS トークンを作成します。By default, you create a SAS token for an entire table. ただし、PartitionKey 値の範囲、または PartitionKey 値と RowKey 値の範囲へのアクセスを SAS トークンが許可するように指定することもできます。But it's 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 storage 内にあるユーザー独自のエンティティだけになります。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 Table storage.

非同期と並列操作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 can 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 running 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 メソッドを呼び出すようになりました。Instead of calling the ExecuteSegmented method to retrieve results, the method now calls the ExecuteSegmentedAsync method. メソッドは await 修飾子を使用して結果を非同期に取得します。The method uses the await modifier to retrieve results asynchronously.

クライアント アプリケーションは、department パラメーターの値を変えて、このメソッドを複数回呼び出すことができます。The client application can call this method multiple times, with different values for the department parameter. 各クエリは個別のスレッドで実行されます。Each query runs on a separate thread.

IEnumerable インターフェイスは非同期列挙型をサポートしていないため、TableQuery クラスの Execute メソッドには非同期バージョンはありません。There is no asynchronous version of the Execute method in the TableQuery class, because the IEnumerable interface doesn't 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 メソッドを呼び出すようになりました。Instead of calling the Execute method to update the entity, the method now calls the ExecuteAsync method. メソッドは await 修飾子を使用して結果を非同期に取得します。The method uses the await modifier to retrieve results asynchronously.

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