防止并行事务更新行

时间:2013-11-17 17:28:04

标签: sql database database-design transactions

我正在为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的用户的行。

2 个答案:

答案 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所提议的那样。