我有一个 IIS 模块,可同时处理多个请求。在每个请求的入口处,我想记录上次访问该服务的时间:
update log set last_access = getdate()
但是,由于有时我会拥有的并发级别,我担心很容易发生冲突。
因为我的日志不需要毫秒的精度(它将被查阅以检查服务器在过去 X 分钟内是否被访问过,而不是毫秒),所以如果我的日志遇到冲突,它可以完全忽略该日志而不执行任何操作。
我最初的想法是在 try-catch 块内保护我的日志记录并忽略冲突。
begin try
update log set last_access = getdate()
end try
begin catch
-- We do nothing and ignore that another request has logged an access at the same time
end catch
但这是正确的吗?如果发生冲突,将自动跳转到 catch 部分,不执行任何操作,因此我的后端可以继续正常处理其余请求?
我担心它可能会等待死锁解除,然后在超时后继续。有没有更好的方法来更新值,同时告诉 SQL Server 如果另一个会话已经同时执行了该更新,则忽略该更新?
PS:此更新日志将在其自己的事务中运行,而服务请求所需的任何 SQL 操作都将在单独的事务中运行。
谢谢。
考虑到这两件事,我认为你想得太多了,其实没必要担心。
确实有可能出现并发或竞争条件的情况,这样较晚的访问可能会设法在较早的访问之前将其更新推送出去。但是,无论如何,我们在这里讨论的最多是几毫秒。由于该精度级别无关紧要,所以您不会有任何问题。
另一个问题是同时进行多个更新导致的锁定或阻塞。但同样,你似乎想得太多了。关系数据库通常擅长在数据库内处理这种情况。它们会适当地锁定以确保两个更新都发生,并且以原子和高性能的方式进行,其中较晚的更新获胜以设置给定行/列的最终值。这种事情是关系数据库最初被创建的重要原因之一。
可能会产生死锁,例如事务 A 正在等待事务 B 的锁,而事务 B 又在等待事务 A 的锁,因此它们永远被卡住了。更长、更复杂的链/环也是可能的。但这通常涉及锁定多个事物且顺序不同的事务。这里没有发生这种情况。
我可能会做的一件事是确保你有一个
WHERE
子句,即使表中只有一行。回顾自我开始撰写原始答案以来发表的评论,我发现毕竟有多行在起作用。这确实会造成(微小的)死锁可能性。有四件事可以帮到你。选择其中任何一件事,只要始终如一地做,就能保证你避免死锁。
getdate()
,