Google Cloud Platform

Cloud Spanner が外部整合性を提供する理由

皆さんは複雑なアプリケーション ロジックが好きですか? 私たちはそうではありません。というのも、私たちは Google で 1 つのことを学んだからです。それは、「データ ストアが提供する複雑なトランザクション処理やデータ整理の機能を開発者が利用できれば、アプリケーション コードはよりシンプルになり、開発期間が短縮される」ということです。

Google Cloud Spanner に関する最初の論文にはこう書かれています。「アプリケーション プログラマーは、トランザクション機能の欠如を補うためのコーディングに終始するよりも、トランザクションの過度の利用に伴うボトルネックの発生というパフォーマンス問題に取り組むほうが望ましい。」*1

言い換えれば、データセット全体にわたってトランザクションと整合性をデフォルトで提供するデータ ストアを使用することで、バグや問題が少なく、メンテナンスしやすいアプリケーション コードを作成できるのです。

データベースにおける整合性の定義

整合性について実りある議論を行うには、まず用語を定義することが重要です。市場に出回っているさまざまなデータベースを概観すると、すべての整合性モデルが同じというわけではなく、関連する用語の中には紛らわしいものもあることがわかります。そこで、整合性に関する基礎的な解説を以下の表にまとめました。

用語 定義 Cloud Spanner は何をサポートするか
整合性 データベース システムにおける整合性は、あらゆるデータベース トランザクションが、影響されるデータを、許可された方法でのみ変更しなければならないという要件を指します。データベースに書き込まれるデータは、定義されたすべてのルールに従って有効でなければなりません。*2 Cloud Spanner は外部整合性を提供します。外部整合性は、強整合性と追加的なプロパティ(直列化可能性と線形化可能性など)を兼ね備えたプロパティです。Cloud Spanner データベースのレプリカやリージョン内のトランザクションだけでなく、すべてのトランザクションがこの整合性プロパティを満たします。
直列化可能性 直列化可能性は、異なるトランザクションの一部が並列に複数のオブジェクトを読み書きする可能性がある、トランザクション分離プロパティです。トランザクションの挙動が、トランザクションが順次実行された場合と同じになることを保証します。ただし、その順序は、トランザクションが実際に実行された順序と違っても問題ないとされます。*3 Cloud Spanner は、直列化可能性よりも強いプロパティである外部整合性を提供します。これは、Cloud Spanner ではトランザクションの挙動が、トランザクションが順次実行された場合と同じになり、かつトランザクションが実際に実行される順序と、トランザクションの commit が認識される順序が必ず整合することを意味します。
線形化可能性 線形化可能性は、レジスタ(個々のオブジェクト)の読み書きの順序を保証します。オペレーションをトランザクションとしてグループ化しないので、競合のマテリアライズのような対策を講じなければ、書き込みスキューのような問題を防げません。*4 線形化可能性はトランザクションの動作に影響しないので、Cloud Spanner が提供する外部整合性は、線形化可能性よりも強いプロパティです。線形化可能性は、単一オブジェクトに対する単一の読み取りまたは書き込みオペレーションのみがトランザクションに含まれる、外部整合性の特殊なケースと見なすことができます。
強整合性 すべてのアクセスがすべての並列プロセス(またはノード、プロセッサなど)によって、同じ順序で(順次実行されるように)見えます。*5 レプリケートされたオブジェクトが線形化可能である場合、レプリケーション プロトコルは強整合性を示すという定義もあります。 Cloud Spanner のデフォルトの読み取りモードは「強力」です。これにより、どのレプリカが読み取りを受け取るかに関係なく、オペレーションの開始前に commit されたすべてのトランザクションが必ず反映されます。
結果整合性 結果整合性は、データベースへの書き込みを停止してしばらく待つと、最終的に、すべての読み取り要求に対して同じ値が返されることを意味します。*6 Cloud Spanner がサポートするバウンド ステイルネス読み取りでは、結果整合性と同等の高いパフォーマンスを実現でき、しかもはるかに強い整合性保証が得られます。

注目すべきは、強整合性と直列化可能性のすべてのメリットをもたらす外部整合性を Cloud Spanner が提供することです。Cloud Spanner データベースのレプリカのトランザクションだけでなく、すべてのトランザクション(行、リージョン、大陸をまたぐ)が、この外部整合性プロパティを満たします。

外部整合性により、Cloud Spanner でのトランザクション実行方法は、トランザクションが順次実行されるシステムと区別がつきません。さらに、トランザクションの commit が認識される順序は順次実行順序と整合します。外部整合性は、線形化可能性と直列化可能性のどちらよりも強いプロパティです。

整合性の実際

外部整合性が必要なユース ケースはたくさんあります。たとえば、ユーザーの口座残高を表示する必要がある金融アプリケーションはその 1 つです。ユーザーは預金をすると、その金額がすぐに残高に反映されることを求めます(反映されないと、お金が消えた! と真っ青になるかもしれません)。口座残高は、実際より多くても少なくても許されません。

また、もう 1 つの例としてメールやメッセージング アプリがあります。ユーザーはメッセージ画面で「送信」をクリックすると、すぐに「送信済みメッセージ」を確認します。書いた内容を二重チェックしたいからです。外部整合性が確保されていない場合は、送信済みメッセージを取り出す要求が、すべての状態変更が反映されていない別のレプリカに届き、表示されたメッセージ内容が送信時とは異なるかもしれません。そうなれば、ユーザーを混乱させてしまい、ユーザー エクスペリエンスは貧弱なものになってしまいます。

外部整合性があるというのは、技術的な観点から見ると、どういうことなのでしょうか。読み取りオペレーションを実行するケースについて考えてみると、外部整合性は、グローバル レベルでデータの最新コピーを読み取ることを意味します。つまり、すべての行、リージョン、大陸においてデータに最も新しく加えられた変更を読み取ることなのです。

開発者の観点で見ると、外部整合性は、データベース全体(行やオブジェクトだけでなく)の状態について、あらゆる時点で一貫したビューを読み取れることを意味します。整合性がこのレベルに達していないと、アプリケーション設計が複雑化することがあります。その場合、ソフトウェアが不安定でメンテナンスしにくくなり、開発者や運用担当者はメンテナンス上の問題を数多く抱えることになりかねません。

マルチマスター アーキテクチャや複数の整合性レベルは、外部整合性を提供できないために採用する代替策です。

外部整合性より整合性が低いデータベース システムを使用すると、どのような問題があるのでしょうか。緩い / 結果整合性モードを選択した場合は、個々のユース ケースごとにどの整合性モードを使用する必要があるかを理解したうえで、オペレーションの正確性と順序を保証するために、厳密なトランザクション ロジックをアプリケーションにハード コードしなければなりません。

ドキュメント / オブジェクト / 行全体にわたって限られた整合性しかない、あるいは強整合性を備えていないデータベース システムで “トランザクション” を利用するには、複数の “モノ” に同時に変更を加える必要がないようなアプリケーション スキーマを設計しなければなりません。しかし、これは大きな制限です。アプリケーション レイヤでの代替策は手間がかかり、複雑で、多くの場合、バグだらけになってしまいます。

さらに、こうした代替策はシステムのいたるところに適用する必要があります。たとえば、カラー スキームの設定ボタンを管理用設定パネルに追加するケースを見てみましょう。こうした単純な機能を、アプリケーション全体や他のデバイス、セッション全体にわたってすぐに反映させるためには、同期型で強整合性の更新を行わなければなりません。さもないと、その場しのぎの方法で同じ結果を得なければならなくなります。

代替策を用いてアプリケーション レベルで強整合性を実現すると、それ以降は新機能を追加するたびに余計に時間がかかります。新機能がいかに小規模でも事態は変わりません。また、アプリケーション開発チームを拡大するのも非常に難しくなります。こうした強引なアプローチを取るには、チーム メンバー全員がエキスパートでなければならないからです。しかも、アプリケーションが開発者のワークステーション上でユニット テストに合格したとしても、大規模な本番環境できちんと動く保証はありません。高並列アプリケーションでは特にそうです。

結果整合性を持つデータ ストアに代替策を講じると、バグの混入を招き、顧客のデータを破損させてしまうまで気づかないことがよくあります。実のところ、それ以前に、そもそも代替策が必要なことすら認識されないかもしれません。

多くのアプリケーション開発者は、外部整合性や強整合性のパフォーマンス負荷は大きすぎると考えています。確かに、一部のシステムではそれは事実かもしれません。私たちも、選択肢があるのは良いことだと固く信じています。ただし、それはデータベースが、アプリケーションの不要な複雑さや潜在的なバグを招かない限りにおいてです。

私たち Google は、アプリケーション開発者に対して彼らが必要とするパフォーマンスを提供するとともに、彼らのアプリケーション コードから不要な複雑さを排除することを目指しています。このゴールに向けて先進的な分散データベース システムを長年研究し、強整合性を適切に確保できる幅広いデータ ストアを構築してきました。

たとえば、行内での強整合性を提供する Cloud Bigtable、ドキュメントやオブジェクト内での強整合性を提供する Cloud Datastore、行 / リージョン / 大陸をまたぐ強整合性を直列化可能性とともに提供する Cloud Spanner などです(実のところ、Cloud Spanner は強整合性より強力な外部整合性(強整合性と直列化可能性の両方)を保証しますが、私たちは Cloud Spanner を説明する際に強整合性について述べる傾向があります。強整合性のほうが広く知られている用語だからです)。

強整合性のある読み取りと Cloud Spanner

Cloud Spanner は、低レイテンシで高スループットの強力な読み取り(強整合性のある読み取り)をデフォルトで提供するべく一から設計されました。TrueTime のユニークなパワーのおかげで、Cloud Spanner は、複雑なマルチフェーズ コンセンサス プロトコルやいかなるロックも使用することなく、任意のクエリの強力な読み取りを提供します。Cloud Spanner による TrueTime の利用も、グローバルなバウンド ステイルネス読み取りができるという付加的なメリットをもたらします。

しかも、Cloud Spanner は単一リージョンとマルチリージョンの両方の構成で強整合性を提供します。これに対してグローバルに分散された他のデータベースは開発者にジレンマをもたらします。地理的に分散されたリージョンからデータの読み取りを行いたいときでも、強整合性のある読み取りができないからです。こうした他のシステムでは、顧客が強整合性のある読み取りを行うことを選択すると、リモート リージョンから読み取りを行う機能を利用できません。

Cloud Spanner が提供する外部整合性の保証を最大限に活用し、アプリケーションのパフォーマンスを最大化するには、以下の 2 つを行うことをお勧めします。

  1. 可能なかぎり強力な読み取りを常に使用する : 強力な読み取りは強整合性を提供し、常にデータの最新コピーを読み取ることを保証します。強整合性により、アプリケーション コードがシンプルになり、アプリケーションの信頼性が向上します。
  2. レイテンシが理由で強力な読み取りを実行できないのであれば、最新データの強力な読み取りが不要のときはバウンド ステイルネス読み取りを使用し、パフォーマンスを高める : バウンド ステイルネス セマンティクスは、あらかじめ設定されたタイムスタンプ バウンド(所定時間の範囲内などの条件)に従って整合性のあるデータを読み取ることを保証します。これに対し、結果整合性を提供するデータベース システムでは何も保証がなく、アプリケーションは、クエリが発行された時間の前後のほとんどどのようなデータも読み取ることが可能です。

強整合性が保証されない環境を使用することにはリスクが付きまといます。データベースを対象とした強力な読み取りでは、データの最新コピーを読み取ることになり、データセット全体の参照整合性が維持され、同時リクエストについて判断を行うのも簡単です。弱い整合性モデルは、ソフトウェア バグのリスクを招き、開発時間の浪費につながることがあり、顧客の信頼を損なってしまうおそれもあります。

強整合性と書き込み

強整合性は、書き込みオペレーション(特に読み取り / 変更 / 書き込みトランザクション)でさらに重要です。そうした状況で強整合性を提供しないシステムは、アプリケーション開発者に負担を強います。データを整合性のない状態に置くおそれが常にあるからです。

最もたちの悪いタイプの問題は書き込みスキューかもしれません。書き込みスキューでは、2 つのトランザクションが一連のオブジェクトを読み取り、それらの一部に変更を加えます。しかし、各トランザクションが行う変更は、各トランザクションが読み取るべきだったものに影響を与えます。

たとえば、サンフランシスコに本拠を置く航空会社のデータベースを考えてみましょう。この航空会社は、常に予備機を 1 機、サンフランシスコに待機させておくというポリシーを採用しています。メンテナンス上の問題やその他の理由から、他の旅客機の代わりが必要になるケースを想定してのことです。

ここで、以下のように、近くサンフランシスコを発つフライトのために旅客機を予約するトランザクションが 2 件あるとします。

  Begin Transaction
SELECT * FROM Airplanes WHERE location = "San Francisco" AND Availability = "Free";
If number of airplanes is > 1:  # to enforce "one free plane" rule
Pick 1 airplane
Set its Availability to "InUse"
Commit
Else: Rollback

強整合性(そして特に、これらのトランザクションの直列化可能な分離)が保証されていない場合は、両方のトランザクションが commit に成功する可能性があります。そうなれば、予備機を 1 機待機させておくポリシーに違反してしまうかもしれません。このように、書き込みスキューが問題を引き起こすケースは多々あります。*7

Cloud Spanner は、(複雑な複数行および複数テーブル トランザクションでも)強力なトランザクション整合性を確保できるリレーショナル データベースとして一から開発されました。したがって、NoSQL データベースがアプリケーション開発者にとって頭痛の種となるさまざまな状況でも使用できます。書き込みスキューのような問題からアプリケーションを保護できるので、金融や物流、ゲーム、マーチャンダイジングなど多くの分野のミッションクリティカル アプリケーションに適しています。

Cloud Spanner とマルチマスター レプリケーションの違い

スケーラビリティと整合性に関する議論でよくひとまとめにされる 1 つのトピックが、マルチマスター レプリケーションです。しかし、マルチマスター レプリケーションは根本的に、垂直スケーラビリティを備えたデータベース システムの平均回復時間を短縮するために使われる手法です。言い換えれば、ディザスタ リカバリ ソリューションであり、グローバルな強整合性ソリューションではありません。マルチマスター システムの場合、読み取りのスケーリングとディザスタ リカバリのために、各マシンがデータセット全体を含み、変更は他のマシンにレプリケートされます。

これに対し、Cloud Spanner は真の分散システムです。データはレプリカ内の複数のマシンに分散されるとともに、複数のマシンや複数のデータセンターにレプリケートされます。Cloud Spanner とマルチマスター レプリケーションの主な違いは、Cloud Spanner が Paxos を使って書き込みをリージョン外に同期的にレプリケートし、単一のサーバー / クラスタ / リージョンで障害が発生した場合も稼働を継続する点です。

リージョン外への同期的なレプリケーションは、リージョンが使用不能な場合でも整合性を維持でき、強整合性のあるデータをダウンタイムなしで提供できることを意味します。使用不能なリージョンのせいで、承認された書き込みが遅れたり、失われたりすることはありません。

Cloud Spanner での Paxos の実装は、リーダーを選ぶことで、時間集約型のクォーラム読み取りによる強整合性の確保を不要にしています。さらに、Cloud Spanner はデータをサーバー間で水平にシャーディングし、個々のマシンの障害に影響されるデータを少なく抑えています。ノードの回復時には、そのデータセットを含み、レプリケートされていた他のクラスタ上のノードが簡単にマスターとして機能し、強力な読み取りを実現します。ユーザーにはダウンタイムを感じさせません。

ミッションクリティカルなデータの強整合性ソリューション

Cloud Spanner は、重要なトランザクション データをクラウドに保存できるようにするため、強力な外部整合性、リレーショナル セマンティクス、高可用性、水平スケーリングのユニークな組み合わせを提供します。厳密な整合性を保証することは、信頼性の高いサービスを提供するうえで不可欠です。Cloud Spanner は、こうした保証を高いパフォーマンスと直感的な操作で提供するために一から開発されました。ぜひこのサービスをお試しになり、使いこなしていただければ幸いです。

Cloud Spanner外部整合性の詳細はウェブ ページやドキュメントをご覧ください。

*1 https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf
*2 https://en.wikipedia.org/wiki/Consistency_(database_systems)
*3 Kleppmann, Martin. Designing Data-Intensive Applications. O’Reilly、2017、p. 329
*4 Kleppmann, Martin. Designing Data-Intensive Applications. O’Reilly、2017、p. 329
*5 https://en.wikipedia.org/wiki/Strong_consistency
*6 Kleppmann, Martin. Designing Data-Intensive Applications. O’Reilly、2017、p. 322
*7 Kleppmann, Martin. Designing Data-Intensive Applications. O'Reilly、2017、p. 246

* この投稿は米国時間 1 月 11 日、Cloud Spanner 担当の Software Engineer である Mike Curtiss によって投稿されたもの(投稿はこちら)の抄訳です。

- By Mike Curtiss, Software Engineer, Cloud Spanner