なので、実はチャプター9.11ということで一つ一つステップアップしていくような感じですね。
ここで、コンシスタンシーモデル、レプリケーションしていくにあたって、コンシスタンシー整合性の問題というのも一緒に考えなくてはいけないですね。
本章のまとめに入る前に、ここで分散システムの整合性問題を身近な例で捉えてもらうために、またちょっとしたアナロジーを導入してみようと思います。
ここでは、読みたい本がある大学生だとしましょう。
ここで自分が通っている大学のキャンパスに読みたい本があって、図書館に借りに行くというシチュエーションを想像してみてください。
あなたが通っている大学のキャンパスには複数の図書館の文館があると想像してみてください。
例えば本館であったり、自分が通っているキャンパスの別館であったり、また自分が住んでいる寮の近くにある図書館の文館とかもあるかもしれませんね。
そして、新しい借りたいなと思っていた本が入荷した時に、その本の情報と本自体をメインの図書館、本館から他の文館にどのように伝えていくかというところを考えてみると面白いと思います。
通信手段ですね。
実際に本館のスタッフから文館で働いているスタッフに通信するための手段には色々ありますよね。
メールを送ってもいいし、電話を送ってもいいし、でもそれぞれの連絡手段にはディレイ、遅延が発生してしまいますし、時には失敗することもあります。
例えばメールを送っても、メールを見るのは翌日かもしれないですし、メールが間違って迷惑メールボックスに入ってしまったら相手は受け取ってくれませんよね。
この前提を踏まえた上で、例えば学生が新館のデータベースインターナルズはありますかと文館で尋ねたというシチュエーションを考えてみましょう。
ここのアナロジーではですね、文館と本館というのはデータベースのノードですね。
ここでその学生というのはデータベースにクエリを投げているクライアントです。
ウェブアプリケーションでもいいですし、iOSとかAndroidのアプリケーションでもいいですね。
クライアントがデータベースに問い合わせをしています。
問い合わせの内容というのが、新館のデータベースインターナルズはありますかということで、
セレクトオールフロムブックスウェアブックスタイトルイーコースデータベースインターナルズみたいなセレクトクを投げていることをイメージしてみてください。
この図書館、この大学では最近データベースインターナルズの新館を入手して取り入れました。
ここで文鑑がいくつかあるんです。文鑑A,B,Cみたいなときに最初の文鑑に問い合わせてみると、
2階の新館コーナーにありますよとすぐ答えてくるような文鑑がありました。
これはメインの図書館のどういうところにありますよと文鑑Aでは問い合わせがあったときに答えられました。
文鑑Bでは本当は入科しているのに申し訳ありません、ちょっとまだ入科しているかどうか分かりませんと答えてきました。
文鑑Cに行ってみたら分かります、ちょっとシステムを確認してみますみたいなふうに答えられました。
新館のデータベースインターナルズを借りたい学生が文鑑Aで聞くかBで聞くかCで問い合わせるかによってまた全く別々の結果が返ってきたんですね。
本館にはすでにデータベースインターナルズがあるんですよ。
でもその本館から文鑑A、B、Cそれぞれのスタッフまでに連絡手段が届いていたりまだ届いていなかったり、文鑑側確認していなかったみたいな状況によって全然本当はデータ、ここで言うデータというのはデータベースインターナルズの書籍自体のことなんですけど、
本館にはあるのに文鑑では正しい情報が見れていないという状態なんですね。
一つ目の文鑑Aでは正しくどこにデータがあるか、あるかどうかも正しく答えられてきたので、これはメイン図書館にあるデータが見えているという状態なので、文鑑Aは本館と整合性が保てていると言えると思います。
二つ目の文鑑の例では本当はあるのに申し訳ありません、まだ入荷していませんと答えてきたので、これは整合性が取れていない状態ですよね。
これはもしかしたら何らかの理由で本館と文鑑の間の情報伝達に遅延もしくは失敗があったのかもしれません。
こういうように同じ質問なのにどこに問い合わせるか、どの文鑑に問い合わせるか、もしくはどのデータベースのノードに問い合わせるかによって異なる答えが返ってくるという、これが分散システムにおける整合性の問題を端的に表しているんですね。
文鑑と本館の間には情報伝達のラグがあるというところがポイントで、本館にあるデータベースインターナルズを借りたという情報を各文鑑に複製しているわけですよね。
最近こういう新しいデータが入りましたみたいな新刊図書リストみたいなのを各文鑑にコピーしていくんですけれども、この複製をするときにリアルタイムにデータを複製するというのは実質不可能です。
なぜかというとインターネットを通して、ネットを通して何かしらのデータ情報を渡さなきゃいけないので、そこはネットワークの寸断によって何回かリトライしなきゃ届かないかもしれませんし、もしくは間違った情報が伝わってしまうかもしれませんよね。
なので大学のキャンパスと図書館の文鑑というアナロジーでしたけれども、これは例えば本屋と本屋の視点みたいなアナロジーでも似たようなことがイメージできるかと思います。
ポイントとしてはデータの元にあるところとそれを問い合わせに答えられるブランチというか視点のところで整合性の問題がタイミングによって起こりますよね。
これをどう解決しますかというのを扱っているのがこの章になります。
これはアナロジーだけじゃなくて現実のウェブアプリケーションではどのような時に整合性を考えなくてはいけないのかというのを紹介すると、
例えばソーシャルネットワーク、SNSとか掲示板のようなメッセージをやり取りするアプリケーションを実装すると考えてみてください。
メッセージのやり取りというのは他の人と掲示板とかSNSでやり取りしたことがある方は容易に想像できるかと思うんですけれども、
順序性、オーダーが期待されていますよね。
例えば自分のX帳で自分のツイートに対して誰かがリプライをくれるとか、
あとは相手のダイレクトメッセージに対して自分がリプライするというシチュエーションで、
ここではメッセージごとの順序性があるからこそ会話が成り立つわけですよね。
例えば今日はこういうブッククラブでこういう勉強をしましたというツイートに対して誰かがこれってどういうことですかみたいな質問をくれるという、
自分の投稿に対して質問がくるというのがあるからこそメッセージの順序性があるからこそ会話が成り立つわけであって、
先に質問が来てその後で自分の感想を述べるといったらこれはチグハグというか意味を全くなさないですよね。
この整合性が取れていなかったら実際のウェブアプリケーションで様々な問題を起こしてしまいます。
例えばもしアリスの投稿に対してボブが返信したみたいなSNSの投稿を見ようとしたときにボブの返信だけが表示されて、
アリスの元投稿が見えないという状況に遭遇したら一体何が起こっているのかわからないと思います。
なので自分が作っているウェブアプリケーションにおいてその整合性の問題がどのような結果に起因するかというのを考えながら、
正しくデータベースの整合性モデルを選定したりアプリケーションレイヤーで整合性を担保するというのはかなり重要なスキルになってくると思います。
なのでまとめると今回の章を読む目的というのは分散システムにおいてデータの複製を行いながらも、
皆さんが作っているアプリケーションの要件に応じた適切な整合性レベルを選択するための理論といくつかのアルゴリズムを理解することかなと思っています。
キーワードとしてはいろいろ挙げられますけれども、リニアライザビリティ、線形可能性とかイベンチャルコンシスタンシー、結果整合性など様々な整合性モデルを一緒に学んでいこうかなと思います。
以前DDIA、デザイニングデータインテンシブアプリケーションとか関連の本を読んだことがある方は何回かキーワードに触れてきた方も多いかと思います。
実際僕もそうでしたけれども、今回の章を読みながら新しく記憶をリフレッシュする感じで読んできました。
ということで、それでは簡単に内容を振り返っていこうと思います。
まず、レプリケーションが必要なのか、そもそもなぜレプリケーションが必要なのかっていう大前提から簡単に話すと、
分散システム、ライターのデータベースとかリーダーのデータベースみたいに分かれているときには、
アベラビリティとかパフォーマンスを向上させるためにデータを複数のノートに複製するんですよね。
例えば、またSNSになっちゃうし、例えば動画を見る閲覧するYouTubeとか微妙でもなんでもいいですけれども、
動画を見るときにその動画をアップロードしたデータセンターがUSのリージョンにありますと、北米のリージョンにありますと、
でもユーザーはオーストラリアとかシンガポールとか日本からアクセスしてくるというときに、
それぞれのユーザーがその動画を見るたびにその都度USの北米のデータセンターにアクセスさせるとしたら、
それはすごい遅いしユーザー体験としても悪いですよね。
なのでそこで、元のデータはUSセントラルにあると北米にあるけれども、
そのデータをオーストラリアのデータセンターとか日本のデータセンターとかシンガポールのデータセンターにコピーした、
複製することで地理的に分散したユーザーによりパフォーマンス高く低レンシーでサービスを提供することができます。
しかももしUSセントラルのデータセンターが何かの理由でダウンしてしまったときには、
じゃあ一時的に日本のデータセンターにあるアプリケーションをリーダーに選出しましょうということで、
システム全体の可用性を向上させることができますよね。
なのでその複製というのは分散システムにおいても基本中の基本なんですけれども、
この複製を行うということは複数の場所に同じデータのコピーが存在するということなので、
これらの間の一貫性をどう保つかという問題が生じるという話でしたね。
まず本書で紹介されている重要なキーワードの一つ目がCAP定理です。
これはその分散システム設計における重要な概念キーワードで、
いろんなところで聞いたことがある方ももしかしたらいるかもしれません。
CAPというのは3つのキーワードの頭文字ですね。
Cはコンシスタンシー、一貫性、整合性。
Aがアベラビリティ、今やった可用性ですね。
そしてPがパーティション・トレンス、パーティションに対するトレンスなので、
パーティション、分断に対する体制というところですね。
この3つのうち、そのCAP定理が言おうとしているのは、
最大2つまでしか同時に満たすことができないというシンプリフィケーション、
単純化した定理のことですね。
例えばそのCPシステムというのは、
コンシスタンシーとアベラビリティを担保したシステムなんですけれども、
この具体例としては例えばGoogleのスパナーとか、
RDBMSなどがあって、これらはですね、
ネットワークの分断が発生した際に、
アベラビリティではなく一貫性を優先しますということなんで、
潜在的に不整合なデータを返すくらいなら、
リクエスト、エラーレスポンスを返すというところなので、
コンシスタンシーを選んでいるCPシステムなんですね。
一方でAPシステムというのは、
アベラビリティというパーティションのトレンスを選んでいます。
この代表例というのは、
例えばAmazon Dynamoとか、
あとCassandraなどのNoSQLデータベースとかがよく挙げられたりしますけれども、
これらは可用性を優先していて、
ネットワークの分断中でも、
潜在的に不整合な値であったとしても、
リクエストやエラーレスポンスを返し続けるというところですね。
例えばAmazon Dynamoが当時使われたときのショッピングカートみたいなユースケースでは、
多少の不整合が許容されるので、
サービス停止よりもレスポンスを優先するというところです。
これがそもそもキャップ定理というものがあります。
世の中にはいろんなところで言及されているものがありますというところですね。
ただ、この本書の面白いところは、
キャップ定理を紹介した上で、
より突っ込んだ著者のオピニオンという意見が書かれているんですね。
カサンドラのコミッターでもある彼はですね、
キャップ定理というのは、
皆さんが思ったようなそんなシンプルなものじゃないよ、
注意深く使用することがありますよ、みたいなことを言っていますと。
もちろんキャップ定理自体というのは、
複雑ないろんな要件のあるデータベースを、
たった3つのキーワードで表現しようとしている、
メンタルモデルなので、
もちろん現実のシステムに照らし合わせようとしたら合わないよね、
というところが出るのはもちろんなんですけど、
そのギャップを丁寧に説明してくれるという意味では、
価値のあるところかなと思います。
これはどういうことかというと、
例えば現実のシステムを考えてくださいと。
パーティションというのはネットワークの分担のことですけれども、
現実のシステムを考えたときに、
パーティションネットワーク分担って、
常に発生しているわけではないですよね、
というところから彼の問いは発しているわけですよね。
実際のアプリケーションを運用する、
プラクティカルな実践的な考えで見たときには、
通常時はCPかAPかみたいな、
そんなシンプルなバイナリーではなくて、
より細かい整合性レベルの運用が求められるし、
できますよね、ということを言いたいですね。
例えば分担が発生していないときは、
両方のCPかつAPという両方の性質をバランスよく持つこともできますよね。
だから、なんちゃってCAP、
だからCもAも保ったかつパーティショントレースなシステムを、
実践的にはプラクティカルには達成できますよね、
みたいなことを言いたいわけですね。
だから、彼の言いたいことを勝手にエスパーして想像すると、
はい、このデータベースはCPです。
だからアバイラビリティがいけてませんとか、
このデータベースはAPです。
だからコンシスタンシーがダメです、みたいな、
そういう物差しじゃないよね、ということを言いたいんじゃないかなと思います。
システムの状態によって、
コンシスタンシー、アベラビリティ、P、CAP、
すべてを保証できるタイミングもあれば、
ビジネスのアプリケーションの機能によっては、
CP、時々CP、時々AP、
みたいな特快非快使うことができるような場合もありますから、
その無理にキャップ定理という物差しで、
すべてのデータベースをラベリングしなくてもいいよね、
というのが筆者の主張なんじゃないかなと理解しています。
そうですね、こういうセオリー定理とかっていうのは全部そうだと思うんですけども、
ちょっと脱線しちゃいますが、
こういう定理とかっていうのは基本地図みたいなものだと思うんですよね。
現実世界っていうのはありますよね。
現実世界の道、山とか森みたいな。
現実世界には複雑性というのがありますけれども、
それをナビゲートしやすいように、
ある程度単純化したモデルとしての地図を作りますよね。
キャップ定理も分散システムの設計における地図みたいなものだと思うと、
いいかもしれませんね。
地図っていうのは現実世界のあくまで写像であり、
かなり抽象化というか、
単純化された概念なので、
実際の道を完璧に再現しているものではないんですよね。
だから地図では、
例えばGoogleマップを使っていろんなところにドライブしたり、
散歩したりしたことがある方は分かるかもしれないんですけど、
地図ではこの道は通れないと書いてあっても、
実際に行ってみるとなんだかんだ通れたりとか、
迂回路があったりとか、
一時的な橋がかかっているみたいなことだってありますよね。
だからその設計者は、
キャップ定理はあくまで地図なので、
それを参考程度にしつつ、
実際の複雑な現実世界を、
分散システムの現実を歩くときには、
ネットワークの状況とか、
ビジネス要件とか、
ユーザー体験を統合的に考慮してシステムを構築する必要があるよねっていう、
多分バズワードみたいなキャップ定理にちょっと釘を刺しているみたいなところが、
あるんじゃないかなと思ったりします。
これは改めて読んでみると、
なかなかいいことを言っているんじゃないかなと思いました。
分散システム関連のインタビューの練習とか、
YouTube動画とか記事とかでは、
キャップ定理、キャップ定理って言いますけれども、
キャップ定理はあくまで定理の一つだよね、
みたいな距離感がいいのかなと思います。
実はキャップ定理の話はまだまだ序盤で、
本書の革新というのが徐々に移っていくのが、
コンシスタンシーモデルのセクションですね。
ここがこの本書の目玉です。
整合性モデルいくつかは紹介されていましたね。
リニアライザビリティ、これは日本語で言うと線形化可能性、
あとシークエンシャルコンシスタンシー、
蓄磁整合性かな、あとはコーザルコンシスタンシー、
因果整合性、イベンチュアルコンシスタンシー、
結果整合性みたいな順番で紹介されていました。
実際のアプリケーションでリニアライザビリティが求められたことは、
僕のウェブ開発の経験ですね、
レシピプラットフォームとか広告事業とか、
eコマースでは特にありませんでしたね。
これらはおそらく僕がウェブアプリケーション界隈にいるというところで、
最終的なリニアライザビリティが求められなかっただけかもしれませんけれども、
僕の印象としては割と学術的というか理論的な整合性モデルなんですね。
次に見ていく整合性モデルというのが、
そのニュアンスも含めて正確に理解しておく必要があるかなと思っています。
まず一つ目がSequential Consistencyです。
これはリニアライザビリティとの大きな違いは、
全てのノードが同じ順番でイベント結果を確認できればいいよねということを保証するだけれども、
実際のクロックタイムというかリアルタイムの時計、タイムスタンプとかは気にしていないということなんですね。
例えばこれはSNSで考えてみると、
この整合性が担保されていないとしたらどういうことが起こるかという、
逆で考えてみると、
例えばアリスの投稿に対してボブがリプライしたみたいな状況で、
ボブのリプライが先に流れてきた後で、
アリスの投稿が後でフィードに流れてくるみたいな意味をなさない状態になってしまいますね。
銀行システムというか同様で、
このSequential Consistencyが担保されていないと、
アリスからボブに1億円送金して、
ボブがそのうちの半分の5000万をチャーリーに送金するみたいな、
この3人の中でお金のやり取りをするというシチュエーションを考えたときに、
アリスからボブには1億円行って、ボブからチャーリーに5000万行ったので、
ボブの手元には5000万残りますよね。
これがもしSequential Consistencyが保たれていなかったら、
ここでの2つのトランザクションイベントがありますよね。
先にボブからチャーリーへの送金イベントが記録され、
その後でアリスからボブへの送金が記録されるってなってしまったら、
これは結果としてボブの銀行残額が1億円になってしまうんですね。
本当は5000万なのに、
本当はアリスからボブの送金があった上で、
ボブからチャーリーの送金が成り立つわけなんですけども、
これが別の、違う順番で記録されてしまうと、
整合性が担保できないよね、みたいなところを言っています。
これをさらに推し進めたのが、
Causal Consistencyと、
これは因果整合性と呼ばれているんですけれども、
これはSequential Consistencyよりも緩い整合性モデルといっても、
差し支えがないと思います。
これは依存関係のあるイベント同士が結果整合であれば、
それぞれのノードが全てのイベントを同じ順番で処理しなくてもいいということなんですね。
これはSequential Consistencyの方は、
全てのイベントを全てのノードが同じ順番で処理することを期待しているんですけれども、
Causal Consistencyの場合は、
Logical ClockとかVector Clockみたいなものをあって、
そういうアルゴリズムを使っているんですけれども、
その依存関係のあるイベントが結果的に成功であれば、
ノードそれぞれが同じ順番で処理しなくても同じCausalなので、
Aが起こったらB、Bが起こったらC、つまりAからCみたいなイング関係が表現できればいいよねみたいなところなんですよね。
さらにその3つ目、もっともっと緩くした整合性モデルというのが、
Eventual Consistencyということがあって、
これはニュアンスが難しいんですけれども、
更新というのはシステム全体に非同期で伝播していって、
すべての更新が最終的にすべてのノードに、
データベースのノードとかインスタンスに到達すれば、
最終的に期間した状態になりますよねということを言いたいですね。
これちょっと具体例で紹介しようと思いますが、
Eventual Consistencyを実装したものとしては、
AmazonがDynamoというものを2006年くらいにペーパーを出しましたと。
Amazon Dynamoというのは、
AWS Dynamo DBというAWSのプロダクトの前身である、
社内版のキーバリストアです。
彼らが社内で作ったサービスを指すときはAmazon Dynamo、
僕らがAWSで使えるDynamo DBを指すときには、
AWS Dynamo DBと一応言い分けられるみたいなんですけど、
Amazon Dynamoの方でショッピングカートを、
Eventual Consistencyで実装した話は結構有名ですね。
というのも、ショッピングカートへの追加イベント、
商品の追加とか個数の変更、商品の削除というのは、
結果的にはすべてのノードに伝わるんですけども、
常に最新の状態が見えるわけではないですよね。
例えば、自分の携帯と自分のパソコンから、
同じAmazonアカウントにログインして何か買い物を考えると、
ただ決済するまでにちょっと時間があって、
バスに乗って地元のスーパーに行って、
物があるかどうか確認してから、
買うことを決めるみたいなシチュエーションだとしたときに、
まず自分のパソコンからアイテムAを3個、
アイテムBを5個追加したとしますよね。
その後で、もしかしてアイテムBはローカルにあるかもなということで、
ちょっとバスに乗って出かけて、
近くのスーパーまで確認しに行きますと。
帰ってきたので、その中はなかったので、
帰りのバスの中で自分の携帯からアイテムBを3個追加したとします。
なので、買いたい物としては、
アイテムAが3個、アイテムBが5を足す3で8個みたいな感じになりますよね。
このとき、例えば、
家に帰ってすぐパソコンを立ち上げたとしたときに、
パソコンを立ち上げた瞬間は、
自分のカートを見たときには、
アイテムAが3個、アイテムBが5個、
つまりバスで出かける前の状態が記録されていますと。
だけどそのチェックアウト、決済をするタイミングで、
まだ同期されていなかった携帯のアイテムBプラス3という情報がマージされて、
決済のタイミングでは、
アイテムAが3個、アイテムBが8個買います。
それでいいですか?みたいな確認画面が出てくるわけですよね。
この確認画面のところまでで、
結果成功であれば、
ショッピングカートの機能は満たしているよねというところを作ったんですね。
つまり、複数のデバイスからログインするときに、
必ずカートの状態が最新じゃなくても、
まあいいじゃんって言ったのが、
Amazon Dynamoを使ったショッピングカートなんですよ。