我正在为一个在线游戏开发一个在线小时系统。问题是,有时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中,它用作玩家在在线游戏商店中花费的虚拟现金。
发布于 2014-07-26 05:17:14
我建议作以下修改。
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次)。
发布于 2014-07-26 04:37:30
我建议使用WITH(ROWLOCK)选项:
UPDATE [dbo].[PlayerTable] WITH (ROWLOCK)
SET OnlineMinutes = OnlineMinutes + @Minutes
WHERE PlayerName = @PlayerNamehttps://stackoverflow.com/questions/24967556
复制相似问题