SQLにおける行のロッキング: SELECT FOR UPDATEの内部
Apr 07, 2025
# SQL
James Reed
Infrastructure Engineer · Leapcell

SELECT FOR UPDATE
は、SQLで行レベルのロック機構であり、トランザクションで取得された行をロックするために使用されます。
その目的は、他のトランザクションがこれらの行を変更したり、ロックを取得したりするのを防ぐことであり、データの整合性を確保する必要があるシナリオで一般的に使用されます。
MySQLでは、SELECT FOR UPDATE
の具体的な実装は、ストレージエンジン(InnoDBなど)と密接に関連しています。その核心では、行ロックを使用してターゲット行をロックします。
コア機能
ロックの種類:
SELECT FOR UPDATE
は、クエリによって取得された行に 排他ロック (X locks) をかけます。- 他のトランザクションは、これらの行を変更したり、共有ロックまたは排他ロックをかけたりすることはできません。
使用範囲:
- トランザクション内 (つまり、
BEGIN
とCOMMIT
の間) で使用する必要があります。 - トランザクションをサポートする ストレージエンジン (InnoDBなど) を使用する場合にのみ有効です。
動作:
- ターゲット行が別のトランザクションによって既にロックされている場合、現在のトランザクションは、ロックが解除されるか、タイムアウトが発生するまで 待機状態 になります。
実装原理
InnoDB行ロック機構
- InnoDBストレージエンジンは、インデックスを介して 行レベルのロックを実装します。
SELECT FOR UPDATE
は、クエリ条件を満たす すべての行 をロックします。クエリが インデックスを使用しない 場合、テーブルロック にダウングレードし、テーブル全体をロックします。
ロックプロセス
- クエリの実行中、InnoDBはスキャンされた各行に排他ロックをかけようとします。
- 行が別のトランザクションによって既にロックされている場合、現在のトランザクションはそのロックが解除されるまで待機します。
ロックの種類
- 行ロック: 取得された行のみをロックするインデックスベースのロック。
- ギャップロック:
REPEATABLE READ
分離レベルでは、範囲クエリが行にヒットしない場合、新しい行が挿入されないように、範囲間のギャップにロックがかけられます。
トランザクション分離レベルの効果
- READ COMMITTED: 各クエリは最新のデータを読み取り、現在のクエリによって返された行のみをロックします。
- REPEATABLE READ: クエリ結果は トランザクションスナップショット に基づいており、ロックされた範囲には一致しない行が含まれる場合があります ( ギャップロック が原因)。
- SERIALIZABLE: クエリ範囲内の すべてのデータ をロックします。
実行プロセス
以下は、InnoDBを使用したSELECT FOR UPDATE
の実行プロセスを示しています。
- トランザクションの開始:
BEGIN
を使用してトランザクションを開始します。 - クエリとロック:
SELECT ... FOR UPDATE
を実行して、条件を満たすレコードに排他ロック (X lock) を適用します。 - データ操作: ロックされたデータを変更または読み取ります。
- ロックの解除: トランザクションがコミットされると (
COMMIT
)、ロックが解除されます。トランザクションがロールバックされると (ROLLBACK
)、ロックも解除されます。
例
基本的な使用法
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; /* order_id = 1 の行をロックします */ UPDATE orders SET status = 'processed' WHERE order_id = 1; COMMIT;
複数トランザクション競合のシナリオ
トランザクション A:
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; -- order_id = 1 の行をロックします
トランザクション B (トランザクション A がコミットする前):
BEGIN; SELECT * FROM orders WHERE order_id = 1 FOR UPDATE; -- トランザクション A がロックを解除するのを待ちます
トランザクション A がコミットまたはロールバックするまで、トランザクション B はロックを取得できません。
ギャップロックの動作
REPEATABLE READ
分離レベルでは、範囲クエリによってギャップロックがトリガーされる場合があります。例:
SELECT * FROM orders WHERE order_id BETWEEN 10 AND 20 FOR UPDATE;
クエリが結果を返さない場合でも、InnoDB は 10 から 20 の範囲に ギャップロック を配置し、他のトランザクションがこの範囲内に新しいレコードを挿入するのを防ぎます。
最適化と考慮事項
インデックスの使用:
- インデックスを使用する クエリは 行レベルのロック を適用し、テーブルレベルのロックへのダウングレードを回避します。
- インデックスが使用されない 場合、テーブル全体がロックされ、同時実行のパフォーマンスに悪影響を与える可能性があります。
長いトランザクションの回避:
- ロックを保持しすぎると、他のトランザクションがブロックされる可能性があります。トランザクションの期間を最小限に抑える ことをお勧めします。
デッドロックの検出:
- InnoDB は自動的にデッドロックを検出し、トランザクションの 1 つをロールバックします。デッドロックを回避するために、トランザクションロジックを慎重に設計する ことをお勧めします。
ビジネス要件に応じた使用:
- データ変更の競合を防ぐために必要な場合にのみ
SELECT FOR UPDATE
を使用し、不必要なロック競合を回避します。
まとめ
SELECT FOR UPDATE
は、MySQL のロック操作であり、行ロック機構を使用して 同時変更の競合を防ぎます。- その実装は、トランザクション、分離レベル、および インデックスの最適化 に依存します。
- 適切に使用すると、
SELECT FOR UPDATE
は データの一貫性を効果的に保護できます が、パフォーマンスのオーバーヘッドに注意し、ロック競合とデッドロックを回避する必要があります。
Leapcell へようこそ。バックエンドプロジェクトをホストするための最適な選択肢です。
Leapcell は、Webホスティング、非同期タスク、Redisのための次世代サーバーレスプラットフォームです。
多言語サポート
- Node.js、Python、Go、または Rust で開発します。
無制限のプロジェクトを無料でデプロイ
- 使用量に応じてのみ料金が発生——リクエストや料金は発生しません。
比類なき費用効率
- アイドル料金なしの従量課金制。
- 例: 25ドルで、平均応答時間60msで694万リクエストをサポートします。
合理化された開発者体験
- 容易なセットアップのための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察のためのリアルタイムメトリクスとログ。
容易なスケーラビリティと高性能
- 容易に高い同時実行性を処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロ——構築に集中するだけです。
ドキュメンテーションで詳細をご覧ください!
Xでフォローしてください: @LeapcellHQ