Fanr

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

 如何诊断修正17883,17884,17887,17888错误 英文白皮书

目录

前言... 1

SQL Server调度... 1

调度器... 1

工作线程... 3

请求... 3

退让(Yielding... 4

SwichtPreemptive SwichtNonPreemptive. 5

17883诊断... 5

SQL Server 2003 SP3. 5

SQL Server 2000 SP4. 6

SQL Server 2005 调度监视器... 6

诊断间隙... 7

状态... 7

回调... 8

输出... 9

Watson. 10

1788417888诊断... 11

SQL Server 2000 SP3 SP4. 11

SQL Server 2005. 11

I/O端口完成挂起(17887诊断)... 12

研究17883错误... 12

T-sql和动态管理视图... 13

Always check for previosus errors first. 14

报告到微软的Nonyield例子... 14

I/O subsystem problem.. 15

User Display under the SQL Server service. 15

Virturalquery of afragment address space. 16

杀毒软件问题... 17

Registry flush wait. 17

总结... 18

 

前言

Sql server 错误信息17883,17884,17887,17888说明了worker调度的健康状态,对sql server 的并发和吞吐量是有害的

 

Sql server 错误信息17883 17884 是在sql server 2000 sql server 7.0 sp4中被引用(1788117882)来提供基本的调度器的健康监控。在sql server 2005中增加了错误17887 17888是新的健康监控。

 

白皮书重点指明这些错误的判断调点,并且数据库管理员如何去理解和补救这些问题。

 

SQL Server调度

理解sql server 记录调度器健康信息的条件需要简单理解sql server 调度机制。Sql server 7.0引入了一个逻辑调度模式。在这个模式sql 更大的可扩展性和对纤程提供了支持。

 

因为引入了用户模式调度器和增加了调度器健康信息,用户模式调度和健康信息被发布到不同的版本中。白皮书说明调度器健康细节,并且联合除错技术。

 

更多信息

全面理解sql server 用户模式调度和出现在白皮书中的术语,第一需要阅读ken Hendersonmsdn中的文章。

Inside the SQL Server 2000 User Mode Scheduler

The Perils of Fiber Mode

另外阅读 User Mode Schedulingken的书 The Guru’s Guide to SQL Server Architecture and Internals

 

这些材料拉动了微软培训的关键信息不论在工作中还是在代码设计中。这些信息是微软sql server开发和微软sql server客户支持提供的。

 

你可以在Knowledge base articles中的错误判断的条件,在白皮书和微软sql server 联机文档中找到最新的信息,和详细被已知的bug的详细信息。可以到微软的帮助和支持网站中查找178883,调度,ums或者sql server io查找。

 

调度器

Sql server调度器是被sql server工作线程使用的逻辑cpu。在sql server 中调度器被叫做用户模式调度器(ums)。在sql server 2005 中被叫做 sql server operation systemSOS)。下面详细介绍sql server 调度规则的内核。

Max worker thread 设置被用来规定每个调度器的工作线程个数。计算规则如下:

每个调度器的工作线程数 = (最大工作线程/在线的调度器)

只有一个工作线程可以拥有一个调度器因此限制了活动的工作线程数和可扩展性的增加。

Io请求(读写)是和调度器一一对应的,除非设置io affinity参数。下面微软knowledge base article 指出了 io affinity 的细节。

INF: Understanding How to Set the SQL Server I/O Affinity Option (298402)

工作线程并没有被绑定到物理的cpu,除非sp_configure affinity mask参数被设置。这个操作可以被race falgs扩展在如下 微软 knowledge base article 中详细说明:

FIX: Trace Flag -T8002 Treats Affinity Masks Like Process Affinities(818765)

Sql server并不会区别对内核,超线程或者多物理cpu。操作系统报告了所有的cpu数量。Sql server把报告的cpu 当物理cpu使用。唯一一次sql server 使用逻辑cpu是来决定合适的licens约束。关于超线程的介绍可以看如下文章:

SQL Server support in a hyper-threaded environment (322385)

Sql server 创建根据如下元素来创建调度器

Sql server 2000

sql server 200 中,一个逻辑调度器和被报告的cpu数量一一对应,除非 sp_configure中的affinity mask 选项被创建来限制调度器数量

Sql server 2005

sql server2005,一个逻辑调度器和被报告的cpu数量一一对应,除非 sp_configure中的affinity mask 选项被创建。调度器被设置为online 或者offline根据affinity mask的设置。这个允许数据库管理员不重启sql server 来设置affinity maskReconfigure操作时的调度器onlineoffline来根据max work thread 设置来分配工作线程数量。

Note:当一个调度器从online被设置为offline的时候,工作线程如果已经被分配给了这个调度器那么完成。不会再有新的工作线程被分配到这个调度器,然而调度器并不会被完全的设置为offline直到这个调度器中的所有的活动的或者挂起的请求都被执行完毕。

查看基本的调度器的活动信息,你可以看一下的tsql命令。

SQL Server 2000

DBCC SQLPERF(umsstats)

SQL Server 2005

SELECT * FROM sys.dm_os_schedulers

SELECT * FROM sys.dm_os_workers

SELECT * FROM sys.dm_os_threads

SELECT * FROM sys.dm_os_tasks

SELECT * FROM sys.dm_os_waiting_tasks

SELECT * FROM sys.dm_os_ring_buffers

警告:动态管理视图是强大的,但是他们是同步访问内部数据结构和列表的。频繁的使用这些DMV查询会影响系统性能并且一些查询会返回很大的数据库,访问这些信息是线程安全的但是建议直到必要的时候访问这些系统dmv

工作线程

工作线程就是一个线程或者一个纤程。SQL Server数据库引擎开发者使用工作线程来代替线程或者纤程。线程或者纤程被内核引擎封装。内核工作线程有如下的明确的总结:

1.      工作线程被绑定到逻辑调度器和线程和纤程上。Affinity mask被设置后,工作线程被绑定到物理的cpu上。

2.      每个调度器会被分配一定的工作线程数量,并且可以根据需求创建和销毁工作线程。

3.      工作线程只能执行一个完整的请求或者任务。

4.      一个工作线程知道调度器接收到一个请求或者任务,并且没有一个空闲的工作线程的时候才会被创建。

5.      一个工作线程空闲15分钟或者更多的时候,工作线程会被清理。并且销毁线程或者纤程。

6.      Sql server 2005 在内存压力的时候,空闲的工作线程会被马上清理。

Note:sql server 2005 使用在操作系统设置的堆栈大小。Sql server 2005 sql server 2000使用更多的物理内存来支持事实上的工作线程堆栈。

         SQL Server 2000 X64 WOW模式下每个工作线程使用的内存数量也比较多。

         768kb = 512kb标准的堆栈+256kbWOW支持的堆栈)

这种方式设计的工作线程池使得sql server 十分搞笑。大多数管理员会十分惊讶的发现大系统中,工作线程的数量常常远远小于 sp_configure max worker thread 的设置。管理员可以监控工作线程使用一些基本的信息查询,可以查看上一节的信息。

 

请求

工作线程工作单元是一个请求(SQL Server 2000UMSWorkRequest,SQL Server 2005:SOS_Task)。

一个请求可以看成是一个批处理(IE.SQLBatchStarted trace event.运行如下的例子:

SELECT @@VERSION

GO

SELECT ‘This is a test’

GO

这个例子返回的结果是2个请求。客户端提交第一个请求 select @@version,执行结果,之后提交第二个请求。

在服务端,每个请求被分配了一个UmsWorkRequest 或者一个SOS_Task。一个工作线程在执行其他请求前,绑定到一个请求并且执行整个请求。

一个通常的误区是,一个工作线程执行了一个请求,切换到另外一个,并且返回先签订请求。在sql server 7.0 sql server2000sql server 2005 中都不是这样的。请求和工作线程在完成请求前都是被紧密绑定的。

一旦工作线程绑定到了一个请求,工作线程必须完成这个请求。如一个简单的select语句堵塞了,工作线程被绑定到堵塞查询知道堵塞条件被解决并且完成请求。

SQL Server 2000

spid被创建,spid被分配到一个调度器上。所有这个spid请求都会被分配到同一个调度器。Spid的分配在连接的时候就已经被设置了。Spid 会被分配到连接个数最少的调度器中。

SQL Server 2005

Sql server 2005有工作负载分配算法,不在和连接数关联。在sql server 2005中维护了一个推荐的调度器给一个连接。这个推荐的调度器是这个连接最后一次运行的调度器。

根据当前的任务数量确定分配到那个调度器上。当用户登录,用户被分配到最少的任务的调度器。如果调度器少于推荐调度器的120%的任务数,新的任务就会被分配到任务数最少的调度器中。就避免了不平衡的负载除非一个连接提交很多负载。每个新的请求都会相应的被分配调度器。

Note:连接归属被限制到同一个numa节点上。选择调度器分配到统计一个节点的某个调度器被先前连接联合的调度器限制。

         Notesql server2005 调度器负载均衡是无限的。

 

退让(Yielding

逻辑调度器的设计让工作线程在非抢占模式下调度。工作线程拥有调度器直到,工作线程换出,让给另外一个工作线程。

每个调度器都有一个可运行队列。调度器上可运行的工作线程都在这个队列上。但是他们维护了一个waitforsingleobject 调用,等待事件使用了infinite超时。这个infinite超时使得工作线程避免进入了操作系统的调度队列,因此提高了操作系统的性能。

Note:操作系统可以通过系统的规则抢占工作线程。Sql server简单控制那些工作线程可以动态获取cpu资源,因此限制了操作系统的调度负荷。允许更多的cpu使用率。

数据库引擎中有逻辑的退让点,可以在最优的方式下撤回调度器的所有权。对于实例如果工作线程将要堵塞在一个锁中,工作线程就使用锁资源注册自己并且退让调度器。在sql server 上下文切换的时候收回所有权。

退让动作会检查sql server 调度器中的timmer 队列和io队列。删除可运行队列的头,并且设置工作线程的私有事件。工作线程然后在事件上调用waitforsingleobject,然后完成事务。退让的工作线程没有了调度器的所有权。标记的工作线程变成了新的调度器的所有者,并且sql server工作线程上下文切换也被考虑了。

当锁资源的所有者完成了,拥有者检查锁资源的等待队列并且以先进先出的排序,协调工作线程到各自的可运行队列上。调度器继续切换上写文。很快等待的工作线程变成了可运行队列的头,修改调度器的所有者,获取锁资源。

其他地方在sql server代码中使用相似的设计来退让锁资源。这些设计允许数据库引擎最大化cpu资源的并发度。

技术工作线程的配额目标是4ms。以下是退让例子的列表:

1.      当工作线程获取了一个数据库页,工作线程的执行就会被检测,如果超过4ms,就会被换出

2.      每个64-kb大小的排序会被换出

3.      在编译逻辑的时候都会被换出

4.      工作线程调用扩展过程的时候被换出

5.      如果客户端无法不能快速的回去结果,被换出

6.      当强制等待闩锁的时候,工作线程换出

7.      执行完batch中的一个脚本被换出。

当没有工作线程在可用队列的时候,换出工作换成允许另外一个配额或者执行一个必要的空闲调度器的维护。

SwichtPreemptive SwichtNonPreemptive

调度器提供一种方法保护工作线程不受外边的影响,这个逻辑调度器无法控制。当一个扩展调用发生,被封装在切换调用中。如:

SwitchPreemptive

IRowset->GetRows(…)

SwitchNonPreemptive

在这个例子中,SwitchPreemptive强制一个工作线程获取调度器。标记可运行队列的头为新的调度器拥有者并且从逻辑调度器中移除当前工作线程。工作线程所有权改变并且从调度器中删除直到外部执行完成。当外部活动完成,通过调用switchNonPreemptive工作线程返回到和运行队列的底部.

这个外部执行在操作系统层,允许一个调度器多个线程出于活动状态,并且如果活动的过多会减少全局的性能。但是当进程输入了一个代码无法被sql server调度器控制的代码并且执行时间长短不一时,防止了逻辑调度器僵住。

Note:并不是所有的api调用被切换逻辑封装的。Api调用允许异步执行(如readfilewritefile)总是避免使用内核模式执行并且允许工作线程继续运行,因此提高了资源使用率

 

17883诊断

Sql server 安装包含一系列的硬件和软件设置这些可以影响逻辑调度器活动。

其中大量的17883错误是因为api调用僵住。这个通常是子系统,驱动,或者环境的问题并不是运行sql server 的系统问题。

sql server 2000 sp3,sp4 sql server 2005中,指明了一些引擎的特殊bug。当出现错误的时候第一步要做的就是使用包含已知错误修正的sql server 安装包。

如果你使用sql server 2000 sp3 你可以考虑升级到sp4 或者升级到sql server 2005。这些版本增加了额外诊断过程。因此你可以更好的判断错误的根本原因。Sql server 2000 sp4也包含对排序和编译sqlhot fixes,会影响大内存的分配和应用。

更多信息

下面的微软knowledge base article 重点描述了1788317884的错误信息,额外的也修改了消息的文本。

New concurrency and scheduling diagnostics have been added to SQL Server (319892)

 

SQL Server 2003 SP3

为了能够直接的反应出一些比较难调试的场景来判断调度器是否堵塞,调度器监控器就被引入到sql server 2000 sp3.

当调度器的拥有工作线程在60s 内没有被换出并且请求被挂起,sql server 2000 sp3 就会记录一个17883的错误信息。

调度器死锁检查步骤被用来检查17883,18774错误。检查的方式是检查sql server 2000 sp3的调度器信息快照信息的比较。在sql server 2000 sp360s下面的条件就会被检查。

if    快照上下文切换次数= 当前的上下文切换次数
and  
快照空闲上下文切换次数 =当前空闲上下文切换次数
and  
有项目在可运行队列 (调度器被请求)

then  记录17883错误dump错误

这个逻辑上断定在60s内,工作线程没有转移到其他用户上,并且可运行队列内有一个可以被运行的用户。这个清楚的表明了一个问题会对并发性产生影响并且需要深入研究。

Note:这个逻辑不单单是检查每个调度器的17883错误,当在60s内没有新工作请求一个调度器的时候也可以检查到17884错误。

错误17884是有点不同的,至少需要一个队列的想在最后60中被执行。错误17884会在后面详细讨论。

 

SQL Server 2000 SP4

Sql Server 2000 sp4 包含健康检查但是算法被加强了。

sql server 2003 sp3中有小瑕疵 60秒算法逻辑一样的算法在sp4中继续被应用。因为没有其他工作被挂起所以很少情况工作线程可以运行并且被换出但是没有上下文切换的状况。一个工作线程可以换到可运行队列中并且调度器死锁检查器检查它就有可能出现类似17833不活动的工作线程错误。

sql server 2000 sp4 中,判断逻辑被修改了。在这个版本中,基于调度器的换出计数器被添加进来,并且当试图换出的时候计数器加1不管是否做了上下文切换。17883的判断逻辑被修改,因此移除了很少部分的17883 错误报告。

·        if Snapshot Yields = Current Yields

·        and Scheduler is not currently idle

note:空闲说明可运行队列为空。一个空闲的调度器会运行无意义的工作,可能是等待资源,也可能是没有任务。

17883错误一般发生在比较高的调度器id 5,6,7等等。通常来说低的调度器id接收系统任务,如延迟写,锁监控。这些任务执行的间隔少于5s,因此阻止了错误的报告。

 

SQL Server 2005 调度监视器

sql server 2005 1788317884的诊断被扩展并且被重定义。逻辑变得更加复杂,更多细节被捕获因此更容易发现问题。

sql server 2000 sp3 sp4 中学到的17883错误判断的方法被使用到产品中,在sqlserver 2005中诊断和报告的算法都有所改变。很多这些加强已经在sql server 2005 中很久了,通过了大量的测试,已经不想是一个nonyield错误的判断条件了。

调度监视器代替了调度器死锁检查器。调度监控器是一个独立的线程监控给定的调度节点。每个调度节点都有自己的调度监控器并且不受sql server 调度器调度。

诊断间隙

调度监控器的最大提升就是减少了调度器的监控间隔从60s到了5s,并检查当前的调度器状态。这个粒度的提升允许更多的调度器工作负载细节的跟踪。如在监控间隔内属于哪个工作线程,如内核时间,用户模式时间,都直接关系到时钟时序。

 

状态

调度监视器被设计为围绕状态的。这节会描述每个状态:

1.      诊断状态:基本检查

2.      额外报告状态:阀值和资源的边界检查

调度器健康检查的每个状态也变得越来越激烈。

1.      诊断状态(基本判断)

第一个状态是检查nonyield条件,和之间的sql server 2000 sp4 中相似

如果:

         调度器是非空闲状态的

         并且调度器的yield没有换出

并且工作线程没有任何动作

那个这个工作线程就会被认为是nonyield工作线程

Note:有个标记可以设置在每个工作线程上来表示一个工作线程是活动的,因此就不会被考虑为nonyield工作线程。这个是sql server 2005的工作线程发生错误的时候如何判断这个工作线程是否异常。

每个调度器包含内部跟踪结构,当基本检查确定了一个工作线程后跟踪结构会被添加或者修改。如果是nonyield条件的开始,那么数据捕获变成了初始化基线。如果nonyield条件一直对同一个工作线程保持,然后跟踪活动更新数据。这个允许17883错误信息和其他诊断逻辑计算这些值如内核和用户模式的时间并且比较所有的时钟时序。Yield条件重启基本检查状态,重新设置数据。

当一个工作线程被认为是nonyieldnonyield回调函数被调用。Sql serversql clr nonyield回调函数会在以后介绍

2.      额外报告状态

当确定了一个nonyield工作线程的时候额外报告状态被调用。这些检查不影响nonyield回调函数的调用。实体会被记录到sys.dm_os_ring_buffers中。

阀值检查

         如果nonyield状态(占用的时钟时序)>=nonyield阀值

通常这个阀值是10s

调度监视器算法的心跳是5s。在每个心跳之间,基本的测试就会被检查。当基本检查为真,跟踪这个工作线程。因此这里有将近15s的时间从最后一次让出到被检查到为真。

一旦阀值检查为真,资源边界就会检查会被调用。资源边界的检查涉及内核和用户模式的时间和时钟时序的时间比较。

如果下面任意一条件成立,工作线程被认为是nonyield

         如果工作线程(跟踪的 内核时间+用户模式时间) <= 5%

         如果工作线程(跟踪的 内核时间+用户模式时间) >=40%

并且

         工作进程可以获得住够的处理器时间

这个检查是基于sql server 处理的利用率和全局的处理器的可用性。如果sql server 不能获得占用的cpu资源,那么就不会认为工作线程是nonyield

设置5%40%的原因是避免因外部影响而报告的nonyield活动。值较低说明在等待api调用或者等待外部资源,对应的较高的值说明如无限循环这种状况。

如果这些值落在区间内,那么说明工作线程很有可能被外界因素影响。经验表明外界的因素如高优先级的进程或者过多的分页会阻止sql server 获取合理的处理器时间。对于这种状态,对sql server进程执行mini-dump不会抓取全局的系统果冻。Ringbuffer实体和系统监视器通常是被用来诊断和分析这类错误。

为了获取更详细的关于启发式的确定,开启跟踪标记T8022。最好在微软sql server产品支持的指导下使用这个标记,这个标记会生产大量的sql server error log

下面是开启跟踪后的错误日志:

2005-07-05 08:16:56.38 Server   Scheduler monitor trace:

  Scheduler 1 is STALLED

2005-07-05 08:17:01.38 Server   Scheduler monitor trace:

  Worker         : 036BA0E8 (pass: 274)
  Wall           : 319765518
  System Use     : 639527562, User 371593, Kernel 639155968,

                   Idle: 38835593

  System idle    : 99
  Thread Use     : 140
  Worker util    : 0
  Process util   : 0
  Page Faults    : 0
  WS delta       : 0
  Starved?       : N
  Verdict        : NON-YIELD

回调

Sql server 2005提供了一个回调机制来处理nonyield工作线程。当sql server 启动的时候一系列的nonyield回调函数会被注册到调度监控器中。在诊断阶段诊断出一个nonyield工作线程。一旦nonyield工作线程被跟踪回调就会被调用。回调确定了间隔间是否需要其他的深入的操作。

sql server 2005中有2nonyield 回调:sql nonyield reporting sqlclr 强制让出。Sqlclr回调强制clr让出执行。Sql server报告nonyield调度器回调句柄报告和mini-dump捕获。

Clr引擎是集成在sql server 2005引擎中。共享一个线程池,内存池和同步机制。集成提升了性能,额外的死锁诊断,更好的资源管理。当调用clr集合作为存储过程,用户函数,用户定义类型的时候,sql server 不会切换工作线程到抢占模式。

clr引擎中还有一些自然的让出的点,允许工作线程让出给sql server调度。为了保护sql server clr代码可能存在的nonyield情况,调度监控器帮助保护调度器。当一个nonyield被诊断出来了,sqlclr nonyield回调被调用。如果工作线程执行一个clr任务,sql server使用clr接口强制换出。

Notenonyield clr模式通常包含了一个计算密集型循环缺少内存分配。为了收集问题,添加sleep(0)调用。Sleep(0)调用导致工作线程换出。

一旦clr任务换出,当其他可用任务或者完成等待之后才能再被调度。这个保护了调度器。

如,clr nonyield的判断条件是5ssql server 调度器的配额是4ms。这个任务被强制等待1250个配额才能配释放。当前的强制等待的峰值是5s并且阻止clr任务影响大量的其他任务的调度。

强制等待的配额公式如下:

    = min(5000, (Time At Yield – Time of scheduler ownership)/4ms )

这里有个简单的例子说明档clr被强制等待的时候会发生什么:

quantums_to_yield =  <<formula from above>>;
while(quantums_to_yield > 0)

{

    YieldToScheduler(0 /* no wait time */);  -- See note below

   quantums_to_yield--;

}

Note:执行一个使用0值的让出调度和sleep(0)api一样。工作线程只是简单的被放到可运行队列的低部并且切换。如果可运行队列的头和地步是同一个工作线程,没有其他的工作线程需要被执行。因此同一个工作线程会马上被允许执行获取另外一个配额。循环里主要是确定当没有其他任务被相应的时候等待并不一定总是(quantums_to_yield * 4ms)

为了避免惩罚过于严重,惩罚逻辑能够很快把工作线程切换到活动任务中,诊断,中断一个在失控的clr任务会造成并发问题。任何时候一个任务允许使用调度器10s以上,会发生不可预计的结果。如果一个失控的clr任务被分配到想通的调度器,日志写入,就可以控制10s左右。在所有的调度器上执行提交操作,10秒或者更久。

当调度监视器强制让出clr任务,任务记录了强行换出的次数并且把输入buffer 写到sql server error log 中。

强制clr任务换出,废物收集器被调用抢占当前任务。Clr gc线程以实时的优先级运行在每个cpu上。当被调用,会加大cpu的负载导致sql servercpu属性变高。

Sql nonyield 回调控制了传统的17883报告和数据捕获。和sql server 2000不同,这里回调会在第一个nonyield诊断10秒阀值的时候被调用,并且如果条件成立之后的每个5s也会报告。Sql nonyield回调每隔60s,报告17883信息来避免反复的出现相同的错误信息写入到错误日志中。

         Note:如果clr nonyield条件成立,sql nonyield回调会忽略这个nonyield条件

 

输出

Nonyield回调会生成sql server 错误日志报告也会生成mini-dump.

17883错误报告

17883错误每隔60s被报告。间隔时间可以再sql server 产品支持的见一下通过T1262 修改。生成mini-dump之后来通知sql server 2000 sp4sql server 2005记录17883信息,避免捕获数据的延迟。微软sql server产品支持预见到了io问题导致的17883条件成立。写入错误日志也能指明是io问题,因此快速的抓取dump可以提供能提供更好的排错信息。

17884 mini-dump生成

nonyield状态被诊断出,sql server 就视图抓取所有线程的mini-dump

当一个指定的nonyield状态到达60s就会产生dump。一旦17883 mini-dump被捕获,当-t1262被设置或者sql server 重启的时候,才会捕获更深入的17883mini-dump。然而,17883错误信息不会理睬mini-dump是否被捕获。

跟踪标记-t8024可以用来改变mini-dump捕获。

-T8024 设置

Mini-dump 动作

OFF (Default)

Dump被抓取的时候,不会对dump影响
<![if !supportLineBreakNewLine]>
<![endif]>

ON

增加额外的判断条件来确实是否捕获mini-dump。若要捕获mini-dump必须满足以下的一个条件。

1.   nonyield的工作线程cpu占用必须大于40%

2.   sql server进程并没有缺少cpu占用

额外监控1是监控失控的cpu用户的,监控2是监控可能是api调用堵塞用户或者类似的用户

跟踪标记-t1262也被使用来改变mini-dump捕获

         SQL Server 2000

默认的sql server动作是捕获mini-dump在第一个17883的报告。之后的17883报告不会捕获mini-dump。当sql server 启动的时候使用-t1262启动阐述的时候,sql server 会为每个17883错误生产一个mini-dump.

         SQL Server 2005

如果-t1262被设置,当发生唯一的nonyield的时候sql server 2005 就会集合这些mini-dump.-t1262被设置,nonyield条件成立就会生产mini-dump,之后60s相同的nonyield发生。一个新的nonyield发生导致dump捕获发生。

Note:-t1262并不是需要配置在启动参数里面的。也可以动态的执行 dbcc traceno(1262,-1),或者撤销 dbcc traceoff(1262,-1).

Caution:使用-t1262要谨慎。Mini-dump发生覆盖了所有的线程堆栈。如果在64位的系统中是一个很大的文件。

跟踪标记-t1260可以是被用来阻止任何17883,17884,17887,17888错误信息,生产mini-dump生成。这个跟踪标记何以和-t1262一起使用。如:你可以使用-t1262获取1060s间隔内的报告并且使用-t1260避免使用mini-dump

Watson

17883错误生成的mini-dumpsql server 2005 强化被上传到watson。如果sql server 2005进程被允许报告到watson上,17883 mini-dump会被上传。先前版本不允许17883错误mini-dump上传。

 

1788417888诊断

1788417888错误用来诊断是否出现调度器死锁。当调度器判断没有在工作,一条17884或者17888错误被记录。

一下是条件可能会出发错误:

         所有的调度器被标记为17883

         所有工作线程被临界资源堵塞

         所有工作线程执行一个很长的查询

Note:在现实中微软很少看到17884错误。这个问题通常是第二个或者第三个条件引起的。当17884错误被报告并且关系到相关bug,先前的错误的根原因可以在sql server 错误日志中找到。

 

SQL Server 2000 SP3 SP4

sql server 2000 sp3 sp4,当调度器死锁检查启动检查出17883 nonyield,后也会检查一下条件。

         任务请求队列(tasks)>0

         快照的工作进程 == 当前的工作进程

每次从调度器工作队列中被取出来的新的任务请求时,工作进程计数器就会加1.因此如果调度器有工作队列但是没有执行,如果工作请求了60s那么调度器被认为是僵住了。

如果调度器被认为僵住了那么17884就会被报告。第一个报告17884错误会生成mini-dump

SQL Server 2005

sql server 2005中,当调度器监控器检查nonyield条件,同时诊断17884条件。以下条件判断调度器是否被僵住。

1.      工作请求队列存在

2.      或者新的工作线程被创建

3.      或者快照工作进度 == 当前工作进度

如果这些条件的一个成立,sql server 2005 会认为是一个不可用的调度器死锁如果所有的调度器被堵塞了并且sql server 被没有内存压力,sql server 就会报告17884 17888错误。

Note:sys.dm_os_ring_buffer dmv包含了当调度监视区被诊断为1788317884的解决信息和错误信息。

类似17883的回调,17884(调度器死锁),当调度器被认为死锁回调也会被调用。当前只有一个回调是可用的。死锁回调被用来捕获mini-dump和记录17884错误信息。

就像17883,不管被调用的多么频繁,回调报告发生的间隔是60s。除非用了-t1262跟踪标记,不然mini-dump 总是在第一次报告的时候产生。以下是如何产生mini-dump的逻辑。

1.      先前没有产生mini-dump

2.      并且sql server 进程没有在cpu压力

错误17888,是17884的变种在sql server 2005中引入。当调度器死锁发生,并且50%以上的工作线程都在等待相同的资源(如lck_m_x,network_io,或者soap_read)。在这种状况下,dump不会自动生成,因为假定错误是由其他因素引起。如果-t1262被设置,dump也会产生。Dmvssys.dm_exec_sessions,sys.dm_exec_requests,sys.locks,等其他可以提供深入问题的本身。

 

I/O端口完成挂起(17887诊断)

Io端口完成挂起是一个新的诊断特性,在sql server 2005中被添加到调度监视器。每当调度监视器执行检查io端口成功完成。完成端口也用于网络io

如果出现以下状况 就会发生17887错误。

一个io完成是活跃的,从最新的调度器心跳开始并且没有其他新的io完成被执行,并且持续了10s并且当前sql server 没有内存压力和cpu不足的状况。

调度监视器就考虑在io完成流程考虑io完成被挂起。

一旦io完成挂起确认就会调用io完成回调函数。17887错误信息的报告是10s一次。Mini-dump只会在第一次发生的时候被捕获。如果-t1262可用就会一直捕获。

1788317884想通。17887的根源可能和上一个错误相关。在确定根源错误前在sql server 错误日志中查看上一个错误日志,很有可能是上一个错误造成的问题。

研究17883错误

研究17883,17884,17887,17888错误都是一样的。这节主要指出17883错误。这些排错步骤可以用来处理很多错误。

17883,17884,17887,17888错误不能马上告诉你。尽管,错误报告,mini-dump被设计成捕获所有线程的堆栈。这些信息和mini-dump指示了这些内容是nonyield数据。

Note:从开始sql server 2005mini-dump报告会被上传到微软watson server。如果回应是可用的。就会被记录到应用程序事件日志中。

Sql server 200017883

如果在sql server 2000中错误17883发生,以下错误信息会被记录到错误日志中。这个错误指明SPID:EC,进程id,调度器id

         Process 51:0 (dbc) UMS Context 0x018DA930 appears to be non-yielding on Scheduler 0.

Note:这个错误信息从sp3开始被修改。在微软knowledge base articles FIX: Error 17883 May Display Message Text That Is Not CorrectNew concurrency and scheduling diagnostics have been added to SQL Server有详细说明

SQL Server 2005(17883)

Sql server 2005 17883错误信息提供了更多关于17883的错误。这些信息已经被扩展了。包含会话,请求id信息,进程id,调度器id并提供进程的详细信息。以下是sql server 2005 17883错误信息例子:

Process 51:0:0 (0xdbc) Worker 0x036BA0E8 appears to be non-yielding on Scheduler 1. Thread creation time: 12764721496978. Approx Thread CPU Used: kernel 15 ms, user 171 ms. Process Utilization 0%. System Idle 99%. Interval: 325602683 ms.

Approx thread信息展示了问题的详细信息。从被认为是nonyield以来,内核,用户时间是用户模式和内核模式cpu运行时间的综合,并不是从线程被创建开始。如果要看线程被创建开始的,查询以下sql语句:

SELECT * FROM sys.dm_os_threads
WHERE os_thread_id = << Integer thread Id value goes here >>

进程使用率,系统空闲和间隔信息显示了sql server 进程自己的详细信息。

如果如果看错误信息。你会发现以下几点错误。

1.      如果用户模式时间快速的升高,并且还在持续,可能是死循环引起的nonyield

2.      如果内核模式时间快速升高,并且线程花费大量的时间在操作系统中,那么需要内核debug来确定因此错误的原因。

3.      如果内核模式和用户模式的时间增加不快速。进程可能是等待api调用的返回,如waitforsignleobject,sleep,writefile或者readfile。或者线程没办法得到调度。Api僵住,那么就需要调试内核查找根本问题。

4.      如果系统空闲时间比较低,进程使用率比较低,之后sql可能得不到猪狗的cpu,检查其他进程的cpu使用率。也检查系统在进行分页。Select * from sys,dm_os_ring_buffers 语句可以提供更多详细的信息。

5.      如果内核时间+用户时间比较低,但是进程占用比较高,这种错误的时候可能是抢占模式的线程小号了所有的cpu

结合系统利用率和空闲时间可以查看内部的真正原因。

这个17883的例子表明了,自从工作线程被僵住的时候15ms花在了内核模式上。错误信息的间隔是10s17883错误。只有15ms 被用在内核模式。171ms 用在用户模式。这种场景的例子是调用了api结果在做底层的安全检查,安全检查从pdc中取回数据。线程并没有在执行内核模式或者用户模式,而是在等待网络从pdc中相应。

Note:这个例子的解决方法有2个。Sql server会在调用api的时候使用switchnonpreemptive逻辑描述。管理员必须查看从pdc相应的时间以避免sql server 在请求安全性检查的时候负载僵死。

要注意的是当sql server 切换到SwitchPreemptive的解决方式并不是最好的解决方法。在这个场景中,sql server 在登陆检查的时候使用了SwitchPreemptive逻辑。结果是大量的工作线程僵死而每个工作线程和对应的调度器。当工作线程切换到抢占模式,返回最少的诊断,返回的结果任然会因为pdc相应问题僵住。

内核时间和用户模式时间会让你无法预料。一个可重生的自旋锁(在sql server 内部用来同步)会返回17883错误并内核时间和用户模式时间比较少。

因为叫自旋锁,所以一个可以预期,就是sql server 进程会使用大量的cpu。事实上,自旋锁发生比较快,之后调用switchtothread或者sleep

内核模式时间不会爬升因为线程在sleep。进程占用实际上下潜,当线程SpinToAcquire的时候被僵住。如果用户模式爬升,那么就是nonyield循环被请求。

T-sql和动态管理视图

Sql server 2005可以查看内核和用户模式时间使用dmv sys.dm_os_threads。当和sys.dm_os_works sys.dm_os_schedulers,在一个活动的服务上可以查看相关的系统和调度器的利用率。

WARNINGsqlos 的动态视图(sys.dm_os_*)需要同步访问临界的,搞活跃度的链表,同时使用内核模式调用getthreadtimes。这个动作被优化并且设计为线程安全但是使用这些dmv应该被限制为,在必要的排查的时候使用。定期反复的查询这些并不被推荐。

sql server 2000中,访问相同类型的调度器数据需要请求用户模式的进程dump。这些和debug工具像是可以使用命令(!runaway)查看各个线程时间信息。下面是dm_os_threads查询的例子完成在!runaway

SELECT kernel_time + usermode_time as TotalTime, *
FROM sys.dm_osthreads
ORDER BY kernel_time + usermode_time desc

一个常用的技术被微软技术支持使用关注调度器健康的是检查调度器时钟列表。时钟列表在每次让出的时候被检查,因此这些值可以说明工作线程让出后的经历时间。下面的查询可以在sql server 2005 上运行查看从调度器最后检查列表到现在经过的毫秒数。

SELECT yield_count, last_timer_activity,

(SELECT ms_ticks from sys.dm_os_sys_info) - last_timer_activity

AS MSSinceYield, * FROM sys.dm_os_schedulers

WHERE is_online = 1 and is_idle <> 1 and scheduler_id < 255

         Note:微软sql server支持服务和开发也可以使用用户模式的dump产生相似的结果。

Always check for previosus errors first

17883,17884,17887,17888错误信息通常指明了一个问题。在开始深入研究错误报告前,查看系统的监控信息,比如性能信息,时间日志,sql server错误日志和其他应用程序日志信息。

理解先前的系统上的问题可以很快的知道为什么sql server指明并且报告一个健康错误。在研究之前要先了解之前的错误。

更多信息

下面是微软knowledge base article 说明可能会引起17883,17884,17887错误信息的场景。

New concurrency and scheduling diagnostics have been added to SQL Server (319892)

报告到微软的Nonyield例子

Sql server 2000的公布的sql server进程的symbolsbinn\exe binn\dll中。在2005之后就不附带这个symbol但是在发布的symbol任然可用。通过使用symboldebug工具可以看到进程里的堆栈来决定可能的错误。这些不必在sql server 所在的电脑上运行。

查看被僵住的线程堆栈

这个过程教你如何检查被堵塞的线程的堆栈(17883

1.      复制打开sqldmpr####.mdmp文件来debug或者在调试器中打开。

2.      建立适当的symbol文件地址可以在调试器命令窗口使用以下命令

.sympath=srv*c:\symbols*http://msdl.microsoft.com/download/symbols;c:\program files\microsoft sql server\mssql\binn

3.      获取17883 sql server错误日志中错误信息。在错误信息里面定位线程id0xdbc

Process 51:0:0 (0xdbc) Worker 0x036BA0E8 appears to be non-yielding on Scheduler 1. Thread creation time: 12764721496978. Approx Thread CPU Used: kernel 15 ms, user 171 ms. Process Utilization 0%. System Idle 99%. Interval: 325602683 ms.

4.      在调试器的命令窗口,键入

         ~~[TID]s

         Tid是从17883的错误信息中的出来的。这个例子键入的命令应该是~~[0xdbc]s

5.      查看调试器窗口中堆栈中的命令kb 1000

一旦你有了堆栈你就能估计问题的条件。

以下是报告到sql server 的堆栈的例子。

I/O subsystem problem

Io子系统问题往往是17883问题的根源。SQL Server 2000 I/O Basics 白皮书细致的描述了sql server执行io的过程。特别是sql server 异步io,应该不会再io中堵塞。

以下堆栈标示试图写入数据库文件。Io被堵塞在writefile调用中。线程在io调用上堵塞表示系统级别问题。

NTDLL!NtWriteFileGather
KERNEL32!WriteFileGather

ums!UmsScheduler::GatherWriteAsync
sqlservr!UmsGatherWriteAsync
sqlservr!FCB::GatherWrite

ums!ProcessWorkRequests
ums!ThreadStartRoutine
msvcrt!_threadstart
KERNEL32!BaseThreadStart

通常导致io阻塞的原因

l 数据库文件使用ntfs压缩

l 过滤驱动不能完全执行

l 非分页池处于内存压力

l Io路径问题如错误重试,或者丢失了SAN连接。

错误排除

排查io的问题需要调试系统内核获取irp跟踪,获取系统中的io请求状态。Sql server已经让io请求运行在异步模式下,所以设置switchpreemptive 不能解决问题。Switchpreemptive只会导致大量工作线程在生产io请求的时候堵塞并且增加了上下文切换。

sql server io的时候,Sql server 使用闩保护bufffer。这种状况会导致错误比如io被堵塞,buffer上的闩锁超时。

User Display under the SQL Server service

开发人员的dllcom组件都假定用户显示的兼容是可用的。但是sql server 这种服务程序使用隐藏的桌面。Windows,消息框,或者其他虚拟组件都是在隐藏桌面被创建的。用户去回应这些组件是不可能的。因此,工作线程是一直被嵌入的,直到sql server 重启。

下面是堆栈清楚的显示了线程想创建窗口,但是sql server是以服务形式运行的,所以输入不被允许。窗口和对话框会等待一个回应,但是永远不会发生。

user32!InternalCallWinProc
user32!UserCallWinProcCheckWow
user32!DispatchClientMessage
user32!fnINLPCREATESTRUCT
ntdll!KiUserCallbackDispatcher
user32!NtUserCreateWindowEx
user32!_CreateWindowEx

导致基于服务的程序创建窗口的通常原因

Seterrormode设置被连接服务器或者xproc修改成不正确的值。

一个dll设置了不正确的假设,一个用户回应总是可用的。

故障排除

使用调试器确定对话框的文本或者标题。这个可以让你更好的理解倒是显示的原因是什么。通过显示这个堆栈,帮助指出这个组件和其他细节。

Sql server不能阻止所有用户对象的输入,所以组件所有者必须修正,在运行服务的时候可以正确的运行。Sql server 2005 增加了基本保护,不允许clr使用gui。未来的发布版本中,sql server会勾住所有的windows和终端的创建。

 

Virturalquery of afragment address space

基于windows 的程序使用windows api 获取操作系统的服务。Sql server使用很多windows api调用。当api调用预期会快速返回的时候,Sql server 不会把工作线程切换到抢占模式。如果api调用不快速返回,就会发生调度器健康问题。

这个堆栈来至于一个已知的sql server问题,在sql server 2000 sql server 2005中被修正。当多个备份被启用时,virtualquery 被频繁的调用。有虚拟地址空间碎片的机器会报17883错误。

NTDLL!ZwQueryVirtualMemory
KERNEL32!VirtualQueryEx
KERNEL32!VirtualQuery
sqlservr!BackupOperation::GetLargestFreeVirtualRegion sqlservr!BackupOperation::ChooseBufferParameters sqlservr!BackupOperation::InitiateStartOfBackup sqlservr!BackupEntry::BackupLog

引起这个问题的通常原因

1.      虚拟地址空间碎片因为申请和释放操作

2.      很多sql server 备份被同一时间使用

3.      大量的virtualalloc/virtualfree 调用(通常不是sql server)会发生virtualquery

故障排查

Sql server 2000 sp4 sql server 2005修改了备份的buffer大小决定的逻辑来避免virtualquery

其他活动可能造成虚拟地址空间碎片。如调用windows api formatmessage使用virtualalloc/virtualfree。当记录错误日志或者事件日志的时候会被调用。李日,如果你要审核登陆,就会有更多的formatmessage被调用。这些单独的并不会报17883错误,但是是诱因之一。

杀毒软件问题

匆匆一看,这个堆栈是因为virtualquery问题。仔细检查myvirusscanner函数。杀毒软件供应商会保护buffer溢出并且勾住(hook)一些api 调用。堆栈显示了勾住的调用CryptAcquireContextW供应商过多的调用了virtualquery,必须修正。

NTDLL!ZwQueryVirtualMemory+0xb
KERNEL32!VirtualQueryEx+0x1b
KERNEL32!VirtualQuery+0x13
MyVirusScanner+0x36c3
MyVirusScanner+0x34d0
MyVirusScanner+0x117e              <-- Virus hook

ADVAPI32!CryptAcquireContextW+0xb8
sqlservr!SecureHash+0x117
sqlservr!FEvalPasswdW+0x35
sqlservr!FCheckPswd+0xe4
sqlservr!FindLogin+0x417
sqlservr!login+0x20f
sqlservr!process_login+0x7d
sqlservr!process_commands+0x201
ums!ProcessWorkRequests+0x272
ums!ThreadStartRoutine+0x98
msvcrt!_threadstart+0x87
KERNEL32!BaseThreadStart+0x52

在前面描述过sql server2000 sp4 在调用CryptAcquireContextW前会switchpreemptive但是这样会使上百个工作线程堵塞。这个问题是杀毒软件导致sql server停止登陆进程并且让sql server不可用,抢占工作线程不能被识别为nonyield

Registry flush wait 

以下堆栈是sql server 引起已经被sql server 2000 sp4修正。Sql server 2000 sp3新增了60s的心跳来更新注册表。假设regopenkeyregwrite可以快速的发生因此不需要switchpreemptive

NTDLL!NtOpenKey+0xb
ADVAPI32!LocalBaseRegOpenKey
ADVAPI32!RegOpenKeyExW

sqlservr!CServerHeartbeat::UpdateUptimeRegKey

sqlservr!CServerHeartbeat::OnHeartbeat
sqlservr!CHeartbeatTask::ProcessTskPkt

当系统要刷新注册表cache 的时候,假设就不成立了,新的注册表调用会被堵塞。在sql server 2000 sp3,sp4sql server 2005被修正在对注册表操作的时候都会被切换到抢占模式。

总结

文章中的例子有些是bug已经在最新的版本中修正。主要的已知的在sql server 2000 sp4中修正了。另外,sql server 2005 使用了更多的测试。

大多数报告到watson的问题被描述为外部问题的是其他非sql server程序影响的。

 

posted on 2012-07-18 15:46  Fanr_Zh  阅读(2820)  评论(1编辑  收藏  举报