こんにちは!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での開発を楽しんでくださいね!あなたのエンジニアライフを応援しています。
コメント