Sql 在线小时制中的死锁
我正在为一个在线游戏开发一个在线小时系统。问题是,有时SQL会抛出死锁错误:Sql 在线小时制中的死锁,sql,sql-server,stored-procedures,database-deadlocks,Sql,Sql Server,Stored Procedures,Database Deadlocks,我正在为一个在线游戏开发一个在线小时系统。问题是,有时SQL会抛出死锁错误: 40001事务(进程ID 411)在与另一个进程的锁通信缓冲区资源上被死锁,并被选为死锁受害者。重新运行事务。 这个游戏有5个房间,每个房间都是一个有自己SQL连接的应用程序。玩家不能登录多个房间。每个应用程序每10分钟在在线播放器上循环一次,将命令发送到SQL过程以更新其在线分钟数 EXEC StoredProcess'PlayerName',7 遵循存储过程代码: USE [DataBase] GO SET AN
40001事务(进程ID 411)在与另一个进程的锁通信缓冲区资源上被死锁,并被选为死锁受害者。重新运行事务。
这个游戏有5个房间,每个房间都是一个有自己SQL连接的应用程序。玩家不能登录多个房间。每个应用程序每10分钟在在线播放器上循环一次,将命令发送到SQL过程以更新其在线分钟数
EXEC StoredProcess'PlayerName',7
遵循存储过程代码:
USE [DataBase]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[StoredProcedure]
@PlayerName varchar(10),
@Minutes int
AS
BEGIN
DECLARE @PlayerLogin varchar(10),
@OnlineMinutes int
SELECT @PlayerLogin = PlayerLogin FROM [dbo].[PlayerTable] WHERE PlayerName = @PlayerName
UPDATE [dbo].[PlayerTable] SET OnlineMinutes = OnlineMinutes + @Minutes WHERE PlayerName = @PlayerName
UPDATE [dbo].[AccountTable] SET OnlineMinutes = OnlineMinutes + @Minutes WHERE PlayerLogin = @PlayerLogin
SELECT @OnlineMinutes = OnlineMinutes FROM [dbo].[AccountTable] WHERE PlayerLogin = @PlayerLogin
IF ( @OnlineMinutes >= 60 )
BEGIN
UPDATE [dbo].[AccountTable] SET OnlineHours = OnlineHours + ( @OnlineMinutes / 60 ), OnlineMinutes = ( @OnlineMinutes % 60 ) WHERE PlayerLogin = @PlayerLogin
END
END
在这两个表中存储在线分钟数的原因是
PlayerTable
用于排名页面,在AccountTable
中,在线小时数用作玩家在在线小时数游戏商店中花费的虚拟现金。我建议使用和(ROWLOCK)
选项:
UPDATE [dbo].[PlayerTable] WITH (ROWLOCK)
SET OnlineMinutes = OnlineMinutes + @Minutes
WHERE PlayerName = @PlayerName
我建议进行以下修改 1) 在调用应用程序中,跟踪@PLayerName并将其传递给此过程。然后,您可以使用它来查找@PlayerLogin。您还应该在调用者中跟踪@PlayerName,并将这两个变量传递到此过程,避免在过程中查找@PlayerLogin 2) 替换 与 通过这些更改,可以消除这两个表上的先选择后更新模式。这对于避免死锁是一件好事 最后,您应该遵循重新运行事务的建议。将所有三条update语句放在事务中包装的循环中,出错时回滚,让它再次尝试事务,当然,如果成功,只需提交并退出循环即可。您仍然需要进行前2项更改,因为避免死锁比死锁后重试要好得多
您需要对重试次数设置一个合理的小上限(我建议不超过5次)。谢谢您的回答。你能解释一下为什么吗?我只需要在
播放表中使用
?当您不使用此选项时,SQL Server可能会将单页、额外页(8页)或表孔以及其他进程锁定到此更新。对,因此,对于更新用户数据的多SQL连接应用程序,始终建议使用此选项?否。当您确保更新命令仅更新单行或表时,建议使用此选项。好的,谢谢。在我看来,死锁只有在跨资源锁发生时才会发生。那么这个选项可以解决我的问题吗?非常感谢。您能解释一下如何在事务中执行此循环吗?您在循环中执行事务,而不是事务中的外观。许多地方展示了如何进行事务处理,下面是一个很好的S/O示例。
SELECT @OnlineMinutes = OnlineMinutes FROM [dbo].[AccountTable] WHERE PlayerLogin = @PlayerLogin
IF ( @OnlineMinutes >= 60 )
BEGIN
UPDATE [dbo].[AccountTable] SET OnlineHours = OnlineHours + ( @OnlineMinutes / 60 ),
OnlineMinutes = ( @OnlineMinutes % 60 ) WHERE PlayerLogin = @PlayerLogin
END
UPDATE [dbo].[AccountTable] SET
OnlineHours = OnlineHours + ( @OnlineMinutes / 60 )
, OnlineMinutes = ( @OnlineMinutes % 60 )
WHERE PlayerLogin = @PlayerLogin AND OnlineMinutes >= 60