1

行を挿入したいのですが、競合が発生した場合 (以下の例)、デバッグ目的でその内容をログに記録できるように、データベースで既存の行をロックしたいと考えています。READ_COMMITTEDトランザクション分離を使用しています。

例えば:

CREATE TABLE users(id BIGINT AUTO_INCREMENT, name VARCHAR(30),
  count INT NOT NULL, PRIMARY KEY(id), UNIQUE(name));

Thread 1:
  INSERT INTO users(username, count) VALUES('joe', 1000);
  transaction.commit();

Thread 2:
  // Insert fails due to conflict with above record
  INSERT INTO users(username, count) VALUES('joe', 0);

  // Get the conflicting row and log its properties
  SELECT * FROM users WHERE username = 'joe';

競合する行がロックされていない場合、確認するまでに変更されている可能性があります。SELECT id FROM users WHERE username = 'joe' FOR UPDATE私が見つけた唯一の回避策は、挿入の前に呼び出すことです。競合が発生しない場合、オーバーヘッドなしでこれを実装する方法はありますか?

更新:競合や結果を避けるように求めているわけSQLExceptionではありません。競合する行をロックするように求めているだけなので、競合を引き起こした値を調べることができます。はい、競合するレコードに含まれていることはわかっていjoeますが、他のすべての列をログに記録したいと考えています。

4

4 に答える 4

1

いいえ、一意の列を持つ行を UNIQUE使用する場合、列の競合を排除することはできません。INSERT

SQL 例外を処理する必要のない SQL を作成しようとすることは、ある条件下で失敗する SQL を常に作成してしまう無駄な努力です。

テーブルをロックし、更新を行い、テーブルのロックを解除する余裕がない限り、リアルタイム マルチスレッド マルチユーザー データベース サーバーを処理する場合、例外処理を回避することはできません (多くの負荷がかかるとひどいパフォーマンスが発生します)。ユーザー)

UNIQUE CONSTRAINT VIOLATION例外は常に 2nd で発生します。これは、例INSERTの 2 つINSERTの s が時間的に大きく離れている可能性があるためです (たとえば、時間、日、または週)。テーブルまたは行のロックはこれを変更しません。

この問題は、以前のユーザーがすでに選択している可能性のある「ユーザー名」を選択する場合、「申し訳ありませんが、そのユーザー名は既に使用されています。そのため、UNIQUE VIOLATION例外の処理を「回避」できる、または回避する必要があるとは考えにくいでしょう。

さらに、結果がorになるかどうかを確認SELECT ... FOR UPDATEするだけなので、 にする理由はありません。(id == null) => ユーザー名は使用されていませんが、それでも 2 人のユーザーが同時に「使用されていません」という結果を取得しようとして、いずれかが失敗する可能性があります。 SELECT id WHERE name = newNameidnullINSERT

UNIQUEduplicate で例外が返された場合INSERT、2 番目のレコードは失敗し、そのレコードは作成されていないため、failed で例外が返されたINSERT後にロックして読み取る「重複」レコードはありません。UNIQUEINSERT

于 2012-12-01T01:23:29.303 に答える
0

どのバージョンの SQL を使用していますか? あなたの質問を正しく理解しているかどうかはわかりませんが、トリガーでこれを行うことができると思います。

トリガーでは、挿入された値 (競合する行) を表示してログに記録し、ロールバックを行うことができます。これは、行を挿入するときに競合が発生しない場合は何もコミットする必要がなく、競合が発生した場合はログが作成され、行が挿入されないことを意味します。

于 2012-11-29T19:06:06.160 に答える
0

いいえ、ほとんどのデータベースはそのような操作をサポートしていません。

明示的なトランザクションを作成するなどのトリックを行うことができます

BEGIN TRANSACTION
IF EXIST(SELECT ...)
  ROLLBACK
INSERT INTO...
COMMIT

しかし、それはまさにあなたが望むものではありません。あなたが求めていることを達成する唯一の方法は、より低レベルの B-TREE スタイル ライブラリの 1 つを使用することです。

于 2012-12-01T12:26:21.127 に答える
0

これを実行するための移植可能な方法はないようで、MVCCを見ると、パフォーマンスに大きな影響を与えずにこれを実装することはできないという強い兆候があります。

結論として、競合が発生したことを知ることで解決する必要がありますが、原因を 100% 確実にする方法はありません (検証するスレッドセーフはありません)。

于 2012-12-06T21:41:24.113 に答える