DynamoDBのパーティション分割問題について
こんにちは、井上です。
DynamoDBを使用していて、スループットは十分なはずなのに、書き込み/読み込みエラーが発生する、というケースはありませんでしょうか。
ハンズラボでは以前からこの問題に悩まされており、原因が分からないまま必要以上に高いスループットを設定したりしてお茶をにごしていたのですが、サポートやSAの方に相談したところ、パーティションの分割による問題であることが分かりました。
このパーティション問題は結構ハマりどころだと思うので共有しておきます。
背景
昨年、お客様の購入履歴(約2億3,000万レコード)などのデータをDynamoDBへ移行しました。
その際、店舗の閉店時間から、翌日の開店時間までの間にデータ移行を行う必要があったため、書き込みスループットを最大限に上げ、並列処理にて一気にデータを投入しました。
初期インポートは無事成功し、スループットを通常オペレーション上必要な値に下げ、運用を開始しました。
が、CloudWatch上では、スループットが足りているにもかかわらず、ProvisionedThroughputExceededException が定期的に発生するという状況に陥りました。
原因
DynamoDBは、データの容量と指定したスループットによって自動的にスケールアウトするようになっています。
このあたりは、AWS Black Beltの資料に計算式が書かれていますが、簡単にいうと、「サイズによるパーティション数と、スループットによるパーティション数の大きい方がパーティション数」です。
サイズによるパーティションは、テーブルサイズ(バイト数)を10GBで割った数値で、スループットによるパーティション数は、Read Capacity Unitを3000で割った数+Write Capacity Unitを1000で割った数の合算値です。
例えば、データ容量: 100GB、RCU: 12000、WCU: 12000 の場合、
サイズによるパーティション: 100GB / 10GB = 10
スループットによるパーティション数: (12000/3000) + (12000/1000) = 16
となり、大きい方の 16パーティションとなります。
このパーティション数の計算は裏側で行われ、データのスケールアウトもこちらの知らないところで、知らないうちに自動的に実施されます。
ここまでだと、DynamoDB素晴らしい!となるのですが、問題は、スループットを下げた時です。
Blackbelt資料にも「キャパシティ変更時の注意事項」というページに記載があるのですが、
キャパシティを減らしても、パーティション数は減らないのです。
1パーティションあたりのスループットは、設定したスループットをパーティション数で割った数値になるので、ReadCapacity:20 を設定しても、10パーティションあった場合は ReadCapacity: 2 しか使えないというような事が発生します。
ハッシュキー+レンジキーのテーブルで1ハッシュキーに多くのデータが入っていた場合、同一パーティションにリクエストが集中しエラーが起きやすくなると思います。
対策
Black Belt資料にも、「注意」と書かれているだけで、ではどうするかというのが記載がありません。
現実的に可能な対策としては、エラーが起きなくなるまでスループットを上げるという解決方法があります。10パーティションに分割されている場合は、想定されるスループットの10倍に設定します。
(ただし、今いくつのパーティションに分割されているかは知る術がないので、以前のスループット変更履歴からの推測となります)
これは、システムには手をいれる必要はありませんが、本来必要ない余計なスループットのためにお金を払い続ける事になります。
もうひとつの方法としては、テーブルを新規で作り直し、大量インポート時にも「スループットを大きく上げない」という方法があります。
スループットを上げずに、平常時に必要なスループットでデータをちょぼちょぼと投入していくことで問題を回避できます。
が、現実的に、億レベルのデータを少しずつ入れていたら終わりませんし、自動的にスケールできるというDynamoDBのメリットを享受しないということになるので、あまり選択したくない方法です。
また、このような問題が発生するテーブルは大規模なテーブルであることが多く、テーブルを作りなおすことがあまり現実的でない事が多いです。
まとめ
パーティションが自動で縮小されるようになるといいなぁ。 お金が…