関係データベースにおいて、悲観的同時実行制御(悲観ロック)と楽観的同時実行制御(楽観ロック)は、リソースの同時実行制御に主に採用される解決策です。
悲観ロックでも楽観ロックでも、それは人々が定義した概念であり、一種の思想と考えることができます。実際、関係データベースシステムだけでなく、memcache、hibernate、tair などにも類似の概念があります。
異なるビジネスシーンに応じて、異なる同時実行制御方式を選択するべきです。データに提供されるロックメカニズム(行ロック、テーブルロック、排他ロック、共有ロック)と混同しないようにしましょう。
以下で、悲観ロックと楽観ロックをそれぞれ理解していきましょう。まずは、商品テーブルの簡単な設計を定義します:
CREATE TABLE `products` (
`id` INT unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`quantity` INT unsigned COMMENT '在庫',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
悲観ロック#
データベース内のデータを変更する際、他の人による同時変更を避けるための最良の方法は、そのデータに対して直接ロックをかけて並行処理を防ぐことです。
このように、データを変更する前にデータベースのロックメカニズムを利用してロックをかけ、その後に変更を行う方法は、悲観的同時実行制御(別名「悲観ロック」、Pessimistic Concurrency Control、略称「PCC」)と呼ばれます。
この設計は「一ロック二チェック三更新」モデルを採用しており、データベースに備わっている select ... for update
キーワードを使用して、現在のトランザクションに行レベルのロックを追加し、操作するデータをロックした後、対応するデータをクエリして更新操作を実行します。
BEGIN;
SELECT quantity FROM products WHERE id = 1 FOR UPDATE;
UPDATE products SET quantity = quantity - 1 WHERE id = 1;
COMMIT;
MySQL データベースの InnoDB では、デフォルトで行レベルロックが使用されます。行レベルロックはインデックスに基づいており、SQL 文がインデックスにヒットしない場合は行レベルロックは使用されず、テーブル全体がロックされます。したがって、
select ... for update
では、クエリ範囲をできるだけ狭め、インデックスにヒットさせることが重要です。
悲観的同時実行制御は主にデータ競争が激しい環境で使用され、並行衝突が発生した場合にロックでデータを保護するコストがトランザクションのロールバックコストよりも低い環境で使用されます。
楽観ロック#
楽観ロック(Optimistic Concurrency Control、略称「OCC」)は、悲観ロックに対して、一般的にはデータが衝突を引き起こさないと仮定します。そのため、データの更新をコミットする際に、初めてデータの衝突の有無を検出します。衝突が発生した場合は、ユーザーにエラーメッセージを返し、ユーザーがどのように対処するかを決定させます。
楽観ロックはデータベースが提供するロックメカニズムを使用しません。一般的な楽観ロックの実装方法は、データのバージョンを記録することです。データバージョンはバージョン番号またはタイムスタンプで実現できます。
SELECT quantity, version FROM products WHERE id = 1;
UPDATE products SET quantity = quantity - 1, version = version + 1
WHERE id = 1 AND version = 元のバージョン番号;
バージョン番号を使用する場合、データの初期化時にバージョン番号を指定し、データの更新操作ごとにバージョン番号を + 1 します。そして、現在のバージョン番号がそのデータの最新のバージョン番号であるかどうかを判断します。
しかし、バージョン番号を使用する場合、高い同時実行性のあるサイトでは、1 つのスレッドのみがデータを正常に変更でき、多くのビジネスエラーが発生し、ユーザーにとって非常に不親切です。
したがって、ビジネスの状況に応じて楽観ロックの強度を減少させ、スループットを最大限に向上させ、同時実行能力を高めることができます。たとえば、商品を注文するビジネスでは、商品に在庫があれば注文できます。
UPDATE products SET quantity = quantity - 1 WHERE id = 1 AND quantity - 1 > 0;
楽観的同時実行制御は主にデータ競争が少ない環境で使用されます。このような環境では、トランザクションをロールバックするコストがデータを読み取る際にデータをロックするコストよりも低いため、他の同時実行制御方法よりも高いスループットを得ることができます。
簡単な違い#
-
楽観ロックは実際にロックをかけていないため、効率が高い。一度ロックの粒度をうまく把握できないと、更新失敗の確率が高くなり、ビジネスの失敗が起こりやすい。
-
悲観ロックはデータベースロックに依存しているため、効率が低い。更新失敗の確率は比較的低い。
参考リンク#
- https://learnku.com/articles/27880
- https://blog.debuginn.com/p/mysql-lock-occ-pcc
- https://www.hollischuang.com/archives/934
謝辞#
ご覧いただきありがとうございます。共に知識を探求し、共に成長していきましょう。