DynamoDB の論文読む (1) - Dynamo: Amazon’s Highly Available Key-value Store
出た当初のやつ (2007) と 2022 年に出たもので2つある。
どっち読むかは決めかねるのでアブストや他の日本人が翻訳してる記事など見ながら興味のある方をチョイスしようと思う。
Dynamo: Amazon’s Highly Available Key-value Store
pdf: https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf
読破・翻訳した人のアウトプット
Amazon DynamoDB: A Scalable, Predictably Performant, and Fully Managed NoSQL Database Service
pdf: https://www.usenix.org/system/files/atc22-elhemali.pdf
読破・翻訳した人のアウトプット
Dynamo: Amazon’s Highly Available Key-value Store
こっちを見ていく。
Dynamo: Amazon’s Highly Available Key-value Store (2007) の方を見ていく。
参考
[著者] . [掲載サイト名] . [記事名]
- mrasu . Hatena Blog 発明のための再発明 . Dynamo: Amazon’s Highly Available Key-value Store
- github.com/matope . Gist . Dynamo: Amazonの高可用性Key-value Store[和訳]
[link N]
の書式で参照する。
論文中で引用されている参考文献は [N]
で表記する
abstract の部分は [link 1] の訳をそのまま受け取る。
重要なのは以下のポイント。
- この論文では "Dynamo" の設計と実装を説明している
- Dynamo は一定の障害シナリオの下で一貫性を犠牲にしている("always-on" のユーザー体験(?)を達成するための可用性レベルを達成するために)
Intro
Amazon は世界最大級の e コマースサービスであり、わずかなダウンでも重大な経済的損失が生じ、顧客の信頼に影響する。
Amazon のスケールになると、絶え間なくシステムコンポーネント(サーバーや N/W コンポーネント)は故障する。
そのような状況下で永続的なステートをどのように管理するかが鍵であり、システムの信頼性とスケーラビリティを左右する。
Amazon のプラットフォームを構成するサービスのうち、ほとんどのデータストアへのアクセスは primary key を使うものである。例えば以下のようなもの。
- ベストセラーのリスト
- ショッピングカート
- 顧客の設定
- セッション管理
- 売上ランキング
- 製品カタログ
疑問メモ - 今挙がったものは promary key アクセスなのか?
ランキングを提供するものが primary key アクセス、と書かれているが、どうして primary key になるのかがちょっとよく分かってない。上記の中だと「ベストセラーのリスト」「売上ランキング」あたり。
売上ランキングに関しては別の場所とストレージを使って集計データ作る仕組みが動いているはず && 製品のカテゴリのようなジャンル指定のアクセスであろうと想像する。
DDB で集計やるなら Stream 使うなりして集計用のアイテムを update するような仕組みが思いつく。アイテムの変更の都度 stream が動くアーキテクチャはかなり高価になる気がするけども。
こういう集計結果へのアクセスは hash & range で limit N する感じになると思われる。DDB なら limit 句はストレージからの実際のフェッチ件数を減らせるので primary key アクセスで効率的に上位 N 件をクエリできる。はず。
自分の発想の範囲だと、こうしたアクセス要件なら集計・ソート済みのアイテムから hash = best_seller#product_category=${category_id}
, range = reputation=${reputation_score}
のような Primary key を指定して、そこに limit N する感じのクエリになるはず。なので、こういうクエリでランキング系を出すのであればここに挙がっているものも primary key アクセスと言える
RDB に共通する使用パターンは、スケールと可用性の制限を受け非効率になる可能性がある。Dynamo はシンプルな key-value のみのインタフェースを提供し、こうした(上述のようなパターンを持つ)アプリケーションの要件に合致する。
Dynamo は、スケーラビリティと可用性を実現するために、よく知られた技術を使っている。ストレージノードへの追加・削除は、人手によるパーティショニングや再配置を必要とせず行える。「よく知られた技術」は、例えば以下。
- データは "consistent hashing" によってパーティショニングされ、複製される [10]
- 整合性の担保をオブジェクトのバージョニングによって補助する [12]
- 更新中のレプリカ間の整合性は、"quorum-like" で非集約的な同期プロトコルを用いて実現する
- "gossip" ベースの分散障害検知と、メンバーシッププロトコルを用いている
Dynamo が Amazon のプラットフォームを支えるコアサービスの基幹となってから、ピーク時にもダウンタイムなくサービス提供ができるようになった。例えば Shopping cart サービスは、1日あたり300万のチェックアウトに繋がる数千万のリクエストをさばいて、同時にアクティブな数十万のセッションを管理した。
この論文の主な貢献要素は、単一の高可用性システムを提供するために、異なるテクニックをどう組み合わせれば良いのかを評価したこと。"eventually consistent" なストレージシステムが、高い要求を持つシステムに適用できることを示した。また、それらのテクニックを、非常に高いパフォーマンス要求を持つシステムに応えるためにチューニングしていくための洞察も提供する。
この論文の構成。
- Section 2: Background
- Section 3: Related work
- Section 4: System design
- Section 5: Implementation
- Section 6: Details the experiences and insights gained by running Dynamo in production
- Section 7: Concludes the paper
Amazon の商売に支障のある数字は適宜非公開にしているので注意。例えばデータセンター間のレイテンシなど。
2. Backgorund
かいつまんでいく。
Intro でも言及があったが、多くのサービスが primary key によるアクセスしか必要とせず、複雑なクエリが必要ない。こうした状況では、従来のリレーショナルな DB は非効率でマッチしない。近年の目覚ましい技術的な発展をもってしても、リレーショナルな DB をスケールアウトする、あるいは適切にパーティショニングしつつ負荷分散するのは難しい。
Dynamo(のような key/value なストレージシステム)において、以下のような要求事項を仮定した。
(1) query model
データの read/write 操作は、key によって一意に特定される。
データは BLOBs のようなバイナリで格納される。
RDB に求められるような、複数のアイテムにわたる操作は必要としない。
(2) ACID
データベースのトランザクションが保証すべき特性。
Amazon での経験から、ACID 特性を満たすデータストアは、可用性が低い傾向にある。
これは、企業(での経験則から)の世界でも、アカデミックの世界でもよく知られている。[5]
Dynamo は、高い可用性に繋がるのであれば弱い "C" = consistency で動作することが許容できるアプリケーションを対象としている。また、Dynamo は "I" = isolation を保証しない。単一の key による更新のみサポートする
(3) Efficiency
コモディティなインフラの上で機能すること。Amazon プラットフォームのサービスには厳しいレイテンシ要求があり、一般には分布の 99.9% パーセンタイル値で測定される。
状態アクセス(≒データストアへのアクセス)が重要な鍵になる。つまり、ストレージシステムはこのような厳しい SLA を満たす能力が必要。この辺は 2.2 で説明。
トレードオフは、「パフォーマンス」「コスト効率」「可用性」「耐久性 (D)」である
(4) その他
Dynamo は Amazon の中でのみ使用されるサービスである。悪意のある外部は想定せず、認証認可のようなセキュリティ要求もない。
各サービスは Dynamo の各インスタンスを使用するので、初期設計ではストレージホストを数百規模までのスケールアウトを想定している。Dynamo のスケーラビリティの限界や、スケーラビリティの可能性については後述。
2. Backgorund (2)
(2.2) Aamazon の非集約的なサービスインフラにとって、SLA がめっちゃ重要な役割を持ってるよ、という話。
個人的には、SLA と聞くと最初にイメージしがちなのはシステムなら稼働率とか MTTR などの可用性に関する目標だったりする。が、ここではリクエストのレイテンシ(N パーセンタイル)に代表されるパフォーマンスに対する目標に焦点を当てているっぽい。
こうした指標は算術平均や中央値、分散を使った統計値として定式化されるのがよくあるアプローチだが、Amazon ではそれでは不十分と考えている。
何に対して不十分かというと「"全ての" 顧客に対して良い体験を提供する」というゴールに対して。大半を占めるマジョリティーに対して十分であれば良い、とは考えない。
例えばパーソナライゼーション。広範的(?)なパーソナライゼーションのテクニックは、履歴のサイズで分布の上位に位置する顧客に対しては処理時間がかかる。このようなケースだと一部の上位でレイテンシがスパイクするような分布が形成されてしまうので、算術平均や中央値によるメトリックでは間に合わない。ので、 99.9th のパーセンタイル値を使っている。
99.9th よりも高いパーセンタイルを選ばないのは、そうすることによるコストが顕著に増加し、コスト効率が悪くなることが分析によって示されたからである。
この論文では 99.9 パーセンタイルという数字が頻出する。
2. Backgorund (3)
2.3 Degisn Considerations
商用システムにおいて、レプリケーションのアルゴリズムは同期的なレプリケーションを使っている。これは、強整合性のデータアクセスを提供するためである。強整合性を達成するためには、特定の障害シナリオにおいてデータの可用性に関するトレードオフを余儀なくされる。
例えば以下(原文)
For instance, rather than dealing with the uncertainty of the correctness of an answer, the data is made unavailable until it is absolutely certain that it is correct.
翻訳メモ
rather than で次の2つを対比している。前者の方が重要視される状況を想定した文章になっている
dealing with the uncertainty of <1>
<1> = the correctness of an answer
<2> = the data is made unavailable
<2> + until it is absolutely certain that it is correct.
まとめると、これは「データが正確であること」に関する不確実性を扱った記述で、「正確さ」を重視して、それが確実になるまでデータを利用不可にしておくアプローチについて言及している。
ロックの取得なんかをイメージすると良さそう。私はあんまり詳しくないが、Dirty read, Non-repeatable read, Phantom read のような読み込み不整合を念頭に置き、これらの不整合が可能な限り起きないことを重視した想定ケースを述べている、と捉えている
初期の研究では、複製されたデータベース (※1) では N/W 障害などの可能性があり、強い整合性と高い可用性は両立できないものだと広く考えられてきた。[2, 11]
そうしたシステム、あるはアプリケーションでは、どのような条件下でどのような特性が満たさせるのか、認識しておく必要があるとされていた。
※1 ... 分散データベースと読み替えても良さそう
サーバーや N/W の故障が起きやすいシステムでは、「楽観的」なレプリケーションが可用性を向上すると考えられてきた。このアプローチにおける課題は、競合する変更の検出とその解消である。競合解消のアプローチには2つの問題があった。すなわち「誰が解消を解消するのか?」「いつ競合を解消するのか?」である。Dynamo は "eventually consistent" なデータストアとなるように設計されており、すべての更新はすべてのレプリカに「最終的に」到達する。
楽観的なレプリケーションについて
「変更」はバックグラウンドでレプリカに伝搬することが許されており、
(つまり非同期に、と言ってる・・・?)
かつ並列に行われる、切断された状況でも許容する、と言っている
設計上の重要な考慮事項は、競合を解消する処理が実行されるタイミングである。
すなわち、read/write のどちらで競合は解消されるべきなのか。
翻訳メモ
i.e., で書いてあった訳、ぶっちゃけどうして「すなわち」で接続できるのかが技術的によくわかってない。
すなわち、read/write のどちらで競合は解消されるべきなのか、である
解消するタイミングが read/write のどちらか、という話自体は感覚的に納得できはする。だた、自分の言葉では説明できないので、このへんの話をきちんと理解したうえで納得するためにはデータベースや分散システムのテーマを扱う参考文献をあたってみないとだめだな〜と思った
従来研究では write 時に解消するアプローチが採られていて、read 時の複雑性を増やさない方向性だった。すべての(あるいは過半数の)レプリカに更新が伝搬するまでは write が弾かれるようになっていた。
一方 Dynamo では、"always writable" なデータストア(つまり書き込みに対して高い可用性を持つ)であることを想定して設計された。
いくつかの Amazon のサービスでは、顧客の「更新」を弾くデザインは顧客体験の悪化につながる。例えば Shopping cart サービスでは、カートに対する商品の追加/削除は、サーバーや N/W に障害が起きていようとも常に操作可能である必要がある。
このビジネス要件のため、競合解消の複雑性の問題は read 時に対処する必要がある(write が常に拒否されないようにするために)。
次の設計の意思決定は「誰が 競合を解消するのか」の問題。
これはデータストアかアプリケーションのどちらかになるが、データストア側に寄せた場合は選択肢がより制限される。データストアでは、競合解消のポリシーとして単純な "last write wins" のみが使用できる [22]。
他方、アプリケーション側の責務にした場合はデータスキーマをアプリケーションが理解しているので、アプリケーション自身で競合を解消する方法を決められる。これが顧客体験にとって最適である。
例えば、顧客の Shopping cart を保持するアプリケーションは、競合が発生したバージョンを「マージ」して、単一の(統一された)カートの内容を返すことを選択できる。
(アプリケーション側で書き込みの競合を解消するアプローチに)このような柔軟性があるが、一部の開発者は自分自身で競合解消のメカニズムを実装することを望まず、データストアに任せることを選択するかもしれない。その結果、"last write wins" を選択することになる。
ここまで述べた以外にも、ポイントとなる設計原則がある。
(1) Incremental scalability
ストレージホストをスケールアウトをできる。また、その際にシステムとそのシステムを運用する人に対する影響は最小化されている
(2) Symmetry
Dynamo の各 Node は同じ責務を持つ。特別な役割を持ったり、追加の責務を負うノードはいない。Amazon の経験上、この特性はシステムのプロビジョニングと維持をシンプルにする。
(3) Decentralization
この原則は Symmetry の延長にあり、中央集権的な制御よりも非集約的な p2p のテクニックを好むべきである。過去には、中央集権的な制御がサービス停止を引き起こしてきた。可能な限り中央集権的な制御を避けることが目標になる。そうすることで、よりシンプルでスケーラブルな、可用性の高いシステムになる。
(4) Heterogeneity
システムは、自身が実行されるインフラの異質性を利用できる必要がある。例えば、仕事量の分配は各サーバーの能力に比例しなければならない。
すべてのホストを一度のアップグレードすることなく、より高いキャパシティを持つノードを追加するために不可欠である。
3 RELATED WORK
めっちゃ気になるし勉強することが多いと思うが、Dynamo の設計・実装の理解に興味があるのでいったんスキップする。後で戻ってくるかも。
- 3.1 Peer to Peer Systems
- 3.2 Distributed File Systems and Databases
- 3.3 Discussion
Figure 2 で説明されるような、パーティショニングとレプリケーションの仕組みの話 (Dynamo ring) については特に気になるのでそこだけさらっておく。P2P のセクションを読み飛ばす。
分散ファイルシステムは、P2P とは違って階層的な名前空間を扱う。
競合解消の手続きは、通常はそれぞれのシステムごとに特化した方法で管理される。
例えば、Ficus[15] や Coda[19] といったシステムは NFS のような中央集権的なサーバーを持たない。Google File System [6]も分散ファイルシステムの一つ。GFS はシステム全体のメタデータ(どのデータのどのチャンクがどこに配置されたか、など)を保持するための単一のマスターサーバーが存在し、分割されたデータを保持するチャンクサーバーがある。Bayou は分散 RDB で、切断操作を許容し結果整合性 (eventual consistency) を提供する[21]。
Bayou や Coda, Ficus は切断操作を許容して、N/W パーティションの分断や停止などの問題に対して回復力がある。それぞれの競合解消の方法は違う。Ficus, Coda は DB システムレベルで、Bayou はアプリケーションレベルでそれを行う。いずれのシステムも結果整合性を保証する。
Dynamo はこれらと異なる競合解消の方法を用いて、N/W が分断された場合であっても read/write 操作を継続できるようにしている。
FAB [18] のような分散ブロックにファイルシステムでは高可用性のために大きなオブジェクトは複数の小さなブロックに分割して格納するようになっている。key/valeu システムでもこうしたアプローチには利点があり、分散ブロックファイルシステムよりも以下の理由でこのアプローチが適している。
(a) 扱う想定のデータが小さい (size < 1M)
(b) key/value ストアはアプリケーションごとの要件に応じて設定する手間が少ない
従来の分散ファイルシステムはデータの完全性 (Integrity) やセキュリティ (?) の問題を意識していたが、Dynamo では対照的にこれらの問題に焦点をあてていない。前提事項の方でも言及しているが、Dynamo は Amazon の中でのみ使うサービスとして構築されている。
Bigtable は分散ストレージシステムで、スパースで多次元なソート済みデータ構造を保持する。複数の属性を使ってデータにアクセスが可能 [2]。Bigtable と比べると、Dynamo は key/value アクセスのみを要求するアプリケーションを想定している。これは、N/W やサーバーの障害によらず書き込みに関する高い可用性を提供することを最優先事項としているから。
従来のレプリケーションされた RDB システム(要するに分散 RDB?)は、複製されたデータに強整合性を保証することに関心を寄せてきた。強整合性はアプリケーション開発者にとって利便性をもたらしたが、引き換えにスケーラビリティと可用性が制限された [7]。これらのシステムは強整合性を提供するために N/W の分断に対処する能力を持たない。
3.3 Discussion
ここまで紹介した分散システムと Dynamo との違いがいくつかある。
1つはターゲットとする要件の違い。Dynamo は "always writable" であること、write が(障害または同時書き込みが原因で)決して拒否されないことを目指している。この要件は多くの Amazon のサービスで重要である。
2点目は、Dynamo は完全に社内利用する想定でありすべてのノードは信頼できることが前提だった。
3点目は、Dynamo を利用するアプリケーションは(ファイルシステムや、正規化されたリレーショナルモデルのような)階層的な名前空間をサポートする必要がなかった。
4点目は、Dynamo には非常に高いレイテンシ要求があったこと。read/write 操作は 99.9% が数100ms で実行できること。このレイテンシ要求に応えるため、リクエストルーティングが複数ノードにまたがることは許容できなかった(例えば Chord, Pastry のような分散ハッシュテーブルシステムのような)。
複数のホップ数をまたぐことでレスポンスタイムのばらつきも増え、その分レイテンシのパーセンタイル値も増大する。
Dynamo は "Zero-hop DHT" に分類される。ノードは zero-hop でリクエストをルーティングするために、自分自身のローカルにルーティングに必要十分な情報を保持する。
4. SYSTEM ARCHITECTURE
本番環境で動作する必要のあるストレージシステムのアーキテクチャは複雑である。
実際にデータの永続化を行うコンポーネントのために、システムは様々な要素を備える必要がある。スケーラブルで堅牢な負荷分散、メンバーシップ、障害検知・復旧、レプリカ同期、過負荷への対処、状態の受け渡し、同時実行されるジョブのスケジューリング、リクエストルーティング、リクエストの(転送に適した状態への)変換、リクエストの監視とアラート、設定の管理、など。
全部は説明できないが、代わりにこの論文では Dynamo で使っている分散システムの中核技術の説明に注力する。以下。
- パーティショニング
- レプリケーション
- バージョニング
- メンバーシップ
- 障害のハンドリング
- スケーリング
Table 1 でこれらの要素に対応するものをまとめた。
Problem | Technique | Advantage |
---|---|---|
Partitioning | Consistent Hashing | Incremental Scalability |
High Availability for writes | Vector clocks with reconciliation during reads | Version size is decoupled from update rates. |
Handling temporary failures | Sloppy Quorum and hinted handoff | Provides high availability and durability guarantee when some of the replicas are not available. |
Recovering from permanent failures | Anti-entropy using Merkle trees | Synchronizes divergent replicas in the background. |
Membership and failure detection | Gossip-based membership protocol and failure detection. | Preserves symmetry and avoids having a centralized registry for storing membership and node liveness information. |
[引用] Table 1: Summary of techniques used in Dynamo and their advantages.
翻訳メモ
色々用語がわかっていない部分がある。"Technique" の列に書いてある話が理解できないので個別に調べていきたい。とはいえ今掘り下げると終わらなくなっちゃうのでいったん読み飛ばす。
Consistent Hashing は以下の記事がわかりやすい。手法自体は簡単に理解できそう
[1] Hetena Blog: Carpe Diem . Junpei Tsuji (id:quoll00) . Consistent Hashing (コンシステントハッシュ法)
Quorum はこのへん
[2] Qiita . @everpeace . 最近よく聞くQuorumは過半数(多数決)よりも一般的でパワフルな概念だった
4.1 System Interface
Dynamo は key に関連付けられたオブジェクトを get(), put() の2つの(シンプルな)インタフェースで格納する。
get(key)
操作は key に紐づくオブジェクトがストレージのどこにレプリケーションされているかを特定して、単一のオブジェクト、または競合するバージョンのリストをコンテキスト情報を伴って返す。
put(key, context, object)
操作は、key への関連付けに基づき、object のレプリカがどこに配置され、ディスクに書き込まれるかを決定する。
context は、オブジェクトに関するシステムメタデータをエンコードしたもの。メタデータは呼び出し元からは隠蔽されていて、オブジェクトのバージョンなどの情報を含んでいる。システムが put リクエストから供給される context の正当性を検証できるように、context 情報はオブジェクトと同じ場所に保存される。
Dynamo は、呼び出しもとから渡された key と object をどちらも不透明(?)なバイト列として扱う。
128bit の識別子を生成するために key に対してMD5 ハッシュが適用される。識別子は、その key を処理する責務を負うストレージノードを決定する。
翻訳メモ
原文がこれ
Dynamo treats both the key and the object supplied by the caller as an opaque array of bytes. It applies a MD5 hash on the key to generate a 128-bit identifier, which is used to determine the storage nodes that are responsible for serving the key.
"opaque array of bytes" が直訳すると「不透明なバイト列」になる。ここで言う「不透明」は、そのバイト列の構造や意味にまで立ち入らず、あくまで単なるバイト列として処理されるニュアンスを表現している。
4. SYSTEM ARCHITECTURE (2)
4.2 Partitioning Algorithm
Dynamo が "scale incrementally" であることが設計のポイントの1つになる。そのためパーティショニングのメカニズムはノード集合全体で動的なものになる。
Dynamo のパーティショニングは "consistent hashing" を基盤にしていて、"consistent hashing" によって複数のストレージホストに対する負荷を分散する。
consistent hashing では、hash 関数の出力は固定値のレンジを持つ環状構造、あるいは "ring" 状になる[10]。
Figure 2: Partitioning and replication of keys in Dynamo ring.
システム内の各ノードは(rign 空間内で)ランダムな値が割り振られ、これが ring 内の「位置」を示す。
各アイテムは key によって特定される。key はそのアイテムを hashing して得られた ring 内の「位置」である。
翻訳メモ
"consistent hashing" 自体の説明パートを書いておく。
データ (=key) にもノードにも、ring 上の値が割り振られる。
あるデータは、その key 以上の値かつ最も近い位置にあるノードに属す。条件に合うノードが見つからないなら最小の値を持つノードに所属する。
"consistent hashing" アルゴリズムはいくつかの課題を提示した。
(1) ring 上へのランダムな割り振りは不均一な負荷分散を引き起こすこと。
(2) 基本的なアルゴリズムがノードごとのパフォーマンスの異質性を考慮していないこと。
この問題を解決するため、Dynamo は "variant of consistent hashing" を採用している。これは文献 [10, 20] で使用されている手法に似ている。ノードを ring 上の単一点にマップするのではなく、各ノードは ring 上で複数の値に割り当てされる。
上述の問題を解消するために、Dynamo は「仮想ノード」という概念を取り入れている。「仮想ノード」は、システム上では単一のノードのように見えるが、実在する各ノードは1個以上の仮想ノードに対して(処理する)責務を負う。
新しいノードが追加されたとき、そのノードは(ring 上の)複数個の位置に割り振られる。以降はこれを "token" と呼称する。
Dynamo のパーティショニングをチューニングする手順については Section 6 で述べる。
仮想ノードのアイデアには以下のような利点がある。
(1) ノードが障害や定期メンテによって利用不可能になった場合、そのノードが処理していた負荷は残った利用可能なノードに均等に散らされる
(2) ノードが復帰した、あるいは新しいノードが追加されたとき、そのノードは他の利用可能なノードからおおよそ同等な負荷を受け入れる
(3) ノードが責務を受け持つ仮想ノードの数は、自身のキャパシティによって決定できる。物理的なインフラの異質性を考慮できる
翻訳メモ
わからんかったポイントのメモ。
これらを「仮想ノード」の利点として強調する意図・背景がわからなかった。別に仮想ノードのアイデアを採用しなくても、その利点は享受できうるのでは?と思った。今の自分に想像できる理由をメモっておく。
仮想ノードのアイデアによって1個のノードが ring 空間上の複数の点に配置されることにより、1個の物理ノードが複数パーティションに所属しうる状況ができる。そうすることで、1個の物理ノードの追加/リタイアによって発生するキャパシティ上限の変化が、1個のパーティションではなく ring 内の複数パーティションに分散する。ノードが増減することで起きる処理能力の変化をパーティション間で均す効果があり、それが負荷を均等に分散することに貢献する、よって上記のような利点が生まれる...と、いう風に解釈した。