我正在为SQL数据库构建一组简单的查询。我遇到了一个我想要防范的情况,但我不知道数据库理论术语来解释我所要求的内容。
在此示例中,我在数据库上发生了两个同时发生的事务。交易#1开始,交易#2在T1之后开始,但T2在T1提交之前结束。
表USERS有列id,name,passwordHash
--Transaction #1
BEGIN TRANSACTION;
SELECT id from USERS where name = someName;
--do some work, transaction #2 starts and completes quickly while this work is being performed
UPDATE USERS SET name = newName where id = $id;
COMMIT;
--Transaction #2
BEGIN TRANSACTION;
SELECT id from USERS where name = someName;
UPDATE USERS SET passwordHash = newPasswordHash where id = $id;
COMMIT;
我想进行某种安全检查,如果我要更新一行,我只更新交易开始时存在的那一行的相同版本。
在这种情况下,我希望事务1 COMMIT
失败,因为事务2已经更新了属于名为someName
的用户的行。
答案 0 :(得分:1)
执行此操作的标准方法是向表中添加rowversion
列。您将此列与其他数据一起阅读。提交更新时,请将其包含在where子句中。然后,您可以检查受影响的行数,以查看是否先进入另一个事务。
某些数据库对此类列具有本机支持。例如。 SQL Server具有timestamp/rowversion
数据类型。 Oracle有rowdependencies
。 DB2有rowversion
。
答案 1 :(得分:1)
您可以使用SELECT FOR UPDATE with NOWAIT来锁定行以防止其他事务的并发修改。这将保证您以后的更新将针对这些行的相同版本运行;在您的事务提交之前,其他事务无法更改这些行。
示例(使用Postgresql):
交易1:
begin transaction;
select * from users where username = 'Fabian' for update nowait;
update users set passwordHash = '123' where username = 'Fabian';
commit;
事务2,在事务1选择更新但未提交之后的某处:
> select * from users where username = 'Fabian' for update nowait;
ERROR: could not obtain lock on row in relation "users"
修改强>
这通常称为悲观锁定。首先选择该行的事务将“获胜”,任何后续选择更新的事务都将失败。如果您希望事务获胜,首先写入更改,您可能希望采用乐观锁定方法,如@Laurence所提议的那样。