诊断的僵局SQL服务器2005年
-
09-06-2019 - |
题
我们看到一些有害的,但是罕见的,僵局,条件中的叠溢出SQL服务器的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
被完全清楚,我们没有看到写/编写僵局,但读/写。
我们有一个混合的皇宫和参数化SQL查询的时刻。我们加入 with (nolock)
所有SQL查询。这可能有助于一些。我们还有一个单一的(非常)不编写的徽章的查询,我昨天固定,这是采取以上20秒运行每一个时间和运行的每一分钟。我希望这是来源的一些锁定问题!
不幸的是,我有另外一个僵局的错误大约2个小时前。同样确切的症状,同样确切的罪魁祸首写。
真正奇怪的是,锁定编写SQL statement你看上是的一部分,一个非常特定代码的道路。它的 只 执行时,一个新的回答是加入一个问题--它更新的父母的问题与新的回答数和最后日期/用户。这是,很显然,没有共同的相对数量庞大的读取我们正在做的!我可以告诉,我们不能做巨大的数字写在任何地方的应用程序。
我认识到,NOLOCK是一个巨大的锤子,但大多数查询我们在这里不需要,准确。将你关心如果你的用户配置文件是几秒钟的约会?
使用NOLOCK与皇宫是一个比较困难因为 斯科特Hanselman讨论了这里.
我们调情的想法的使用
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
在基地的数据库方面使我们所有的皇宫查询。如果做不到这一点,我们有包装的每一个皇宫的呼叫我们使(很好,简单的阅读的人,这是他们中的绝大多数)在3-4行交易代码块,这是丑陋的。
我猜我有点沮丧的是,微不足道的读SQL2005可僵局中上写道。我可以看到的写/编写的僵局正在一个巨大的问题,但是 读? 我们不是在运行一个银行的网站在这里,我们不需要完美的精确性。
想法?想法?
是你的实例,一个新的皇宫SQL属性对象的每一动作或者你也许共享相同的静态背景为所有您的电话吗?
杰里米,我们是共享一个静态属性的基础控制器的大部分:
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;
}
}
你建议我们创建了一个新的背景下每一个控制器,或每页,或者..多频繁?
解决方案
根据MSDN:
http://msdn.microsoft.com/en-us/library/ms191242.aspx
当任何 读致力或快照 允许快照隔离的数据库 选项,合乎逻辑的副本 (版本)保持对所有数据 修改执行 数据库。每次修改行 通过一特定交易, 实例的数据库引擎商店 一个版本的先前承诺 图像的行在临.每 版本标记的交易 序列号码的交易 作出的改变。该版本的 修改过的行链接使用的链接 名单。最新的行值总是 存储在当前数据库和 链接到版本控制行储存 在临.
对于短期运行的交易, 版本的修改行可以获得 缓存缓冲区没有游泳池 得到写入磁盘文件 该临的数据库。如果需要 该版本行是短暂的, 简单地将获得所下降 缓冲池并不一定可以 承担I/O开销。
似乎有一种轻微的性能损失的额外开销,但它可能可以忽略不计。我们应该试验,以确保。
试试这种选项的设置和删除所有NOLOCKs从码查询的,除非它是真正有必要的。NOLOCKs或使用全球方法的数据库上下文处理程序来打击数据库事务隔离水平带艾滋病问题。NOLOCKS会掩盖基本的问题与我们的数据层,并可能导致选择不可靠的数据,其中自动选择/更新行版本控制出现的解决方案。
ALTER Database [StackOverflow.Beta] SET READ_COMMITTED_SNAPSHOT ON
其他提示
NOLOCK 和 未提交读 是一个滑坡。你永远不应该使用它们,除非你明白为什么僵局是发生第一次。它会担心我,你说,"我们已经加入(nolock)所有SQL查询".需要增加 与NOLOCK 到处是一个明确的信号,你有问题你的数据层。
更新声明本身看起来有点问题。你确定最早的事务,或者只是把它从一个对象? AnswerCount = AnswerCount+1
当一个问题是增加可能是一个更好的方式来处理这个问题。然后你不需要一个交易得到正确数和你不需要担心的并发问题的潜在暴露自己.
一个简单的方法来解决这类僵局的问题没有很多的工作,没有使脏读是使用 "Snapshot Isolation Mode"
(new SQL2005年),它总是会给你一个干净的读的最后一个未经修改的数据。你也可以抓住并重新陷入僵局的声明相当容易,如果你要处理它们的优雅。
运问题要问,为什么这个问题的发生。此后希望回答,虽然离去可能的解决方案可由其他人。
这可能是一种索引有关的问题。例如,可以说表员额有一个非集群索引X其中包含ParentID和一个(或多个)的领域(s)正在更新(AnswerCount,LastActivityDate,LastActivityUserId).
一僵局将会发生,如果选择cmd并共享读锁上索引X搜索由ParentId然后需要做的一个共同阅读锁在集群指数,以获得剩余柱,同时更新记录并写专锁上聚集指数和需要得到一个写专锁上索引X到更新。
你现在有的情况那里一个上锁的X,并试图得到Y而B锁Y并试图获得X.
当然,我们会需要运来更新他的发布更多的信息,关于什么的指数都在发挥确认,如果这实际上是原因。
我很不舒服关于这个问题以及随之而来的答案。有很多的"试试这个神奇的灰尘!没有魔法的灰尘!"
我看不到任何地方,你已经anaylzed锁所采取,并且确定什么样的确切类型的锁都陷于僵局。
所有你指出的是,一些锁定发生--不是什么死锁.
在SQL2005年可以获得更多的信息,关于什么锁正在采取了通过使用:
DBCC TRACEON (1222, -1)
因此当的僵局出现你就会有更好的诊断。
是你的实例,一个新的皇宫SQL属性对象的每一动作或者你也许共享相同的静态背景为所有您的电话吗?我最初尝试后一种方式,并从我记得,这引起不必要的锁定在数据库。我现在创建一个新的上下文对每一个原子操作。
前燃烧的房子要赶飞NOLOCK切都结束了,你可能想看看这一僵局的曲线你应该已经抓获了与分析器。
记住这一僵局的要求(至少)2锁。1连接已经锁,希望锁B,反之亦然连2.这是一个无法解决的情况,有人得到。
什么你们这么远的解决是通过简单锁,这Sql服务器是乐意这样做。
我怀疑你(或皇宫)开始交易,更新声明,并选择其他一些信息之手。但是,你真的需要回溯通过的僵局图找到锁 举行 通过每个螺纹,然后返回通过分析器来找到的发言,引起这些锁被授予。
我希望,至少有4声明,以完成这个难题(或一项声明,采取多个锁定-也许有一个触发器上的职位表?).
将你关心如果你的用户配置文件是几秒钟的约会?
没了-这是完全可以接受的。设置基础交易的隔离水平可能是最好的/清洁的路要走。
典型的读写的僵局来自索引了访问。读取(T1)查找排索引,然后看起来预计列索引B(通常聚集的).写信(T2)的变化指数B(集群),然后有更新该指数AT1S-荔枝上的,希望S-荔枝上的B,T2有X-荔枝上的B,希望U-荔枝上A.僵局,吹。T1被杀害。这是普遍存在的环境有重只读的交通只是有点太多的索引:).解决方案是为了使任一读不要跳,从A到B(ie。包括列在一个或删除列的预计列表)或T2不要跳从B到一个(不更新索引列)。不幸的是,皇宫不是你的朋友在这里...
@杰夫-我肯定不是专家但我有好结果的实例,一个新的方面几乎在每个呼吁。我认为这是类似于创建一个新连接上的对象每一个电话与ADO.开销是不是糟糕你会认为,由于连接的集会仍然可以使用无论如何。
我只是用一个全球静态的助手像这样:
public static class AppData
{
/// <summary>
/// Gets a new database context
/// </summary>
public static CoreDataContext DB
{
get
{
var dataContext = new CoreDataContext
{
DeferredLoadingEnabled = true
};
return dataContext;
}
}
}
然后我做的东西,像这样:
var db = AppData.DB;
var results = from p in db.Posts where p.ID = id select p;
我会做同样的事情更新。无论如何,我没有几乎一样多的流量,因为你,但我肯定得到了一些锁定当我用一个共同属性早上只有极少数的用户。没有保证,但它可能是值得给人一种尝试。
更新:然后再看看你的代码,就只是分享数据方面的寿命,特别是控制实例,这基本上似乎罚款,除非它是以某种方式得到使用,同时通过多的电话内的控制器。在一线的话题,ScottGu说:
控制器只能活一个单一的请求-所以在底的请求的处理他们的垃圾收集的(这意味着属性是收集)...
所以无论如何,这不可能吧,但是再也许值得一试,也许是结合一些负荷试验。
问:为什么你们储存 AnswerCount
在 Posts
表中的第一个地方吗?
一种替代办法是消除"回写"的 Posts
表通过不存储的 AnswerCount
在表中的动态计算数量的答复,以后作为所必需的。
是的,这将意味着你正在运行一个额外的查询:
SELECT COUNT(*) FROM Answers WHERE post_id = @id
或者更通常地(如果正在显示,这为家庭页):
SELECT p.post_id,
p.<additional post fields>,
a.AnswerCount
FROM Posts p
INNER JOIN AnswersCount_view a
ON <join criteria>
WHERE <home page criteria>
但是,这通常结果 INDEX SCAN
并且可以更有效地利用资源的比使用 READ ISOLATION
.
有一个以上的方式来一只猫的皮肤。早de-正常化的一个数据库架构可以介绍可扩展性问题。
你肯定想READ_COMMITTED_SNAPSHOT置上,它不是通过默认。给你MVCC语义。这是同样的事情Oracle使用的默认。具有MVCC数据库是如此的令人难以置信的有用的,而不是使用一个是疯狂的。这让你跑的下列内部的事务:
更新用户设定FirstName='foobar';//决定睡了一年。
与此同时而不提交上述,每个人都可以继续选择从该表就好了。如果你不熟悉MVCC,你会感到震惊,你是以往任何时候都能够生活在没有它。严重。
设置你的默认阅读了未提交的是不是一个好主意。你将毫无疑问的介绍不一致,并结束了一个问题是比你现在有什么.快照隔离可能会工作良好,但它是一个激烈的改变的方式Sql服务器的工作,并把一个 巨大的 载于临.
这里是什么你应该做的:利用试捕(在T-SQL)检测的僵局状态。当它发生时,仅仅重新运行查询。这是标准的数据库编程的做法。
那是很好的例子,这种技术在保罗*尼尔森的 Sql服务器2005年的圣经.
这里是一个快速模板,我使用:
-- Deadlock retry template
declare @lastError int;
declare @numErrors int;
set @numErrors = 0;
LockTimeoutRetry:
begin try;
-- The query goes here
return; -- this is the normal end of the procedure
end try begin catch
set @lastError=@@error
if @lastError = 1222 or @lastError = 1205 -- Lock timeout or deadlock
begin;
if @numErrors >= 3 -- We hit the retry limit
begin;
raiserror('Could not get a lock after 3 attempts', 16, 1);
return -100;
end;
-- Wait and then try the transaction again
waitfor delay '00:00:00.25';
set @numErrors = @numErrors + 1;
goto LockTimeoutRetry;
end;
-- Some other error occurred
declare @errorMessage nvarchar(4000), @errorSeverity int
select @errorMessage = error_message(),
@errorSeverity = error_severity()
raiserror(@errorMessage, @errorSeverity, 1)
return -100
end catch;
一件事,具有为我工作过去是确保我所有的询问和更新访问资源(表)在相同的顺序。
也就是说,如果一个查询更新,以便Table1,Table2和不同的查询,更新它的顺序表2,Table1然后您可能会看到的僵局。
不知道,如果这是可能的为你改变订单的更新,因为你使用皇宫.但它的东西来看待。
将你关心如果你的用户配置文件是几秒钟的约会?
几秒钟肯定会被接受的。它似乎并不喜欢它将是长期的,无论如何,除非另有一个巨大的人数提交答案在同一时间。
我同意杰里米*在这一个。如果你问你应该创建一个新的数据上下文对每个控制器或每页-我倾向于创建一个新的每一个独立的查询。
我要建一个解决方案目前用于执行静态的背景下像你一样,并且当我把吨的请求在野兽的一个服务器(万)在压力测试,我也得读写锁。
只要我改变了我的策略来使用不同的数据上下文在皇宫级别每次查询,并相信SQL服务器可以工作,其连接池的魔法,锁似乎消失。
我当然是在某些时间上的压力,所以想一些事情所有的大约在同一时间,所以我不能100%肯定是有什么固定它,但是我有一个高级别的建立信任,让我们把这种方式。
你应该实现脏读。
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
如果你不绝对需要完美的事务完整性与您的查询,则应使用肮脏的读时访问表与高并发。我假设你的员额表将是其中的一个。
这个可以给你所谓"幻读",这是您的查询作时数据的交易还没有提交。
我们不是在运行一个银行的网站在这里,我们不需要完美的精确性每时间
使用脏读。你是对的他们不会给你完美的精确性,但他们应该清楚你的死者锁定问题。
如果做不到这一点,我们有包装的每一个皇宫的呼叫我们使(很好,简单的阅读的人,这是他们中的绝大多数)在3-4行交易代码块,这是丑陋的
如果实现脏读关于"基地数据库方面的",你总是可以把你的个人呼吁采用一个更高的水平隔离,如果你需要的事务的完整性。
那么,有什么问题执行一个试机制?总是会有可能陷入僵局ocurring那么,为什么不能有一些逻辑以确定它,只是再试一次吗?
不会的至少一些其他的选择引进的性能的处罚所采取的所有时间,当一个试系统将会在很少?
还有,不要忘记某种形式的记录时,一个试发生这样你就不会进这种情况的罕见成为经常。
现在,我看到杰里米的答案,我想我记得听到的最好的做法是使用一个新的属性,对每个数据操作。抢Conery编写的几个职位,有关属性,并且他总是闻起来,而不是使用一个单独的.
- http://blog.wekeroad.com/2007/08/17/linqtosql-ranch-dressing-for-your-database-pizza/
- http://blog.wekeroad.com/mvc-storefront/mvcstore-part-9/ (见的意见)
这里的模式,我们用于视频。显示(链接来源图在更):
using System.Configuration;
namespace VideoShow.Data
{
public class DataContextFactory
{
public static VideoShowDataContext DataContext()
{
return new VideoShowDataContext(ConfigurationManager.ConnectionStrings["VideoShowConnectionString"].ConnectionString);
}
public static VideoShowDataContext DataContext(string connectionString)
{
return new VideoShowDataContext(connectionString);
}
}
}
然后服务级别的(或甚至更加精细,更新):
private VideoShowDataContext dataContext = DataContextFactory.DataContext();
public VideoSearchResult GetVideos(int pageSize, int pageNumber, string sortType)
{
var videos =
from video in DataContext.Videos
where video.StatusId == (int)VideoServices.VideoStatus.Complete
orderby video.DatePublished descending
select video;
return GetSearchResult(videos, pageSize, pageNumber);
}
我必须同意Greg那么只要设置隔离级读未提交没有任何不良的影响,对其他查询。
我会有兴趣知道,杰夫,如何将它设置在数据库的水平将会影响的查询如下:
Begin Tran
Insert into Table (Columns) Values (Values)
Select Max(ID) From Table
Commit Tran
这是我如果我的个人资料甚至几分钟的时期。
是你重新尝试的读之后,它的失败?这当然是可能的射击时一吨的随机读,一些会撞时他们无法阅读。大多数应用程序,我的工作很少写相比的数量读取而我敢肯定读都没有在附近的号码你得到。
如果执行"未提交读"并不能解决你的问题,那么它是艰难的,要帮助不知道更多有关处理。可能有一些其他调整的选择,这将有助于这种行为。除非另有一些数据库大师就派上用场了,我建议提出的问题给供应商。
我将继续为你一切;如何都是盘子系统进行?什么是平均盘队长?如果I/O的支持,真正的问题可能不被这两个查询死锁,这可能是另外的查询,这是瓶颈的系统;你提到一个查询把20秒已经过调整,有没有其他人吗?
重点放在缩短的长期运行的查询,我敢打赌僵局,问题就会消失。
有同样的问题,和不能使用的"隔离级别=隔离级别.ReadUncommitted"在TransactionScope因为服务器没有DTS启用(!).
这就是我做了什么与一个扩展方法:
public static void SetNoLock(this MyDataContext myDS)
{
myDS.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}
因此,对于选择使用关键的并发表,我们启用的"nolock"这样的:
using (MyDataContext myDS = new MyDataContext())
{
myDS.SetNoLock();
// var query = from ...my dirty querys here...
}
建议都是欢迎!