見出し画像

SQLの悲観ロックと楽観ロックについて説明

この記事では悲観ロックと楽観ロックについて説明しています。
悲観ロック、楽観ロックは以下のような問題を解決することができます。

複数のユーザがデータベースを共有して使う環境で、いかにして互いに干渉せずにトランザクションを扱うか。(同時実行制御とも呼ばれます。)

noteでの具体例を挙げます。noteには記事に「スキ」をすることができます。スキを複数のユーザがした場合は、スキの合計はいくつになるでしょうか?

最初はスキの数は0です。ここでAさんとBさんが同時にスキをした時にちゃんと2にできるようにすることが大切です。

この問題を解決する方法を2つ紹介します!

悲観的ロック

悲観的なロックは、トランザクション同士がお互いに衝突するという前提で考えています。なので、データベースを操作する時は必ずロックを使います

ロックとはデータベースに設定されているフラグです。ロックによって他のユーザがアクセスできるかどうかを判定しています。

具体例
noteの「スキ」を例に悲観ロックについて説明します!

記事のスキは最初は0です。
AさんとBさんが同時にスキをします。
Aさんがスキ数を取得した時は0なので+1してスキ合計を1にします。
Bさんがスキ数を取得した時は0なので+1してスキ合計を1にします。
BさんがAさんのトランザクションが終わる前にトランザクションを始めたので、スキ合計が2になっていないです。

複数のユーザが同時にスキをした時に発生する問題

悲観ロックで問題を解決

記事のスキは最初は0です。
AさんとBさんが同時にスキをします。
Aさんがスキ数を取得したタイミングでトランザクションが始まり、ロックします。
Aさんがスキ数を取得した時は0なので+1してスキ合計を1にします。
Bさんがスキ数を取得した時はAさんのトランザクションがあるので、スキ数を取得できないです。
Aさんのトランザクションが終わった時に、改めてBさんはトランザクションを行います。

悲観ロックの仕組み

ロックにはレベルがあります。レベルによってロックできるサイズが異なります。

  • データベース全体ロック

    • 一度に1つのトランザクションしか動かせなくなる。

    • 使用するとしたら、データベース全体をメンテナンスする時

  • テーブルロック

    • テーブル全体を処理対象にするトランザクションがあれば、そのトランザクションでも1つのフラグだけ使用する。

  • 行ロック

    • 他のユーザもテーブルの他の行へアクセスすることができます。

  • ページロック

    • テーブル内の(ロックしたい行を含む)行の一部にたいしてロックをします。

楽観的ロック

楽観的ロックは、トランザクション同士がそんな頻繁には衝突しないという前提で考えています。

楽観的ロックの手法にはスナップショット分離があります。
スナップショット分離は簡単に言い換えると「早いもの勝ち」です。

記事のスキは最初は0です。
AさんとBさんが同時にスキをします。
Aさんがスキ数を取得した時は0なので+1してスキ合計を1にします。
Bさんがスキ数を取得した時は0です。
BさんよりAさんの方が先に処理を終えたので、Bさんは処理をロールバックして、改めてBさんはトランザクションを行います。

スナップショット分離

※実際はタイムスタンプを利用しています。トランザクションが開始時点は開始タイムスタンプ、コミット時点はコミットタイムスタンプがあります。

スナップショット分離で起こりうる問題A Critique of ANSI SQL Isolation Levelsにまとまっています。

悲観ロックと楽観ロックの違い

悲観ロックの場合は、スキ数を取得したタイミングでロックがかかるのでBさんはスキ数を取得できないです。
楽観ロックの場合は、+1するタイミング重要なので、Bさんの方が先に+1するとAさんのトランザクションがロールバックします。

まとめ

この記事では、複数のユーザがデータベースを共有して利用する時に起きる問題を解決する手段として悲観ロックと楽観ロックがあることを説明しました。
スキやコメントをしていただけると、記事を書くモチベーションが高まるので、この記事がいいなっと思った方はよろしくお願いします!

この記事が気に入ったらサポートをしてみませんか?