我们在 Stack Overflow SQL Server 2005 数据库中看到了一些有害但罕见的死锁情况。
我附加了分析器,使用这篇关于故障排除死锁的优秀文章设置了跟踪配置文件,并捕获了一堆示例。奇怪的是,死锁写总是一样的:
UPDATE [dbo].[Posts]
SET [AnswerCount] = @p1, [LastActivityDate] = @p2, [LastActivityUserId] = @p3
WHERE [Id] = @p0
另一个死锁语句各不相同,但通常是对帖子表的某种琐碎、简单的读取。这个总是在僵局中被杀死。这是一个例子
SELECT
[t0].[Id], [t0].[PostTypeId], [t0].[Score], [t0].[Views], [t0].[AnswerCount],
[t0].[AcceptedAnswerId], [t0].[IsLocked], [t0].[IsLockedEdit], [t0].[ParentId],
[t0].[CurrentRevisionId], [t0].[FirstRevisionId], [t0].[LockedReason],
[t0].[LastActivityDate], [t0].[LastActivityUserId]
FROM [dbo].[Posts] AS [t0]
WHERE [t0].[ParentId] = @p0
非常清楚,我们看到的不是写/写死锁,而是读/写。
目前,我们混合使用了 LINQ 和参数化 SQL 查询。我们已添加with (nolock)
到所有 SQL 查询中。这可能对一些人有所帮助。我们还有一个(非常)写得很糟糕的徽章查询,我昨天修复了它,每次运行需要超过 20 秒,并且每分钟都在运行。我希望这是一些锁定问题的根源!
不幸的是,大约 2 小时前我又遇到了一个死锁错误。完全相同的症状,完全相同的罪魁祸首。
真正奇怪的是,您在上面看到的锁定写入 SQL 语句是非常具体的代码路径的一部分。它仅在向问题添加新答案时执行 - 它使用新答案计数和最后日期/用户更新父问题。显然,相对于我们正在进行的大量读取,这并不常见!据我所知,我们并没有在应用程序的任何地方进行大量写入。
我意识到 NOLOCK 有点像一把巨大的锤子,但是我们在这里运行的大多数查询不需要那么准确。你会关心你的用户资料是否过时了几秒钟吗?
正如Scott Hanselman 在这里所讨论的,在Linq 中使用 NOLOCK 有点困难。
我们正在考虑使用
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
在基本数据库上下文上,以便我们所有的 LINQ 查询都有这个集合。否则,我们将不得不将我们进行的每个 LINQ 调用(嗯,简单的读取调用,其中绝大多数)封装在一个 3-4 行的事务代码块中,这很难看。
我想我有点沮丧,因为 SQL 2005 中的琐碎读取可能会导致写入死锁。我可以看到写/写死锁是一个大问题,但是读吗?我们不是在这里经营银行网站,我们不需要每次都完美准确。
想法?想法?
您是为每个操作实例化一个新的 LINQ to SQL DataContext 对象,还是为所有调用共享相同的静态上下文?
Jeremy,我们大部分时间都在基础控制器中共享一个静态数据上下文:
private DBContext _db;
/// <summary>
/// Gets the DataContext to be used by a Request's controllers.
/// </summary>
public DBContext DB
{
get
{
if (_db == null)
{
_db = new DBContext() { SessionName = GetType().Name };
//_db.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}
return _db;
}
}
您是否建议我们为每个控制器、每个页面或......更频繁地创建一个新的上下文?