DynamoDB の強い整合性のある読み取りと結果整合性のある読み取り
アイテムを更新し、すぐにそれを読み戻すと、古い値 が返ってきます。書き込みは 成功しています — 少し経つと同じ読み取りが新しい値を返します。何も壊れていません。 DynamoDB のデフォルトである 結果整合性のある読み取り に当たったのであり、 リクエストごとにそれをオプトアウトできます。
これは DynamoDB があなたに直接手渡す数少ない正しさのつまみのひとつで、実際の代償が ついて回ります。これを正しく使うには、各モードが何を保証し、いくらかかり、そして どこで強い読み取りがそもそも使えないのかを知る必要があります。
DynamoDB の強い整合性のある読み取りと結果整合性のある読み取りの違いは何ですか?
結果整合性のある読み取り(デフォルト)はいずれかのレプリカが処理するため、書き込み直後に一瞬だけ古いデータを返すことがありますが、コストは半分で済みます。強い整合性のある読み取りはリクエストごとに ConsistentRead=true でオプトインし、パーティションのリーダーへルーティングされて、コミット済みのすべての書き込みを常に反映します — ただし読み取りキャパシティは 2 倍です。
- 結果整合性(デフォルト) — 書き込み直後に一瞬だけ古いデータを返すことがあります。 最も安い読み取りモードです。
- 強い整合性 — 読み取り前にコミットされたすべての書き込みを常に反映します。
ConsistentRead=trueでリクエストごとにオプトインします。 - 強い読み取りは結果整合性の 2 倍のコスト。 強い整合性のある読み取りは、同じデータに ついて結果整合性のある読み取りの 2 倍の読み取りキャパシティを消費します。
- どこでも使えるわけではない。 強い読み取りはベーステーブルと ローカルセカンダリ インデックス で使えます。グローバルセカンダリインデックスは結果整合性のみ で、 オプトインはできません。
- デフォルトは結果整合性に。 自分が今書いたばかりのデータを読み、一瞬の古さが 問題になる場合にのみ強い読み取りに手を伸ばしましょう。
問題:最新の書き込みが見えない読み取り
ユーザーアカウントを運用しているとします。ユーザーが通知用メールを変更し、アプリが 更新を書き込み、確認画面がすぐにプロフィールを読み直して新しいアドレスを表示します。 デフォルトの読み取りモードでは、その読み直しがまだ変更を受け取っていないレプリカに 当たることがあります — その結果、ユーザーは 古い メールを見て、保存が失敗したと 思い込みます。
その窓は小さく(通常は 1 秒を大きく下回ります)、自然に閉じます。しかし read-after-write の確認には「だいたい正しい」では不十分です。それこそが強い整合性が 存在する理由のケースです。
なぜ結果整合性が起きるのか
DynamoDB はすべてのパーティションを、別々のアベイラビリティゾーンにまたがる 3 つのストレージノード — 1 つのプライマリと 2 つのレプリカ — に保存します。書き込みは プライマリと 1 つのレプリカに着地した時点で承認され、その後 3 つ目のノードへ 非同期に 伝播します。
読み取りは負荷を分散するため、3 つのノードの いずれか が処理しうります。結果整合性の ある読み取りは、まだ最新の書き込みを受け取っていないノードに当たることがあります — その ため少し古い値を返します。強い整合性のある 読み取りはパーティションのリーダーへ ルーティングされ、リーダーは常に最新のコミット済みデータを保持しているため、古い結果を 返すことはありません。
その複製遅延こそが違いのすべてです。それは 2 倍のコスト の理由でもあります。強い 読み取りは結果整合性の読み取りのようにレプリカ間で負荷分散できないため、DynamoDB は キャパシティを 2 倍で課金します。
コストを具体的に
読み取りは 読み取りキャパシティユニット(RCU) で計測され、各 RCU は最大 4 KB を
カバーします。1 RCU で、4 KB のアイテムの 強い整合性のある読み取り 1 回 または
結果整合性のある読み取り 2 回 が買えます。つまりホットな読み取りパスで
ConsistentRead=true を切り替えると、その読み取りコストは倍になります — トラフィックの
多いエンドポイントでは目に見えて分かる行項目です。
強い読み取りをデフォルトにする前に、自分のアイテムサイズとリクエストレートで差を DynamoDB 料金計算ツール でモデル化しましょう — 全面的に 2 倍を払う価値があることはめったにありません。
強い読み取りが使える場所(と使えない場所)
| 読み取り対象 | 強い整合性は可能か? |
|---|---|
| ベーステーブル | 可能 — ConsistentRead=true でオプトイン |
| ローカルセカンダリインデックス(LSI) | 可能 — ベーステーブルと同じオプトイン |
| グローバルセカンダリインデックス(GSI) | 不可 — 結果整合性のみ、上書き不可 |
GSI はベーステーブルから非同期にレプリケートされる独自のデータコピーを保持するため、 強い読み取りを提供することは決してできません。あるアクセスパターンが本当に read-after-write を必要としていて、それを GSI で提供する予定だったなら、それは代わりに ベーステーブルまたは LSI で提供すべきというサインです。
落とし穴と次のステップ
- 強い読み取りをデフォルトにしない。 ほとんどの読み取りは 1 秒未満の古さの窓を 許容できます。どこでも 2 倍を払うのは無駄遣いです。
- GSI に read-after-write を期待しない。 設計上、結果整合性です — なぜ GSI は結果整合性なのか を参照。
- トランザクションは強く読む。
TransactGetItemsは常に強い整合性です — DynamoDB トランザクション を参照。 - 整合性はキャパシティと相互作用する。 2 倍の乗数は オンデマンドとプロビジョンド のコスト計画に 直結します。
API 呼び出しを書かずに DynamoDB のテーブルとインデックスを調べたいですか? DynoTable をダウンロード してデータを直接調べましょう。