表Auction
:AuctionType (Integer), Auctioned (Json/Text)
。
我需要一种方法来更新拍卖。通过多线程拍卖。
示例:
Machine1:
int id = 1;
Object obj = parse("SELECT Auctioned FROM Auction WHERE AuctionId = "+id+";");
obj = edit(obj);
update("UPDATE Auction SET Auctioned = "+obj.toString()+" WHERE AuctionId="+id+";");
Machine2:
update("UPDATE Auction SET Auctioned = <value> WHERE AuctionID=<value2>");
问题在于,如果: Thread1获取Sql列。 Thread2更新Sql Column。 Thread1使用过期的获取结果来更新Sql Column,结果是Thread2更新被覆盖。
所以我想找到一种防止数据丢失的安全方法,例如:
Thread1 fetchs Sql Column & stores checksum.
Thread2 updates Sql Column.
Thread1 updates Sql Column if checksum equals storedChecksum.
我希望校验和检查在sql引擎端而不是在进程中完成。
但是,为此,我需要一种方法来允许SQL更新查询在数据库引擎中自行取消,并在不满足校验和条件的情况下返回结果。
答案 0 :(得分:3)
有一个通用的RDBMS机制可以满足此要求,称为 SELECT ... FOR UPDATE
。
原理是,在选择该行时,您指示您的RDBMS即将更新,并且应锁定。如果在释放锁之前另一个SQL会话尝试访问(读取,更新)数据,则将其置于等待状态。
大多数RDBMS实现此功能。通常的约束是您需要使用数据库事务才能正常工作(即,禁用autocommit
)。提交(或回滚)拥有的事务时,将释放该锁定。
使用MySQL InnoDB:
SELECT ... FOR UPDATE
:对于索引记录,遇到搜索,锁定行和任何关联的索引条目,就如同您为这些行发出UPDATE语句一样。禁止其他事务更新这些行,执行SELECT ... FOR SHARE或读取某些事务隔离级别的数据。
SELECT Auctioned FROM Auction WHERE AuctionId = ? FOR UPDATE;
在Oracle中:FOR UPDATE
clause。
通过
FOR UPDATE
子句,您可以锁定选定的行,以便其他用户在结束事务之前不能锁定或更新行。
SELECT Auctioned FROM Auction WHERE AuctionId = ? FOR UPDATE OF Auctioned;
SQL Server有点不同,您需要使用the UPDLOCK
hint:
指定要获取并保持更新锁,直到事务完成。
SELECT Auctioned FROM Auction WITH (UPDLOCK) WHERE AuctionId = ?;
Postgres:explicit row-level locking
FOR UPDATE导致SELECT语句检索的行被锁定,就像更新一样。 [...]也就是说,尝试对这些行进行UPDATE,DELETE,SELECT FOR UPDATE [...]的其他事务将被阻止,直到当前事务结束为止。
SELECT Auctioned FROM Auction WHERE AuctionId = ? FOR UPDATE;
答案 1 :(得分:1)
我希望校验和检查在sql引擎端而不是在进程中完成。
您可以使用悲观锁定,例如,使用Select ... For Update
。 @GMB早已对此进行了解释。
但是我不想使用它的内置校验和,因为将来可能需要更改数据库引擎
您可以使用开放式锁定,但是这需要您向架构添加一个字段。
我通过添加名为version
的新整数字段来使用这种方式。
create temp table temp_x(name varchar, id int, version int);
insert into temp_x values('foo', 1, 1);
update temp_x set name = 'bar', version = version + 1 where id = 1 and version = 1;
update temp_x set name = 'foobar', version = version + 1 where id = 1 and version = 1;
第二次更新将失败,因为版本不匹配。
在您情况下,可能会更改为
update("UPDATE Auction SET Auctioned = <value>, version=version+1 WHERE AuctionID=<value2> AND version=<version_value>");