DBテーブルまたは執筆用の範囲の範囲をロックする方法は?
-
08-10-2019 - |
質問
主キーを備えたシンプルなテーブルがあります。読み取り操作のほとんどは、キーの正確な値によって1つの行をフェッチします。
各行のデータは、キー順序で行の前後の行との関係を維持します。そのため、新しい行を挿入するときは、入力する2行を読み、計算を行い、挿入する必要があります。
懸念は、明らかに、同時に、別の接続が同じ間隔でキー値を持つ行を追加する可能性があることです。 2番目の挿入が失敗するのとまったく同じキーの値である場合、私はカバーされますが、キー値は異なるが同じ間隔で関係が破られる可能性があります。
解決策は、新しい行を追加することにしたときにテーブル全体をロックすることであるようです(可能であれば、疑わしい)キー値の間隔をロックすることです。しかし、私はその時点で読み取り専用のトランザクションがブロックされないことを望んでいます。
ODBCを使用しています c ++のlibodbc ++ラッパー クライアントプログラムとIBM DB2 Free Editionでは(DBの選択肢が引き続き変更される場合があります)。これは私がやろうと思っていたことです。
- 自動コミットとデフォルトの分離モードで接続を開始します
- 新しい行を追加する必要がある場合は、auto-commitをfalseおよび隔離モードにSERIALIZEDに設定します
- 新しいキー値の前後の行を読む
- 新しい行を計算して挿入します
- 専念
- 自動コミットとデフォルトの分離モードに戻ります
これは仕事をしますか?他のトランザクションは同時に読むことが許可されますか?それを行う他の/より良い方法はありますか?
ところで、読み取り専用のトランザクションを指定するためのLibodbc ++ I/FAの方法ではわかりません。 ODBCで可能ですか?
編集:非常に便利な答えをありがとう、私はそれを選択するのに苦労しました。
解決
データベースがシリアル化可能なモードの場合、問題はまったくありません。キーKが与えられた場合、以前のキーと次のキーを取得するには、次のクエリを実行する必要があります。
select key from keys where key > K order by key limit 1; # M?
select key from keys where key < K order by key desc limit 1; # I?
上記はMySQLで機能します。この同等のクエリはDB2で機能します(コメントから):
select key from keys where key = (select min(key) from keys where key > K);
select key from keys where key = (select max(key) from keys where key < K);
最初のクエリは、他のトランザクションがKよりも大きいキーをMよりも挿入するのを防ぐための範囲ロックをセットアップします。
2番目のクエリでは、他のトランザクションがk未満のキーを挿入することを防ぐレンジロックをセットアップします。
プライマリキーの一意のインデックスは、Kが2回挿入されるのを防ぎます。だからあなたは完全に覆われています。
これがトランザクションの目的です。そのため、データベース全体がロックされているかのようにコードを記述できます。
注:これには、真のシリアル化可能性をサポートするデータベースが必要です。幸いなことに、DB2はそうします。真のシリアル化可能性をサポートするその他のDBMS:SQLServer、およびMySQL/InnoDB。 dbms's that not:oracle、postgresql!
他のヒント
データベースとストレージエンジンがそれを許可する場合は、発行する必要があります SELECT FOR UPDATE
両方の行について、間に挿入しようとしています。
これは、あらゆる同時と競合します SELECT FOR UPDATE
.
欠点は、行のロックです 10
と 12
(挿入する 11
)また、選択を防ぎます 8
と 10
(挿入する 9
).
InnoDB
の MySQL
aを配置することもできます next-key
インデックスをロックします。つまり、インデックスレコードのロックと次のレコード間のギャップです。
この場合、あなたは次のように発行する必要があります SELECT FOR UPDATE
最初の行で、したがってその前に同時に行を挿入します。
ただし、これにはインデックスを強制し、 range
クエリに応じて可能である場合とできない場合があるインデックス上の条件。
あなたの一般的なアプローチは正しいです。ただし、2つの行とその間に可能なすべての行をカバーする選択されたステートメントを使用する必要があります。例えば:
SELECT * FROM MYTABLE WHERE PKCOL BETWEEN 6 AND 10
悲観的なロックおよびトランザクション分離レベルのシリアル化可能なデータベースシステムでは、この選択ステートメントは、選択の結果を変更する新しい行を挿入するのを防ぐ必要があります。