在线时间系统死锁

时间:2014-07-26 04:34:46

标签: sql sql-server stored-procedures database-deadlocks

我正在开发一款在线游戏的在线时刻系统。问题是有时SQL会抛出死锁错误:

40001 Transaction (Process ID 411) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

游戏有5个房间,每个房间都是一个有自己SQL连接的应用程序。玩家无法登录多个房间。每个应用程序每10分钟在在线播放器上循环,将命令发送到SQL过程以更新其在线时间......

EXEC StoredProcedure '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在线时间用作玩家花费的虚拟现金在网上小时游戏商店。

2 个答案:

答案 0 :(得分:0)

我建议使用WITH(ROWLOCK)选项:

UPDATE [dbo].[PlayerTable] WITH (ROWLOCK)
SET OnlineMinutes = OnlineMinutes + @Minutes 
WHERE PlayerName = @PlayerName

答案 1 :(得分:0)

我建议进行以下更改。

1)在您的调用应用程序中,您跟踪@PLayerName并将其传递给此过程。然后使用它来查找@PlayerLogin。您还应该在调用者中跟踪@PlayerName并将两个变量传递给此过程,从而避免在proc中查找@PlayerLogin

2)替换

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

通过这些更改,您可以消除这两个表上的SELECT然后UPDATE模式。这对于避免死锁是一件好事。

最后,您应该遵循重新运行事务的建议。将所有三个更新语句放在包含在事务中的循环中,出现错误,回滚并让它再次尝试事务,当然成功时,您只需提交并退出循环。您仍然需要进行前两次更改,因为避免死锁比死锁后重试要好得多。

你需要在重试次数上设置一个相当小的上限(我建议不超过5次)。