如何诊断修正17883,17884,17887,17888错误

系统 2228 0

 

 如何诊断修正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

17884 17888 诊断 ... 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 中被引用( 17881 17882 )来提供基本的调度器的健康监控。在 sql server 2005 中增加了错误 17887 17888 是新的健康监控。

 

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

 

SQL Server 调度

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

 

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

 

更多信息

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

Inside the SQL Server 2000 User Mode Scheduler

The Perils of Fiber Mode

另外阅读 User Mode Scheduling ken 的书 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 system SOS )。下面详细介绍 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 mask Reconfigure 操作时的调度器 online offline 来根据 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 2000 UMSWorkRequest,SQL Server 2005:SOS_Task )。

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

SELECT @@VERSION

GO

SELECT ‘This is a test’

GO

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

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

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

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

SQL Server 2000

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

SQL Server 2005

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

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

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

         Note sql 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 调用允许异步执行(如 readfile writefile )总是避免使用内核模式执行并且允许工作线程继续运行,因此提高了资源使用率

 

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 也包含对排序和编译 sql hot fixes ,会影响大内存的分配和应用。

更多信息

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

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 sp3 60s 下面的条件就会被检查。

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 17883 17884 的诊断被扩展并且被重定义。逻辑变得更加复杂,更多细节被捕获因此更容易发现问题。

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 条件重启基本检查状态,重新设置数据。

当一个工作线程被认为是 nonyield nonyield 回调函数被调用。 Sql server sql 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 中有 2 nonyield 回调: 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 接口强制换出。

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

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

如, clr nonyield 的判断条件是 5s sql 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 server cpu 属性变高。

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 sp4 sql 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 获取 10 60s 间隔内的报告并且使用 -t1260 避免使用 mini-dump

Watson

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

 

17884 17888 诊断

17884 17888 错误用来诊断是否出现调度器死锁。当调度器判断没有在工作,一条 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 包含了当调度监视区被诊断为 17883 17884 的解决信息和错误信息。

类似 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 也会产生。 Dmvs sys.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 可用就会一直捕获。

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

研究 17883 错误

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

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

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

Sql server 2000 17883

如果在 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 Correct New 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 花在了内核模式上。错误信息的间隔是 10s 17883 错误。只有 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 ,在一个活动的服务上可以查看相关的系统和调度器的利用率。

WARNING sqlos 的动态视图( 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 进程的 symbols binn\exe binn\dll 中。在 2005 之后就不附带这个 symbol 但是在发布的 symbol 任然可用。通过使用 symbol debug 工具可以看到进程里的堆栈来决定可能的错误。这些不必在 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 错误日志中错误信息。在错误信息里面定位线程 id 0xdbc

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

开发人员的 dll com 组件都假定用户显示的兼容是可用的。但是 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 的心跳来更新注册表。假设 regopenkey regwrite 可以快速的发生因此不需要 switchpreemptive

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

sqlservr!CServerHeartbeat::UpdateUptimeRegKey

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

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

总结

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

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

 

如何诊断修正17883,17884,17887,17888错误


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论