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

我正在为一个在线游戏开发一个在线小时系统。问题是,有时SQL会抛出死锁错误:

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