如何在事务处理过程中锁定数据



我开始使用SQL Server数据库,很难理解Transaction Isolation Levels以及它们如何锁定数据。

我正在努力完成以下简单的任务:

  • 在SQL存储过程中接受一对整数[ID,counter]
  • 确定某个表中是否存在ID:SELCT COUNT(*) FROM MyTable WHERE Id = {idParam}
  • 如果上一条COUNT语句返回0,请插入此ID和计数器:INSERT INTO MyTable(Id, Counter) VALUES({idParam}, {counterParam})
  • 如果COUNT语句返回1,则更新现有记录:UPDATE MyTable SET Counter = Counter + {counterParam} WHERE Id = {idParam}

现在,我知道我必须将整个存储过程封装在事务中,根据这篇MS文章,适当的隔离级别将是SERIALIZABLE(它说:在当前事务完成之前,任何其他事务都不能修改当前事务读取的数据)。如果我错了,请纠正我。

假设我调用了ID为1的过程,那么第一个查询应该是SELCT COUNT(*) FROM MyTable WHERE SomeId=1(第一个事务开始)。然后,在执行该查询之后,立即调用ID为2的过程(第二个事务开始)。

我不明白的是,在这种情况下,在执行存储过程时会锁定多少数据:

  • 如果第一个事务的第一个查询返回0条记录,这是否意味着第一个事务什么都不锁定,并且其他事务能够在第一个事务尝试之前插入ID=1
  • 还是第一个事务锁定了整个表,使第二个事务等待,即使这两个事务永远无法读取/更新同一行
  • 或者第一个事务是否以某种方式禁止其他人只读/写ID为1的记录,直到它被删除

如果你的过滤器在索引上,那么它就会被锁定。因此,无论该行是否已经存在,它都会在事务期间被锁定。不过要小心——很容易把行锁变成更糟糕的东西,尤其是满表锁。当然,通过这种方式引入死锁很容易:)

然而,我建议采取不同的方法。首先,尝试插入。如果它有效,你就完了——如果它不起作用,你知道你可以安全地进行原子更新。非常快,非常便宜,非常可靠:)

最新更新