gpt4 book ai didi

sql-server-2008 - SELECT/UPDATE 上的死锁

转载 作者:行者123 更新时间:2023-12-04 12:38:37 24 4
gpt4 key购买 nike

我在 SQL Server 2008 上的 SELECT/UPDATE 上遇到死锁问题。
我从这个线程中阅读了答案:SQL Server deadlocks between select/update or multiple selects但我仍然不明白为什么我会陷入僵局。

我在以下测试用例中重现了这种情况。

我有一张 table :

CREATE TABLE [dbo].[SessionTest](
[SessionId] UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL,
[ExpirationTime] DATETIME NOT NULL,
CONSTRAINT [PK_SessionTest] PRIMARY KEY CLUSTERED (
[SessionId] ASC
) WITH (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[SessionTest]
ADD CONSTRAINT [DF_SessionTest_SessionId]
DEFAULT (NEWID()) FOR [SessionId]
GO

我首先尝试从该表中选择一条记录,如果该记录存​​在,则将过期时间设置为当前时间加上一些时间间隔。它是使用以下代码完成的:

protected Guid? GetSessionById(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
Logger.LogInfo("Getting session by id");
using (SqlCommand command = new SqlCommand())
{
command.CommandText = "SELECT * FROM SessionTest WHERE SessionId = @SessionId";
command.Connection = connection;
command.Transaction = transaction;
command.Parameters.Add(new SqlParameter("@SessionId", sessionId));

using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.Read())
{
Logger.LogInfo("Got it");
return (Guid)reader["SessionId"];
}
else
{
return null;
}
}
}
}

protected int UpdateSession(Guid sessionId, SqlConnection connection, SqlTransaction transaction)
{
Logger.LogInfo("Updating session");
using (SqlCommand command = new SqlCommand())
{
command.CommandText = "UPDATE SessionTest SET ExpirationTime = @ExpirationTime WHERE SessionId = @SessionId";
command.Connection = connection;
command.Transaction = transaction;
command.Parameters.Add(new SqlParameter("@ExpirationTime", DateTime.Now.AddMinutes(20)));
command.Parameters.Add(new SqlParameter("@SessionId", sessionId));
int result = command.ExecuteNonQuery();
Logger.LogInfo("Updated");
return result;
}
}

public void UpdateSessionTest(Guid sessionId)
{
using (SqlConnection connection = GetConnection())
{
using (SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.Serializable))
{
if (GetSessionById(sessionId, connection, transaction) != null)
{
Thread.Sleep(1000);
UpdateSession(sessionId, connection, transaction);
}
transaction.Commit();
}
}
}

然后,如果我尝试从两个线程执行测试方法并且他们尝试更新相同的记录,我会得到以下输出:

[4] : Creating/updating session
[3] : Creating/updating session
[3] : Getting session by id
[3] : Got it
[4] : Getting session by id
[4] : Got it
[3] : Updating session
[4] : Updating session
[3] : Updated
[4] : Exception: Transaction (Process ID 59) was deadlocked
on lock resources with another process and has been
chosen as the deadlock victim. Rerun the transaction.

我无法理解它是如何使用可序列化隔离级别发生的。我认为第一个选择应该锁定行/表并且不会让另一个选择获得任何锁定。该示例是使用命令对象编写的,但仅用于测试目的。最初,我使用的是 linq,但我想展示一个简化的例子。 Sql Server Profiler 显示死锁是键锁。我将在几分钟内更新问题并从 sql server profiler 发布图表。任何帮助,将不胜感激。我知道这个问题的解决方案可能是在代码中创建临界区,但我试图理解为什么 Serializable Isolation Level 不起作用。

这是死锁图:
deadlock http://img7.imageshack.us/img7/9970/deadlock.gif

提前致谢。

最佳答案

有一个可序列化的事务是不够的,你需要提示锁定才能工作。

可序列化的隔离级别通常仍会获取它所能获得的“最弱”类型的锁,以确保满足可序列化的条件(可重复读取、无幻影行等)

因此,您正在获取表上的共享锁,稍后(在您的可序列化事务中)尝试升级到 an update lock.如果另一个线程持有共享锁,升级将失败(如果没有其他主体持有共享锁,它将工作)。

您可能希望将其更改为以下内容:

SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId

这将确保在执行 SELECT 时获取更新锁(因此您不需要升级锁)。

关于sql-server-2008 - SELECT/UPDATE 上的死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/959501/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com