Sql 服务器因插入、select、删除而死锁
Sql server deadlock with insert, select, delete
申请详情
C# web api(隐式并发)
用于 sql 执行的小巧玲珑
要求
当一个 "lounge" 中添加了四个用户时,这意味着游戏已准备好开始。第四个添加的用户负责启动游戏。游戏开始后,必须将用户从 "lounge" 中移除。
Table
UserInLounge (LoungeId, UserId)
注意,比lounge多了一个,
代码
var tran = connection.BeginTransaction();
connection.Execute("insert into UserInLounge(LoungeId, UserId, TimeEntered) values (@loungeId, @userId, @timeEntered)", new { loungeId, userId = user.Id, timeEntered = DateTime.UtcNow }, tran);
var userIds = connection.Query<string>("select UserId from UserInLounge where LoungeId = @loungeId", new { loungeId }, tran).ToList();
if (userIds.Count == 4)
{
connection.Execute("delete from UserInLounge where LoungeId = @loungeId and UserId IN @userIds", new { loungeId, userIds }, tran);
}
tran.Commit();
问题
运行超过一个用户(我用4个测试)会导致事务死锁。我使用一个事务,因为我需要在插入后确切地知道现在是否有四个人,如果是,则将他们删除。
第二次尝试
var tran = connection.BeginTransaction();
var userIds = connection.Query<string>("select UserId from UserInLounge where LoungeId = @loungeId", new { loungeId }, tran).ToList();
if (userIds.Count == 3)
{
connection.Execute("delete from UserInLounge where LoungeId = @loungeId and UserId IN @userIds", new { loungeId, userIds }, tran);
tran.Commit();
}
else
{
connection.Execute("insert into UserInLounge(LoungeId, UserId, TimeEntered) values (@loungeId, @userId, @timeEntered)", new { loungeId, userId = user.Id, timeEntered = DateTime.UtcNow }, tran);
tran.Commit();
Log.InfoFormat("{0} added to Lounge: {1}; Now {2} users", user.UserName, ret.Name, userIds.Count);
}
第二次尝试时,if 语句永远不会为真。这是日志输出 -
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user3@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user2@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user1@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user4@fivehundred.com added to Lounge: Main; Now 0 users
如果我按照建议添加 HOLDLOCK,我会遇到死锁
"select UserId from UserInLounge with (holdlock) where LoungeId = @loungeId"
我从这里得到了解决方案 - http://michaeljswart.com/2011/09/mythbusting-concurrent-updateinsert-solutions/
与上面基本相同,但加上 -
var tran = connection.BeginTransaction(IsolationLevel.Serializable);
var userIds = connection.Query<string>("select UserId from UserInLounge with (updlock) where LoungeId = @loungeId", new { loungeId }, tran).ToList();
申请详情
C# web api(隐式并发)
用于 sql 执行的小巧玲珑
要求
当一个 "lounge" 中添加了四个用户时,这意味着游戏已准备好开始。第四个添加的用户负责启动游戏。游戏开始后,必须将用户从 "lounge" 中移除。
Table
UserInLounge (LoungeId, UserId) 注意,比lounge多了一个,
代码
var tran = connection.BeginTransaction();
connection.Execute("insert into UserInLounge(LoungeId, UserId, TimeEntered) values (@loungeId, @userId, @timeEntered)", new { loungeId, userId = user.Id, timeEntered = DateTime.UtcNow }, tran);
var userIds = connection.Query<string>("select UserId from UserInLounge where LoungeId = @loungeId", new { loungeId }, tran).ToList();
if (userIds.Count == 4)
{
connection.Execute("delete from UserInLounge where LoungeId = @loungeId and UserId IN @userIds", new { loungeId, userIds }, tran);
}
tran.Commit();
问题
运行超过一个用户(我用4个测试)会导致事务死锁。我使用一个事务,因为我需要在插入后确切地知道现在是否有四个人,如果是,则将他们删除。
第二次尝试
var tran = connection.BeginTransaction();
var userIds = connection.Query<string>("select UserId from UserInLounge where LoungeId = @loungeId", new { loungeId }, tran).ToList();
if (userIds.Count == 3)
{
connection.Execute("delete from UserInLounge where LoungeId = @loungeId and UserId IN @userIds", new { loungeId, userIds }, tran);
tran.Commit();
}
else
{
connection.Execute("insert into UserInLounge(LoungeId, UserId, TimeEntered) values (@loungeId, @userId, @timeEntered)", new { loungeId, userId = user.Id, timeEntered = DateTime.UtcNow }, tran);
tran.Commit();
Log.InfoFormat("{0} added to Lounge: {1}; Now {2} users", user.UserName, ret.Name, userIds.Count);
}
第二次尝试时,if 语句永远不会为真。这是日志输出 -
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user3@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user2@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user1@fivehundred.com added to Lounge: Main; Now 0 users
|INFO |FiveHundred.Web.Stores.DapperLoungeStore - user4@fivehundred.com added to Lounge: Main; Now 0 users
如果我按照建议添加 HOLDLOCK,我会遇到死锁
"select UserId from UserInLounge with (holdlock) where LoungeId = @loungeId"
我从这里得到了解决方案 - http://michaeljswart.com/2011/09/mythbusting-concurrent-updateinsert-solutions/
与上面基本相同,但加上 -
var tran = connection.BeginTransaction(IsolationLevel.Serializable);
var userIds = connection.Query<string>("select UserId from UserInLounge with (updlock) where LoungeId = @loungeId", new { loungeId }, tran).ToList();