エクスポート (0) 印刷
すべて展開

Azure テーブル ストレージのスケーラブルなパーティション分割方法の設計

更新日: 2014年8月

執筆者: https://msdn.microsoft.com/ja-jp/library/hh307529.aspx

参照画像

RBA Consulting (詳細は左をクリック)

概要 この記事では、Azure テーブルのパーティション分割についてのトピックと、スケーラビリティを効果的に利用するための戦略について説明します。

Azure により、可用性が常時確保され、拡張性と信頼性も高いデータ ストレージが実現します。Azure の基盤となっているストレージ システムは、オブジェクト抽象化セット (サービスと呼ばれることが多い) を介して提供されます。このサービス セットには、テーブル、BLOB、キューの各サービスが含まれます。これらのサービスは、お客様のほとんどの開発ニーズをサポートします。アプリケーションで構造化データの保存が必要である場合、Azure テーブルは最適な選択肢になります。Azure Storage サービスでサポートされるテーブルの数には制限がなく、各テーブルはきわめて大きなレベルに拡張できます。テーブルのスケールアウトは自動的に行われ、10 億個単位のエンティティ (テラバイト級の物理ストレージに相当) を格納します。テーブル機能を利用するためには、最適な PartitionKey 値を使用してデータをパーティション分割する必要があります。この記事では、Azure テーブル ストレージでデータを効率的にパーティション分割する方法について説明します。

テーブル エンティティとは、テーブルに格納されたデータの単位であり、一般的なリレーショナル データベースのテーブルの行に似ています。各エンティティは、プロパティのコレクションを定義します。各プロパティはキー/値のペアで、名前、値、値のデータ型で定義されます。エンティティでは、プロパティ コレクションの一部として、次の 3 つのシステム プロパティを定義する必要があります。

  • PartitionKey - PartitionKey プロパティには、エンティティが属するパーティションを識別する文字列値が格納されます。つまり、同じ PartitionKey 値を持つエンティティは同じパーティションに属することを意味します。後で説明しますが、パーティションはテーブルのスケーラビリティにとって不可欠です。

  • RowKey - RowKey プロパティには、各パーティションに属するエンティティを一意に識別する文字列値が格納されます。

  • Timestamp – Timestamp プロパティは、エンティティを追跡するためのプロパティです。タイムスタンプとは、エンティティが最後に変更された時点を示す DateTime 値で、エンティティのバージョンと呼ばれることもあります。どのような挿入操作や更新操作が行われてもテーブル サービスはタイムスタンプ プロパティの値を保持するため、タイムスタンプへの変更操作は無視されます。

データベース テーブルの主キーは、各行を一意に識別するための列を定義します。これは、Azure のテーブルでも同じです。Azure テーブルの主キーは、PartitionKey と RowKey で、テーブル内の単一のクラスター化インデックスを構成します。PartitionKey と RowKey の各プロパティには、1 KB までの文字列値を格納できます。空の文字列も格納できますが、Null 値は格納できません。クラスター化インデックスは、PartitionKey で、次に RowKey で昇順に並べ替えられます。この並べ替え順は、あらゆるクエリ応答で見られるものです。並べ替え操作では、辞書式比較が使用されます。したがって、文字列値 "111" は、文字列値 "2" よりも前に現れます。場合によっては、数値で並べ替える必要があることもあります。数値の昇順で並べ替えるには、ゼロを加えて固定長にした文字列を使用する必要があります。上の例では、"002" とすることで、この文字列を "111" よりも前に出現させることができます。

パーティションとは、同じ PartitionKey 値を持つエンティティの集まりです。パーティションは常に 1 つのパーティション サーバーから提供されますが、各パーティション サーバーは、1 つ以上のパーティションを提供することができます。パーティション サーバーが 1 つのパーティションを介して一定時間に提供できるエンティティ数には、レート制限があります。具体的には、パーティションのスケーラビリティ ターゲットは毎秒 500 エンティティです。このスループットは、ストレージ ノードの負荷が最小限の場合には比較的高いと言えますが、ストレージ ノードの負荷が大幅に高まった場合には減速されます。次の図は、パーティション分割の概念をわかりやすく説明するために、マラソン大会の記録の小さなサブセット データを含む表を示しています。このパーティション分割の概念図において、表の PartitionKey は、イベント名と走行距離を含む 3 つの値で構成されています。この例では、2 つのパーティション サーバーが示されています。サーバー A には、ハーフマラソンと 10 Km の記録が登録されており、サーバー B には、フルマラソンの記録だけが登録されています。RowKey 値には、記録の内容が示されていますが、この例の場合は重要ではありません。

参照画面

パーティションは常に 1 つのパーティション サーバーから提供され、各パーティション サーバーは 1 つ以上のパーティションを提供できるため、エンティティの提供の効率はサーバーの正常性と相関します。パーティションに関して大量のトラフィックが発生するサーバーは、高スループットを維持できません。たとえば、上の図で、"2011 New York City Marathon__Half" に対して多数の要求がある場合、サーバー A は過負荷な状態になります。ストレージ システムは、サーバーのスループットを向上させるために、パーティションの負荷を他のサーバーに分散させます。その結果、トラフィックは他の多数のサーバー間で分散されます。トラフィックの負荷分散を最適化するためには、Azure テーブル サービスがパーティションを多数のパーティション サーバーに分散できるように、より多くのパーティションを使用する必要があります。

エンティティ グループ トランザクションは、同じ PartitionKey 値を持つエンティティに対して原子的に実施される 1 セットのストレージ操作です。エンティティ グループ内のあるストレージ操作が失敗すると、グループ内のすべてのストレージ操作がロールバックされます。エンティティ グループ トランザクションは、100 以下のストレージ操作で構成され、サイズは 4 MB 以下です。エンティティ グループ トランザクションは、リレーショナル データベースが提供する限定された形態の原子性、一貫性、分離性、持続性 (ACID) というセマンティクスを Azure テーブルにもたらします。エンティティ グループ トランザクションによって、Azure テーブル サービスに送信される個々のストレージ操作の数が減少するため、スループットが向上します。また、エンティティ グループ トランザクションは、含まれるストレージ操作数にかかわらず、1 回のストレージ操作として課金されるため、経済的なメリットもあります。エンティティ グループ トランザクションに含まれるストレージ操作はすべて、同じ PartitionKey 値を持つエンティティに影響するため、エンティティ グループ トランザクションを使用したいという要望が PartitionKey 値の選択を促進する可能性があります。

エンティティに対して一意の PartitionKey 値を使用している場合には、各エンティティはその値のパーティションに属します。使用している一意の値の数値が増加または減少している場合、Azure によって範囲パーティションが作成される可能性があります。範囲パーティションは、連続する一意の PartitionKey 値を持つエンティティをグループ化することにより、範囲クエリのパフォーマンスを向上させます。範囲パーティションがない場合には、範囲クエリがパーティションやサーバーの境界をまたぐ必要があるため、クエリのパフォーマンスが低下する可能性があります。次のように、PartitionKey の値が連続的に増加するテーブルを使用するアプリケーションを考えてみましょう。

 

PartitionKey

RowKey

"0001"

-

"0002"

-

"0003"

-

"0004"

-

"0005"

-

"0006"

-

Azure が最初の 3 つのエンティティを 1 つの範囲パーティションにグループ化した場合を考えます。PartitionKey を基準として使用して "0001" から "0003" までのエンティティを要求する範囲クエリをこのテーブルに適用した場合は、クエリは効率的に実行されます。ただ 1 つのパーティション サーバーからエンティティが提供されるためです。ただし、範囲パーティションが作成されるタイミングと方法についての保証はありません。

増加または減少する PartitionKey 値を持つエンティティを挿入している場合、テーブルに範囲パーティションが存在していると、挿入操作のパフォーマンスに影響する可能性があります。PartitionKey 値が増加するエンティティの挿入は、アペンド オンリー (Append Only) パターンと呼ばれ、減少するエンティティの挿入は、プリペンド オンリー (Prepend Only) パターンと呼ばれます。このようなパターンの使用は避けてください。挿入要求が単一のパーティション サーバーによって処理され、全体的なスループットが制限されるためです。これは、次のような理由によります。範囲パーティションがあると、最初の (範囲) パーティションに最小の PartitionKey 値、最後のパーティションに最大の PartitionKey 値が含まれます。したがって、連続的に減少または増加する PartitionKey 値で新しいエンティティを挿入すると、最後のパーティションの 1 つがターゲットになるためです。次の図は、前の例に基づいた 1 セットの範囲パーティションの例を示しています。"0007"、"0008"、"0009" という一連のエンティティが挿入されると、これらのエンティティは最後の (オレンジ色の) パーティションに割り当てられます。

参照画面

ばらついた PartitionKey 値を使用して挿入操作を行ってもパフォーマンスにマイナスの影響はないことに注意してください。

複数のインデックスを管理できるリレーショナル データベースのテーブルとは異なり、Azure テーブルは、常に PartitionKey プロパティと RowKey プロパティとで構成されるインデックスを 1 つ持っているだけです。テーブルを展開した後で、インデックスの追加や既存のインデックスの変更を行って、テーブルのパフォーマンス チューニングをすることはできません。したがって、テーブルを設計したときの状態でデータを分析する必要があります。スケーラビリティの最適化や、クエリと挿入の効率を考えるとき、最も重要な要素は、PartitionKey 値と RowKey 値です。テーブルのパーティション分割には PartitionKey が直接関係するため、この記事では、PartitionKey の選択方法により多くの重点を置いています。

パーティション サイズとは、パーティションに含まれるエンティティの数です。「スケーラビリティ」のセクションで説明したように、パーティション数が多いほど、負荷分散が効果的に行われます。PartitionKey 値の粒度は、パーティションのサイズに影響します。最も粗いレベルで 1 つの値が PartitionKey として使用されると、すべてのエンティティが 1 つの非常に大きなパーティションに含まれます。また、粒度を最も細かいレベルにすると、PartitionKey には各エンティティごとに一意の値が格納されます。そのため、各エンティティに対して 1 つのパーティションがあるという結果になります。次の表は、粒度の範囲別にその長所と短所を示しています。

 

PartitionKey の粒度

パーティションのサイズ

長所

短所

単一の値

少数のエンティティ

どのエンティティでもバッチ トランザクションを実行できる。

すべてのエンティティがローカルで、同じストレージ ノードから提供される。

 

単一の値

多数のエンティティ

どのエンティティでもエンティティ グループ トランザクションを実行できる。エンティティ グループ トランザクションの制限の詳細については、http://msdn.microsoft.com/en-us/library/dd894038.aspx を参照してください。

スケーリングが制限される。

スループットが単一サーバーのパフォーマンスに制限される。

複数の値

複数のパーティションが存在する。

パーティション サイズはエンティティの分散状況による。

いくつかのエンティティでバッチ トランザクションを実行できる。

動的なパーティション分割を実行できる。

単一要求のクエリを実行できる (継続トークンなし)。

より多くのパーティション サーバー間で負荷分散できる。

パーティション間でエンティティの高度な不均一分散が行われるため、大きくてアクティブなパーティションではパフォーマンスが制限される可能性がある。

一意の値 (複数)

多数の小さなパーティションが存在する。

テーブルは高度にスケーラブルである。

範囲パーティションにより、パーティションをまたぐ範囲クエリのパフォーマンスが向上する可能性がある。

範囲を含むクエリの場合、複数のサーバーへのアクセスが必要になる可能性がある。

バッチ トランザクションが実行できない。

アペンド オンリー パターンやプリペンド オンリー パターンは挿入のスループットに影響する可能性がある。

この表は、PartitionKey 値によるスケーリングへの影響を示しています。パーティションが小さいほど負荷分散が効果的に行われるため、より小さなパーティションにすることをお勧めします。シナリオによっては大きなパーティションの方が適切で、必ずしも短所にならない場合もあります。たとえば、アプリケーションにスケーラビリティが必要ではない場合には、単一の大きなパーティションの方が適切です。

クエリは、テーブルからデータを取得します。Azure テーブルのデータを分析する場合、アプリケーションでどのクエリを使用するかを考慮することが重要です。アプリケーションに複数のクエリがある場合、決定が少し主観的になるとしても、クエリに優先順位を付けることが必要になる場合があります。多くの場合、重要なクエリとそうでないクエリは区別できます。パフォーマンスの点から、クエリはさまざまなカテゴリに分類されます。テーブルには 1 つのインデックスしかないため、クエリのパフォーマンスは通常、PartitionKey プロパティと RowKey プロパティに関係します。次の表は、さまざまなタイプのクエリとそのパフォーマンス評価を示しています。

 

クエリの種類

PartitionKey の一致

RowKey の一致

パフォーマンス評価

Point

完全一致

完全一致

最高

行範囲スキャン

完全一致

一部サポート

小さいサイズのパーティションが高評価。

大きなサイズのパーティションは低評価。

パーティション範囲スキャン

一部サポート

一部サポート

関係するパーティション サーバー数が少ない場合は高評価。

関係するパーティション サーバー数が多い場合は低評価。

テーブル全体スキャン

部分的に一致か、まったく一致せず

部分的に一致か、まったく一致せず

パーティションのサブセットがスキャンされる場合は低評価。

すべてのパーティションがスキャンされる場合は最低評価。

noteメモ
上の表は、相対的なパフォーマンス評価を示しています。最終的には、パーティションの数とサイズによってクエリのパフォーマンスが決まります。たとえば、多数の大きなパーティションがあるテーブルに対するパーティション範囲スキャンは、少数の小さいパーティションがあるテーブルに対するテーブル全体スキャンよりもパフォーマンスが低くなる可能性があります。

この表に記載したクエリ タイプは、それぞれのパフォーマンス評価に基づいて、使用するのに最適なクエリ タイプから最も不適切なタイプまでを、順番に示しています。ポイント クエリは、テーブルのクラスター化インデックスを最大限に活用するため、使用するのに最適なタイプのクエリです。次に示すポイント クエリは、マラソン大会の記録テーブルのデータを使用しています。

http://<account>.windows.core.net/registrations(PartitionKey=”2011 New York City Marathon__Full”,RowKey=”1234__John__M__55”)

アプリケーションで複数のクエリを使用する場合、そのすべてをポイント クエリにすることはできません。パフォーマンスの点で、範囲クエリはポイント クエリに次ぐ評価となっています。範囲クエリには、行範囲スキャンとパーティション範囲スキャンの 2 つのタイプがあります。行範囲スキャンでは、1 つのパーティションだけを指定します。このスキャンは 1 つのパーティション サーバーだけで行われるため、一般に、行範囲スキャンはパーティション範囲スキャンよりも効率的です。ただし、行範囲スキャンのパフォーマンスにとって重要な要因は、クエリの選択度です。クエリの選択度は、一致行を見つけるために反復処理が必要な行数を決定します。クエリの選択度が高いほど、行範囲スキャンの効率が高くなります。

クエリの優先順位を評価するためには、各クエリの頻度と応答時間要件を考慮する必要があります。頻繁に実行されるクエリは、優先順位が高いと考えられます。しかし、重要であってもまれにしか使用されないクエリは、待機時間要件は低くとも、優先順位リストでは高い位置にランクされる場合があります。

テーブル設計は、スケーラビリティ、テーブルにアクセスするために使用されるクエリ、およびストレージ操作要件に基づいて行われます。選択した PartitionKey 値によって、テーブルのパーティション分割の方法と使用可能なクエリのタイプが決まります。特殊な挿入の場合には、ストレージ操作が PartitionKey 値の選択に影響する場合もあります。PartitionKey 値は、単一の値や一意の値などさまざまですが、複数の値で構成される場合もあります。エンティティのプロパティは、PartitionKey 値となるように構成できます。また、アプリケーションでこの値を計算する場合もあります。

開発者は、アプリケーションでエンティティ グループ トランザクション (バッチ更新) を使用するかどうかを最初に検討する必要があります。エンティティ グループ トランザクションを使用する場合は、エンティティが同じ PartitionKey 値を持つ必要があります。また、バッチ更新はグループ全体に対して行われるため、PartitionKey 値の選択は制限される場合があります。たとえば、現金のトランザクションが維持される銀行アプリケーションでは、現金のトランザクションをテーブルに原子的に挿入する必要があります。これは、現金取引には貸し方と借り方の両側面があり、両者の合計はゼロになる必要があるためです。この要件は、PartitionKey のどの部分にも口座番号を使用できないことを意味しています。これはトランザクションのそれぞれの側で異なる口座番号を使用するためです。代替案としては、トランザクション ID がより自然な選択と考えられます。

パーティションの数とサイズは、負荷の下にあるテーブルのスケーラビリティに影響すると共に、PartitionKey 値の粒度によって制御されます。パーティションのサイズに基づいて PartitionKey を決定するのは困難な場合があり、値の分布の予測が難しい場合は特にそれが当てはまります。経験則からは、サイズが小さい複数のパーティションを使用することが推奨されます。パーティションの提供元であるストレージ ノードを Azure テーブル サービスで管理する際、テーブル パーティションが多い方がより簡単になります。

PartitionKey に一意の値か粒度の細かい値を選択すると、サイズが小さく数の多いパーティションになります。その場合、システムは多数のパーティションの負荷を多数のパーティション全体に分散させることができるため、一般に、この選択が好まれます。ただし、多数のパーティションの存在が、パーティションをまたぐ範囲クエリにどのように影響するかを考慮する必要があります。このタイプのクエリは、クエリを満たすために多数のパーティションにアクセスする必要があります。多数のパーティション サーバー全体にパーティションを分散することもできます。クエリがサーバーの境界をまたぐ場合には、継続トークンが返される必要があります。継続トークンは、次のサーバーの PartitionKey 値か RowKey 値を指定します。この値はクエリの次のデータ セットを取得します。つまり、継続トークンは、少なくとももう 1 つの要求をサービスに提示するため、クエリの全体的なパフォーマンスが低下する可能性があります。クエリの選択度は、クエリのパフォーマンスに影響するもう 1 つの要因です。クエリの選択度とは、各パーティションに対して反復される行数の指標です。クエリの選択度が高ければ高いほど、より高い効率で目的の行が返されます。範囲クエリの全体的なパフォーマンスは、接触する必要のあるパーティション サーバーの数やクエリの選択度に左右されます。また、データをテーブルに挿入する際には、アペンド オンリー パターンやプリペンド オンリー パターンの使用を避ける必要もあります。このようなパターンを使用すると、サイズが小さい多数のパーティションが作成されますが、挿入操作のスループットが制限される可能性があります。アペンド オンリー パターンとプリペンド オンリー パターンについては「範囲パーティション」のセクションを参照してください。

使用するクエリを知ることによって、PartitionKey について検討するうえで重要なプロパティを特定できます。クエリ内で使用されているプロパティは、PartitionKey の候補です。次の表は、PartitionKey を決定する方法について、一般的なガイドラインを示しています。

 

エンティティに...

処理

キー プロパティが 1 つある

キー プロパティを PartitionKey として使用する

キー プロパティが 2 つある

一方を PartitionKey、他方を RowKey として使用する

キー プロパティが 2 つより多い

値を連結した複合キーを使用する

等しく重要なクエリが複数ある場合には、異なる RowKey 値を使用して必要な回数だけ何度も情報を挿入できます。2 番目 (または 3 番目など) の行は、アプリケーションによって管理されます。このパターンで、クエリのパフォーマンス要件を満たすことができます。次の例は、マラソン大会の記録の例で示したデータを使用しています。この中には重要なクエリが 2 つあります。次に例を示します。

  • BIB ナンバー (ゼッケン番号) によるクエリ

  • 年齢によるクエリ

重要なクエリを両方とも実行するには、2 つの行をエンティティ グループ トランザクションとして挿入します。次の表は、このシナリオにおける Partitionkey プロパティと RowKey プロパティを示しています。RowKey 値には、BIB と年齢に対するプレフィックスが示されており、この 2 つの値をアプリケーションが識別できるようにしています。

 

PartitionKey

RowKey

2011 New York City Marathon__Full

BIB:01234__John__M__55

2011 New York City Marathon__Full

AGE:055__1234__John__M

この例では、PartitionKey の値が同じであるため、エンティティ グループ トランザクションを実行できます。このグループ トランザクションでは、挿入操作の原子性が提供されます。異なる PartitionKey 値でこのパターンを使用することもできますが、この利点を得るためには同じ値を使用することをお勧めします。また、異なる PartitionKey 値でアトミック トランザクションを実行するには、特別なロジックを記述する必要もあります。

Azure テーブルには、クエリによる負荷だけではなく、挿入、更新、削除などのストレージ操作による負荷も発生する場合があります。そのため、テーブルで実行するストレージ操作のタイプと頻度を考慮する必要があります。これらの操作をめったに実行しない場合には、考慮する必要はありません。ただし、多数の挿入を短期間に実行するようなきわめて頻度の高い操作の場合には、選択した PartitionKey 値の結果、これらの操作がどのように実行されるかを検討する必要があります。1 つの重要な例は、アペンド オンリー パターンにするかプリペンド オンリー パターンにするかです。これらのパターンについては、前のセクションの「範囲パーティション」で説明しています。アペンド オンリー パターンまたはプリペンド オンリー パターンが使用されている場合には、後続の挿入において、PartitionKey に対して一意の昇順または降順の値が使用されることを意味します。このパターンを頻度の多い挿入操作と組み合わせると、テーブルでスケーラビリティを確保維持しつつ挿入操作を行うことができなくなります。Azure は操作要求の負荷を他のパーティション サーバーに分散できなくなるため、テーブルのスケーラビリティが影響を受けます。したがって、この場合には、GUID 値のようなランダムな値の使用を検討する必要があります。そうすると、パーティションのサイズは小さい状態のまま、ストレージ操作時の負荷分散を維持できます。

PartitionKey 値が複雑な場合や、他の PartitionKey マッピングとの比較が必要な場合には、テーブルのパフォーマンスをテストする必要になる場合があります。このテストでは、ピーク負荷時のパーティションのパフォーマンスを調べる必要があります。

ストレス テストを実施するには

  1. テスト テーブルを作成します。

  2. テスト テーブルにデータを読み込み、ターゲットの PartitionKey を持つエンティティがテスト テーブルに含まれるようにします。

  3. アプリケーションを使用して、ピーク負荷をテーブルにかけるシミュレーションを実行します。また、手順 2. の PartitionKey を使用して 1 つのパーティションだけをターゲットにします。この手順はアプリケーションごとに異なりますが、必要なすべてのクエリとストレージ操作をシミュレーションに含める必要があります。また、1 つのパーティションだけをターゲットにするよう、アプリケーションの調整が必要になる場合があります。

  4. テーブル上で GET 操作または PUT 操作のスループットを確認します。

スループットは、1 つのサーバー上の 1 つのパーティション対して指定されている制限と実際の値とを比較して確認します。どのパーティションも毎秒 500 エンティティに制限されています。1 つのパーティションのスループットが 500 エンティティを超えている場合には、実稼働設定においてサーバーが過負荷な状態で稼働する可能性があります。この場合には、PartitionKey 値が粗すぎるため、十分なパーティションがないか、パーティションが大きすぎます。そのため、PartitionKey 値を変更して、より多くのサーバー間にパーティションが分散することが必要になる場合があります。

パーティションが過負荷である場合、つまり、パーティション (特にパーティション サーバー) がスケーラビリティ ターゲットを超えて動作している場合には、パーティション レイヤーで負荷分散が発生します。Azure ストレージの場合、各パーティションには毎秒 500 エンティティというスケーラビリティ ターゲットがあります。負荷分散は、分散ファイル システム (DFS) レイヤーでも発生します。DFS レイヤーでの負荷分散は I/O 負荷に対処するものであるため、この記事では扱いません。パーティション レイヤーでの負荷分散は、スケーラビリティ ターゲットを超過した直後に行われるわけではありません。代わりに、システムは負荷分散プロセスが開始されるまでの数分間待機します。これにより、パーティションは実際に過負荷になります。負荷分散のトリガーとなる負荷が発生したことをパーティションに事前通知する必要はありません。システムが自動的にこのタスクを実行するためです。テーブルに特定の負荷が事前通知されたとしても、システムが実際の負荷に基づいてパーティションの負荷を分散する場合があるため、パーティションの分散が大きく異なる可能性もあります。パーティションに事前通知する代わりに、Timeout エラーと Server Busy エラーを処理するコードを記述することを検討する必要があります。こうしたエラーは、システムが負荷分散を実行しているときに返されます。再試行戦略でこのエラーに対処することで、アプリケーションはより適切にピーク負荷を処理できるようになります。再試行戦略については、次のセクションで詳細に説明します。負荷分散が発生すると、パーティションは数秒間オフラインになります。オフラインの間、システムはパーティションを別のパーティション サーバーに割り当てようとします。ここで、データがパーティション サーバーに格納されないことに注意してください。代わりに、パーティション サーバーは、DFS レイヤーからエンティティを提供します。データがパーティション レイヤーに格納されないため、パーティションは別のサーバーに迅速なプロセスで移動します。これにより、アプリケーションにダウンタイムが発生したとしても、その時間が大幅に制限されます。

アプリケーションにとって重要なことは、ストレージ操作が失敗したときに、更新データが失われないように処理することです。失敗の中には、再試行戦略が必要ないものもあります。たとえば、更新操作で 401 Unauthorized (認証失敗) というエラーが返される場合には、この操作を再試行する意味はありません。これは、401 エラーを解決するために再試行を繰り返したとしても、その間にアプリケーションの状態が何も変わっていない可能性が高いためです。しかし、Server Busy (サーバー ビジー) や Timeout (タイムアウト) のような一部のエラーは、テーブルのスケーラビリティを実現している Azure の負荷分散機能と関係があります。エンティティを提供しているストレージ ノードが過負荷になると、Azure は、パーティションを他のノードに移動して負荷を分散します。この間は、パーティションにアクセスできなくなるため、Server Busy エラーや Timeout エラーが発生します。最終的には、パーティションにアクセスできるようになり、更新が再開可能になります。そのため、Server Busy エラーや Timeout エラーには再試行戦略が適しています。ほとんどの場合、400 番台のエラーと 500 番台の一部のエラー (501 Not Implemented、505 HTTP Version Not Supported など) を再試行ロジックから除き、500 番台の一部のエラー (Server Busy (503)、Timeout (504) など) に対しては再試行戦略を実装するという方法をとることができます。

アプリケーションのために使用できる一般的な再試行戦略は 3 つあります。以下に、これらの再試行戦略とその内容を列挙します。

  • 再試行なし - 再試行は一度も行わない。

  • 固定バックオフ - 固定バックオフ値で操作を N 回再試行する。

  • 指数バックオフ - 指数バックオフ値で操作を N 回再試行する。

再試行なし戦略は、操作の失敗処理としては簡単な (そして回避的な) 方法です。ただし、あまり役に立ちません。再試行をまったく課さないという戦略には、操作が失敗した後、データが正しく格納されないという明白な危険があります。したがって、より適切な戦略は、同じバックオフ間隔で操作を再試行する固定バックオフ戦略です。ただし、この戦略は、高度にスケーラブルなテーブルの処理に対しては最適化されません。これは、多くのスレッドやプロセスが同じ時間待機すると競合が起こる可能性があるためです。推奨される再試行戦略は、再試行ごとに前回の再試行よりも間隔が長くなる指数バックオフを使用する戦略です。この戦略は、イーサネットなどのコンピューター ネットワークで使用されている競合回避 (CA) アルゴリズムとよく似ています。指数バックオフでは、ランダムな要素を使用して算出した差分を追加することによって、次の再試行間隔を求めます。また、バックオフ値は上限値と下限値の間に制限されます。次の計算式を使用すると、指数アルゴリズムに基づいて次回のバックオフ値を計算できます。

y = Rand(0.8z, 1.2z)(2x-1

y = Min(zmin + y, zmax

指定項目:

z = 既定のバックオフ (ミリ秒)

zmin = 既定の最小バックオフ (ミリ秒)

zmax = 既定の最大バックオフ (ミリ秒)

x = 再試行回数

y = バックオフ値 (ミリ秒)

Rand (ランダム) 関数で使用されている乗数の 0.8 と 1.2 により、既定のバックオフのランダムな差分が元の値の ±20% の範囲で生成されます。この ±20% という範囲は、ほとんどの再試行戦略で許容でき、競合の増加を防止します。この計算式は、次のコードを使用して実装することができます。

int retries = 1;
 
// Initialize variables with default values
var defaultBackoff = TimeSpan.FromSeconds(30);
var backoffMin = TimeSpan.FromSeconds(3);
var backoffMax = TimeSpan.FromSeconds(90);
            
var random = new Random();
 
double backoff = random.Next(
    (int)(0.8D * defaultBackoff.TotalMilliseconds), 
    (int)(1.2D * defaultBackoff.TotalMilliseconds));
backoff *= (Math.Pow(2, retries) - 1);
backoff = Math.Min(
    backoffMin.TotalMilliseconds + backoff, 
    backoffMax.TotalMilliseconds);


Azure マネージ ライブラリを使用してアプリケーションを開発している場合には、ストレージ クライアント ライブラリに含まれている再試行ポリシーを利用できます。このライブラリの再試行メカニズムは、カスタムの再試行ポリシーで機能を拡張することもできます。Microsoft.WindowsAzure.StorageClient 名前空間の RetryPolicies クラスは、RetryPolicy オブジェクトを返す静的メソッドを提供します。RetryPolicy オブジェクトは、TableServiceContext クラスの SaveChangesWithRetries メソッドと組み合わせて使用されます。TableServiceContext オブジェクトが使用する既定のポリシーは、RetryPolicies.DefaultClientRetryCount の値と RetryPolicies.DefaultClientBackoff の値を使用して構成された RetryExponential クラスのインスタンスです。次のコードは、別の RetryPolicy で TableServiceContext クラスを構成する方法を示しています。

class MyTableServiceContext : TableServiceContext
{
    public MyTableServiceContext(string baseAddress, CloudStorageAccount account)
        : base(baseAddress, account)
    {
        int retryCount = 5; // Default is 3
        var backoff = TimeSpan.FromSeconds(45); // Default is 30 seconds

        RetryPolicy = RetryPolicies.RetryExponential(retryCount, backoff);
    }
    ...
}

Azure テーブル ストレージは多数のストレージ ノード間でパーティションの管理と再割り当てを行うため、アプリケーションは大量のデータを格納できます。データをパーティション分割することで、テーブルのスケーラビリティを制御できます。テーブルのスキーマを定義して効率的なパーティション分割戦略を実施するには、事前の計画が必要です。具体的には、PartitionKey 値を選択する前に、アプリケーションの要件、データ、およびクエリを分析します。システムがトラフィックに応答する際、各パーティションは別のストレージ ノードに再割り当てされます。パーティション ストレス テストを使用してテーブルが正しい PartitionKey 値を持つようにします。テストすることで、パーティションが過負荷になる時期を確認して、必要なパーティション調整を実行できます。アプリケーションが断続的なエラーを処理してデータを保持するには、バックオフによる再試行戦略を使用する必要があります。Azure ストレージ クライアント ライブラリで使用される既定の再試行ポリシーは、競合を回避してアプリケーションのスループットを最大化する指数バックオフによるポリシーです。

表示:
© 2015 Microsoft