【入門編】 外部整合性 (External Consistency) – Cloud Spanner

こんにちは!クラウドシステムの設計やデータベースの裏側を覗くのが大好きな先輩エンジニアです。

今回は、世界中のシステムエンジニアがその美しさに唸る、Cloud Spannerの心臓部「外部整合性(がいぶせいごうせい)」についてお話しします。

「なんだか難しそうな名前だな……」と思いましたか?
安心してください。専門用語の壁をすっと取り払って、日常の身近な例えを使いながら、この凄まじい仕組みの本質を一緒に紐解いていきましょう。

ここをクリアすれば、Cloud Spannerの基本はバッチリマスターできますよ!それでは、コーヒーでも飲みながらリラックスして読んでいってくださいね。

—

1. 世界中のデータが「一瞬で」一致する不思議

私たちが普段使っているスマホアプリやWebサービス。裏側ではデータベースが動いていますが、もし「東京のサーバー」と「アメリカのサーバー」で同時にデータを書き換えたらどうなるでしょうか?

普通のデータベースだと、光の速さの限界(物理的な距離の壁)があるため、どうしても情報の伝達にタイムラグが生まれます。「あれ、さっきアメリカで更新したはずのデータが、東京の画面にまだ反映されていないぞ?」という現象が起きてしまうわけです。

しかし、Cloud Spannerは違います。地球の裏と表で同時にデータを更新しても、世界中のどこから見ても「絶対に矛盾のない、正しい順番」でデータが見えるようになっています。

この「現実世界で起きた出来事の順番と、データベース上の記録の順番が完全に一致する性質」を、コンピュータ科学の世界では外部整合性(External Consistency)、あるいはリニアizability(線形可能性)と呼びます。

—

2. 日常で例えてみよう:世界時計の「誤差」との戦い

なぜこれがすごいことなのか、身近な例で考えてみましょう。

いま、遠く離れた場所にいるA君とB君がいるとします。

  • A君(東京):「12時00分00秒」に銀行口座から1万円を引き出しました。
  • B君(ニューヨーク):「12時00分01秒」に同じ口座に残っていた全額を引き出しました。

現実世界では、A君の操作が先で、B君の操作が後です。したがって、データベースの記録も必ず「A君の処理 $\to$ B君の処理」の順番で並ばなければなりません。もしこれが逆になって、B君の処理が先になったら、A君の引き出しが「残高不足」でエラーになってしまいますよね。

ここで問題になるのが、「パソコンやサーバーの内蔵時計は、わずかにズレている」ということです。東京の時計とニューヨークの時計が、もし0.5秒ずれていたらどうでしょう? ネットワークの遅延も合わさって、コンピュータには「あれ? B君の操作の方が先に来たぞ?」と誤認されてしまうリスクがあります。

この「時計のズレ」という致命的な弱点を、物理とテクノロジーの力で華麗に解決したのが、Cloud Spannerの秘密兵器「TrueTime(トゥルータイム)」です。

—

3. TrueTimeの魔法:「今、何秒か」を「幅」で捉える

普通の時計は、「今は12時00分00.00秒です」と点(1つの時刻)で答えます。だからズレたときに困るのです。

しかし、Spannerの心臓部にあるTrueTimeは違います。GPS衛星と原子時計を組み合わせることで、時刻を「幅(タイム・アンソティ/不確実性)」として把握します。

> 「今の正確な時間は、だいたい12時00分00.00秒から、12時00分00.07秒の間です!」

このように、あえて「ズレの幅(多くの場合数ミリ秒以内)」を認めてあげるのです。

Spannerはどうやって順番を守るのか?

Spannerはこの「時間の幅」を利用して、次のようなルールを定めています。

1. A君のトランザクションが完了したときの「時間の幅」を記録する。
2. 次にB君のトランザクションが始まる時、「A君の時間の幅の終わり」よりも、自分の「時間の幅の始まり」が後になるまで、ほんの数ミリ秒だけ待つ(コミット・ウェイト)。

この絶妙な「ちょっと待つ」という仕掛けにより、物理的な時間の逆転を完全に防ぎます。これにより、現実世界の因果律(原因が先で、結果が後)が、グローバル規模のデータベースでも完璧に守られるのです。これが外部整合性の正体です。

—

4. 実際にコードと挙動をイメージしてみる

Cloud Spannerを操作する際、開発者はこの複雑な「時計の同期や順番の制御」を意識する必要はありません。Spannerが裏側で完璧にやってくれます。

例えば、Pythonを使ったCloud Spannerへの書き込みイメージは次のような形です。

from google.cloud import spanner

クライアントの初期化
client = spanner.Client()
instance = client.instance(“my-global-instance”)
database = instance.database(“my-database”)

def update_balance(transaction, user_id, amount):
“””指定されたユーザーの残高を更新するトランザクション

Cloud Spannerは、このトランザクションが「いつ世界に確定したか」を
外部整合性を保った状態で安全に記録します。
“””
# 1. 現在の残高を読み込む
row_iters = transaction.execute_sql(
“SELECT balance FROM Accounts WHERE user_id = @user_id”,
params={“user_id”: user_id},
param_types={“user_id”: spanner.param_types.INT64},
)
current_balance = list(row_iters)[0][0]

# 2. 残高を計算
new_balance = current_balance + amount

# 3. データを書き戻す
transaction.update(
table=”Accounts”,
columns=[“user_id”, “balance”],
values=[(user_id, new_balance)],
)

トランザクションの実行
Spannerは内部でTrueTimeを使い、世界中のどこから呼ばれても
矛盾のない順序でこの処理を安全に確定(コミット)させます。
database.run_in_transaction(update_balance, user_id=101, amount=1000)

開発者はただ普通にトランザクションを書くだけで、グローバル規模で「データの整合性が絶対に壊れない」という最高峰の安心を手に入れることができます。

—

5. まとめ:なぜCloud Spannerの外部整合性は革命的なのか?

これまでのデータベースの歴史では、大きなジレンマがありました。

  • 「データをあちこちに分散させて速くする(可用性・スケール)」か、
  • 「一つの場所にデータを集めて順番を厳密に守る(強整合性)」か。

この2つはトレードオフ(一長一短)であり、両立は不可能だと長年言われてきました。それを「TrueTime」というハードウェアとソフトウェアの融合によって見事に打ち破ったのが、Cloud Spannerの外部整合性です。

「世界中どこにデータを置いても、あたかも目の前にある1つのノートにペンで順番に書き込んでいるかのように、矛盾なく記録される」。

このアーキテクチャの美しさと力強さを知ると、クラウドデータベースの設計が一段と楽しくなりますよね。ここを理解していれば、Spannerを使ったシステム設計の基本はもうバッチリです!

それでは、次回のコアアーキテクチャ解説もお楽しみに!

コメント

タイトルとURLをコピーしました