选择..FOR UPDATE和冲突插入



我想插入一行,但如果发生冲突(下面的示例),我希望数据库锁定现有行,以便记录其内容以进行调试。我正在使用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,但我想记录它的所有其他列。

否不可能消除UNIQUE列的冲突当使用具有唯一列的行的CCD_ 6时。

尝试编写从不需要处理SQL异常的SQL只是浪费精力,最终总是创建失败的SQL在某些条件下。

实时处理时无法避免异常处理多线程多用户数据库服务器,除非可以锁定表、进行更新和解锁表(在许多用户的重负载)

CCD_ 7异常将总是发生在第二个CCD_,因为您的示例中的两个INSERT可能在时间上被广泛地分开(例如按小时、天或周);表或行锁定不会改变这一点。

这个问题无论如何都应该在GUI级别解决选择一个可能已经由以前的用户选择的"用户名"用户,要求向"新"用户提供反馈,如"对不起,该用户名已被其他用户使用",因此处理CCD_ 10异常的可能性不大可以或应该"避免"。

此外,没有理由使用SELECT ... FOR UPDATE,因为你所需要做的就是SELECT id WHERE name = newName,看看您得到一个结果idnull;(id==null)=>用户名未使用,但即使这样,两个用户也可以尝试同时获得"未使用"的结果并且CCD_ 15中的一个仍然可能失败。

当在重复的CCD_ 17上返回CCD_,第二CCD_ 18已经失败并且该记录没有被创建,因此没有"重复"记录需要锁定然后读取则在失败的CCD_ 20上返回CCD_。

Wich版本的SQL你在用吗?我不确定我是否正确理解你的问题,但我认为你可以在一个触发器中做到这一点。

在触发器中,您可以查看插入的值(冲突行)并记录它,然后进行回滚。Wich意味着,当您插入行时,当没有发生冲突时,您不必提交任何内容,当发生冲突时会生成日志,而不会插入行。

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

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

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

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

似乎没有一种可移植的方法来做到这一点,从MVCC来看,有一个强有力的迹象表明,如果不对性能产生实质性影响,就无法实现这一点。

因此,总之:你必须满足于知道发生了冲突,但无法100%确定原因(没有安全的线程可以验证)。

最新更新