マルチバージョン同時実行制御(MVCC)は、厳格なロックの代替として登場し、多数のトランザクションの並行動作を保証することを目的としています。これは、データへの同時アクセス時の競合やロックを減らすために重要で、特にOLTPシステムにおいて重要です。
従来のロックアプローチ(例えば、行レベルロック)は、高い競合時にアプリケーションの遅延を引き起こす可能性があります。MVCCの目的は、トランザクションが書き込み操作が並行して行われている場合でも、一貫したデータのスナップショットを読むことを可能にし、隔離性と同時アクセスを提供することです。
MVCCは、人気のあるDBMS(PostgreSQL、Oracle、MySQL/InnoDB)で、行のバージョン履歴を保存することによって実装されます。読み取り時、各トランザクションはその開始前に確定している行のみを表示し、挿入/更新は行をすぐに削除することなく新しいバージョンを作成します。
例クエリ(PostgreSQL):
BEGIN TRANSACTION; SELECT * FROM orders WHERE status = 'processing'; UPDATE orders SET status = 'completed' WHERE id = 42; COMMIT;
トランザクションが完了するまで、他のユーザーは行の以前のバージョンを見ることができ、コミット後にのみ新しいトランザクションが変更を利用できるようになります。
重要な特徴:
MVCCはすべての種類のロックや競合を完全に排除できますか?
いいえ、MVCCでも同じ行を同時に更新する際には競合が発生する可能性があります—例えば、同時にUPDATEが行われると、コミットの競合(書き込み-書き込み競合)が発生し、DBMSはエラーを投げるか、いずれかのトランザクションを巻き戻します。
MVCCの古い行のバージョンはいつ削除され、メモリリークを引き起こす可能性がありますか?
ほとんどのDBMSでは、古い行のバージョンは特別なプロセス(PostgreSQLのVACUUM)によって削除されます。これらのプロセスを実行しないと、データベースは「膨らみ」、パフォーマンスが低下します。
MVCC下での「SELECT FOR UPDATE」は正しく機能しますか?なぜロックが必要ですか?
はい、SELECT FOR UPDATEクエリは行をロックして並行変更による競合を排除します。そうでなければ「ロストアップデート」が発生する可能性があります。
例:
BEGIN; SELECT * FROM products WHERE id = 123 FOR UPDATE; UPDATE products SET quantity = quantity - 1 WHERE id = 123; COMMIT;
大手オンラインストアで、VACUUMの設定なしで頻繁にUPDATEされた注文のスキーマが実装されました。1ヶ月後、データベースは10倍に膨らみ、クエリが数倍遅くなりました。
長所:
短所:
定期的なautovacuumを行い、書き込み競合を制御し、重要なクエリについてのみREPEATABLE READレベルの隔離を使用しました。
長所:
短所: