【入門編】 ロックの仕組み – Cloud Spanner

こんにちは!Cloud Spannerの世界へようこそ。
大規模なシステムを支えるデータベースとして大人気のCloud Spannerですが、「ロックの仕組み」や「デッドロック」と聞くと、なんだか難しそうだな……と身構えてしまいますよね。

大丈夫です。今回は、データベースのロックが持つ本質を、日常の「あるあるなシチュエーション」に例えながら、一緒に優しく紐解いていきましょう。ここさえクリアすれば、あなたもCloud Spannerの基本はバッチリマスターできますよ!

—

1. データベースの「ロック」ってなぁに?(日常の例え)

まずは、データベースにおける「ロック」の概念を、オフィスの共有デスクを想像して考えてみましょう。

  • 共有ロック(みんなで見る用):

分厚いマニュアル本を、何人かで「どれどれ、ちょっと読ませて」と同時に覗き込んでいる状態です。みんなで読むだけなら同時に何人でもOKですよね。これが「共有ロック」です。

  • 排他ロック(私物として独占する用):

そのマニュアルに「重要な書き込み」をしようとしている状態です。書き込んでいる最中に他の人にページをめくられたら困るので、その本を自分専用の机に引き出し、鍵をかけて他の人が触れないようにします。これが「排他ロック」です。

Cloud Spannerでも全く同じことが起きています。データを「読むだけ」のときは他の人の邪魔をしませんが、「書き換える・新しく登録する」ときは、他の人が同時に手を出さないように、ガチャンと鍵(ロック)をかけるのです。

—

2. Cloud Spannerならではの「ロックの特徴」

一般的なデータベース(MySQLやPostgreSQLなど)だと、「データを読み込んでいる最中も、書き込みをブロックしちゃう……」なんてことがよくあって、システムが混み合う原因になります。

しかし、Cloud Spannerはここが違います!

圧倒的な強み:「読み取り」は世界を止めない

Cloud Spannerは、過去のデータの状態を綺麗に保存しておく技術(MVCC:多版同時実行制御)を持っています。そのため、誰かがデータを書き換えている最中でも、別の人は「ちょっと前の綺麗な状態のデータ」をスイスイ読み込むことができます。
つまり、データの読み書きがケンカしてシステム全体の動きが重くなる……という現象が起きにくい、非常にスマートな仕組みになっているんです。

とはいえ、「書き込み(更新)」と「書き込み」のぶつかり合いでは、やはりロックの制御が必要になります。それが次の「デッドロック」という魔物です。

—

3. 恐怖の「デッドロック」の発生条件

デッドロックとは、いわば「お見合い状態」です。これをオフィスで例えてみましょう。

1. あなたが「ホッチキス」を手に取りました(ロックAを取得)。
2. 同僚のA君が「のり」を手に取りました(ロックBを取得)。
3. このとき、あなたは「その『のり』も貸してよ」とA君に言いました(ロックBを待つ)。
4. 同時に、A君は「いや、その『ホッチキス』をこっちに貸してくれよ」と言いました(ロックAを待つ)。

……さあ、どうなったでしょう?
お互いに相手が持っている道具を欲しがり、自分が持っている道具は絶対に放さないため、2人とも永遠に待ちぼうけになってしまいます。これがデッドロックです。

Cloud Spannerでも、複数のデータの書き換えを同時に行うとき、プログラムの順番が噛み合わないと、このデッドロックが発生してしまいます。

—

4. アプリケーション側での「リトライ戦略」という名の処方箋

「じゃあ、デッドロックが起きたらシステムが止まっちゃうの!?」と不安になりますよね。

安心してください。Cloud Spannerの世界では、「デッドロックが起きることは、たまにある前提」でシステムを作ります。もしデッドロックが発生すると、Cloud Spannerは優しくこう言ってくれます。

> 「おっと、お互いに行き詰まっちゃったみたいだから、こっちの一方の処理をいったんキャンセルするね。ごめんけど、もう一回はじめからやり直して(リトライして)もらえるかな?」

この「お互い様の精神」で、失敗した処理を自動的にもう一度やり直す仕組みを、アプリケーション側に持たせてあげること。これが、Cloud Spannerを使いこなす上で最も大切な「リトライ戦略」です。

実装のイメージ(心構えとしてのコード例)

実際のアプリケーションでは、次のようなイメージで「もしロックで怒られたら、少し時間を置いてもう一度トライする」というループを組みます。

擬似的なPythonコードによるリトライのイメージ
max_retries = 3
for attempt in range(max_retries):
try:
# トランザクションの開始
with database.transaction() as tx:
# データの読み書き処理(ここでロックがかかる可能性がある)
tx.execute_update(
“UPDATE Accounts SET Balance = Balance – 100 WHERE Id = 1”
)
tx.execute_update(
“UPDATE Accounts SET Balance = Balance + 100 WHERE Id = 2”
)
# 成功したらループを抜け出す
print(“トランザクション成功!”)
break

except Exception as e:
# もしCloud Spannerから「混み合ってるからやり直して」と言われたら…
if “Aborted” in str(e):
print(f”デッドロック等を検知しました。リトライします… (試行回数: {attempt + 1})”)
continue # もう一度やり直す!
else:
# 関係ないエラーならそのまま例外を投げる
raise e

このように、「エラーが起きても慌てず、数回リトライするお作法」をコードに仕込んでおくだけで、あなたのシステムは驚くほど頑丈(ロバスト)になります。

—

まとめ

いかがでしたでしょうか?

  • 共有ロックは「みんなでマニュアルを見る(同時OK)」
  • 排他ロックは「自分だけのノートに書き込む(独占)」
  • デッドロックは「お互いに道具の貸し借りで膠着状態になること」
  • リトライ戦略は「膠着したら一度やめて、少し待ってからやり直すこと」

Cloud Spannerのロックの仕組みは、一見難しそうに見えますが、本質はとてもシンプルです。この基本さえ押さえておけば、どれだけデータが増えても怖くありません。

ぜひ、この知識を武器に、Cloud Spannerでの開発を楽しんでくださいね!あなたのエンジニアライフを応援しています。

コメント

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