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因为您的示例中的两个INSERTs 可能在时间上相距很远(例如,按小时、天或周);表或行锁定不会改变这一点。

这个问题无论如何都应该在 GUI 级别解决,因为选择以前用户可能已经选择的“用户名”需要向“新”用户提供反馈,例如“对不起,该用户名已经在使用中由另一个用户”,因此看起来不太可能UNIQUE VIOLATION或应该“避免”处理异常。

此外,没有理由这样做SELECT ... FOR UPDATE,因为您需要做的就是SELECT id WHERE name = newName看看您是否得到结果idor null; (id == null) => 用户名未使用,但即便如此,两个用户也可以尝试同时获得“未使用”结果,其中一个INSERT仍然可能失败。

UNIQUE对 duplicate 返回异常时INSERT,第二个INSERT已经失败并且没有创建该记录,因此没有“重复”记录要锁定,然后UNIQUE在 failed 上返回异常后读取INSERT

于 2012-12-01T01:23:29.303 回答
0

您使用的是哪个版本的 SQL?我不确定我是否正确理解了您的问题,但我认为您可以在触发器中执行此操作。

在触发器中,您可以查看插入的值(您的冲突行)并记录它,并进行回滚。Wich 的意思是当你插入你的行时,当没有发生冲突时,你不必提交任何东西,而当发生冲突时,记录日志并且不插入行。

于 2012-11-29T19:06:06.160 回答
0

不,大多数数据库不支持这种操作。

你可以做一些技巧,比如创建一个显式事务

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

但这并不是你想要的。实现您所要求的唯一方法是使用 B-TREE 样式库之一,它的级别要低得多。

于 2012-12-01T12:26:21.127 回答
0

似乎没有一种可移植的方式来执行此操作,并且查看MVCC有一个强烈的迹象表明,如果没有实质性的性能影响,这将无法实现。

所以总而言之:您将不得不接受知道发生了冲突,但无法 100% 确定原因(没有线程安全可验证)。

于 2012-12-06T21:41:24.113 回答