1 --内存压力分析
2
3 --表现特征
4 --SQL经常触发lazy writer
5 --SQL需要经常从硬盘里读数据,会有很多硬盘读
6 --执行计划经常被清除,所以buffer pool里的stolen内存部分应该不会很多
7 --由于数据页经常被清除,所以page life expectancy不会很高,而且会经常下降
8
9 --page life expectancy:缓存页面生存时间
10
11 --如果数据页面 buffer pool内存有压力,SQL会优先清除内存里的执行计划
12
13 --解决办法:
14 --来自外部压力:Windows内存不够,SQL会压缩自己的内存,这时database page首当其冲被压缩
15 --建议SQL安装在专用服务器上
16
17
18 -------------------------------------------------------------------
19 --database page自身使用需求的压力
20 --1、32位服务器开启AWE
21 --2、增加物理内存
22 --3、如果scale up向上扩展不行,考虑scale out向外扩展,把SQL中的多个数据库移到其他服务器上,只留一个应用数据库或者分库处理
23 --4、使用表索引,减少不必要的读和表扫描
24
25 --1、2、3都需要新的硬件投资,而且到底加多少内存能够解决问题不好说,最好是最后一种方法
26
27 -----------------------------------------------------------------------
28 --来自buffer pool里的stolen压力
29 --如果声明了很多游标,用完了不关,或者prepare很多执行计划,不un-prepare,不登出SQL就会占用
30 --database page的空间
31 --使用sys.dm_os_memory_clerks的single_pages_kb字段查看那个clerk用掉了较多的stolen内存
32
33
34 -----------------------------------------------------------------------------------
35
36
37 --来自multi-page memtoleave的压力
38 --由于multi-page的量不大,所以一般这种问题较少
39 --1、32位系统没有开AWE,使用 /g 参数增大了multi-page的上限值
40 --2、64位系统multi-page没有上限,如果SQL调用了一些内存泄漏很厉害的第三方代码,
41 --64位系统就算内存再充裕也有漏完的可能性
42
43 ----使用sys.dm_os_memory_clerks的single_pages_kb字段查看哪个clerk用掉比较多的内存
44
45
46 ---------------------------------------------------------------------------------
47 --使用内存比较多的语句
48 --有一些语句在不停地调用数据,把读数据页面最多的语句找出来,基本就能够找到SQL内存压力
49 --的地方。分析一下这些语句是不是天生就要返回很多数据,为什麽读那么多数据页面
50
51
52 --例如:一个表有100W行记录,查询要返回70W行记录。那么这个语句在SQL使用很多内存是正常的
53 --在SQL上调整的余地不大,选择的方案有限:
54 --1、增加物理内存
55 --2、归档历史数据,把表里的数据降下来
56 --3、调整应用程序设计,避免不必要的大数据量查询
57
58 --因为没有索引,所以SQL不得不把表里的数据页面读到内存里,这样内存消耗是可以优化的
59 --最好的办法添加索引
60
61 --分析方法:
62 --使用DMV提取历史信息
63 --使用SQL Trace
64
65
66 --SQL2005以后有一个动态管理视图sys.dm_exec_query_stats,返回缓存查询计划的性能统计信息
67 --SQL会统计从上次SQL启动以来,一共做了多少次logical读写,多少次physical读,还记录执行所用的
68 --CPU时间总量。
69
70 --按照物理读的页面数排序 前50名
71 SELECT TOP 50
72 qs.total_physical_reads,qs.execution_count,
73 qs.total_physical_reads/qs.execution_count AS [avg I/O],
74 SUBSTRING(qt.text,qs.statement_start_offset/2,
75 (CASE WHEN qs.statement_end_offset=-1
76 THEN LEN(CONVERT(NVARCHAR(max),qt.text))*2
77 ELSE qs.statement_end_offset END -qs.statement_start_offset)/2) AS query_text,
78 qt.dbid,dbname=DB_NAME(qt.dbid),
79 qt.objectid,
80 qs.sql_handle,
81 qs.plan_handle
82 from sys.dm_exec_query_stats qs
83 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
84 ORDER BY qs.total_physical_reads DESC
85
86 --按照逻辑读的页面数排序 前50名
87 SELECT TOP 50
88 qs.total_logical_reads,
89 qs.execution_count,
90 qs.total_logical_reads/qs.execution_count AS [AVG IO],
91 SUBSTRING(qt.text,qs.statement_start_offset/2,(CASE WHEN qs.statement_end_offset=-1 THEN LEN(CONVERT(NVARCHAR(max),qt.text))*2 ELSE qs.statement_end_offset END -qs.statement_start_offset)/2) AS query_text,
92 qt.dbid,
93 dbname=DB_NAME(qt.dbid),
94 qt.objectid,
95 qs.sql_handle,
96 qs.plan_handle
97 from sys.dm_exec_query_stats qs
98 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
99 ORDER BY qs.total_logical_reads DESC
100
101 --上面语句的缺点是没有时效性,记录的生存期与执行计划本身相关联
102
103
104
105
106
107 --使用SQL Trace
108
109 --SQL Trace里面有一个reads字段,记录了某条语句完成过程中一共做了多少次读的动作,找到read最多的语句
110 --每个SQL Trace里有成千成万的语句,可以使用fn_trace_gettable 像一张表一样把trace文件里的记录查询出来
111 --可以用他将记录转入到SQLSERVER里,然后用查询语句进行统计分析。
112
113 --例如,在c:\sample目录下有一个问题时段的trace文件,叫a.trc.
114 --导入到SQLSERVER
115
116 SELECT * INTO #SAMPLE
117 FROM sys.fn_trace_gettable('C:\Users\Administrator\Desktop\1.trc',DEFAULT)
118 WHERE EventClass IN(10,12)
119
120 SELECT TOP 100 * FROM #SAMPLE
121 SELECT TOP 1000 TextData,DatabaseID,HostName,ApplicationName,LoginName,SPID,
122 StartTime,EndTime,Duration,reads,writes,CPU
123 FROM #SAMPLE
124
125
126
127 --(1)找到是哪台客户端上的哪个应用发过来的语句,从整体上讲在数据库上引起的读最多
128 --得到这个结果,就能大概知道哪些客户端要访问大量的数据页面,可能造成内存压力
129 SELECT * INTO #SAMPLE
130 FROM sys.fn_trace_gettable('C:\Users\Administrator\Desktop\1.trc',DEFAULT)
131 WHERE EventClass IN(10,12)
132
133 SELECT DatabaseID,HostName,ApplicationName,SUM(reads) AS reads
134 FROM #SAMPLE
135 GROUP BY DatabaseID,HostName,ApplicationName
136 ORDER BY SUM(reads) DESC
137
138
139
140 --(2)按照reads从大到小排序,最大的1000个语句 用这个方法可以找出最昂贵的单笔语句
141 SELECT TOP 1000 TextData,DatabaseID,HostName,ApplicationName,LoginName,
142 SPID,StartTime,EndTime,Duration,reads,writes,CPU
143 FROM #SAMPLE
144 ORDER BY Reads DESC
145
146
147
148
149 --利用readtrace这个工具自动分析trace文件,找出使用大量系统资源的语句
150 --如果只是要找到一段时间内系统资源使用比较多的语句,不用做详细分析,readtrace这个工具
151 --能够自动完成
152
153
154 --通过以上三种方法,DBA比较快地找到某个SQL上造成很多读操作的语句。这里需要说明的是
155 --SQL Trace里reads字段是逻辑读,而不一定是物理读,只有语句访问的数据不在内存里时,
156 --才有物理读!!!!!!!!
157
158
159
160
161 --stolen内存压力分析
162 --除了database pages,其他内存分配基本都是直接从地址空间申请,不是先reserve,后commit的
163 --stolen内存主要以8KB为单位分配,分布在buffer pool里
164
165 --stolen内存不缓存数据页面,任何一条语句的执行都需要stolen内存来做语句分析,优化,执行计划的缓存
166 --可能也需要内存来做排序,计算。任何一个连接的建立,需要分配stolen内存给他建立数据结构
167 --和输入输出缓存区。如果stolen内存申请不到,SQL任何操作都会遇到问题
168
169
170 --stolen内存有缓存:执行计划,用户安全上下文(每次执行sql语句之前都不需要重新建立安全上下文)
171 --这些缓存越多越好
172
173
174 --stolen内存没有缓存:使用完毕立刻释放,供其他用户使用,例如:语义分析,优化,做排序,做Hash等
175 --所以在32位SQL,虽然stolen内存只有不到2GB,但是很少有stolen内存不够用的情况
176
177
178
179 --在SQL2005 SP2以后,SQL产品组调小了执行计划的最高上限。这是因为如果一个SQL能够缓存这么多不同的执行计划
180 --说明他内部运行的大多数都是动态TSQL,很少能够重用。如果经常有执行计划重用的现象,SQL也就不需要每次都
181 --生成新的执行计划,从而缓存这麽多份了。在这样的SQL里,就算缓存再多的执行计划,重用的机会都很小。
182 --所以这么多的缓存对SQL的性能帮助不是很大,反而增加SQL的维护成本。从经验上看,SQL缓存1GB~2GB的执行计划
183 --基本足够了,更多的缓存基本上对性能帮助不大。在有些情况下,定期清空执行计划缓存,反而对SQL的健康起到
184 --帮助作用
185 CHECKPOINT
186 DBCC DROPCLEANBUFFERS
187
188
189
190 --stolen缓存区与数据缓存区相互关系
191 --stolen内存很多是用完就释放的,不会累积下来。
192 --对于有缓存机制的stolen内存,主要就是执行计划,也有清理机制,当buffer pool有内存压力的时候,SQL
193 --会同时清楚执行计划和database pages ,SQL都会把最久没有使用的对象清除
194
195 --就算一个查询一次访问上百MB的数据,但是他的执行计划却是非常小的。但是当lazy writer没有能清除
196 --正在被使用的stolen内存的时候,会发生stolen内存越积越多,最后侵占database pages空间的现象
197
198
199
200
201
202 --外部压力与内部压力
203 --外部压力:Windows通知SQL要压缩内存的时候,整个buffer pool里的所有内存都面临着清理
204
205 --内部压力:
206 --database pages挤压,当一个查询需要大量的data page的时候
207 --stolen内存内部一些始终未清理的对象,例如,打开了游标不关,或者prepare了一些语句没有unprepare,
208 --那么只要不logout,SQL就无法清理和释放这些对象
209
210
211
212
213
214 --解决办法:
215 --由于stolen内存是SQL自己申请使用的,可以用clerk看到
216 --特征:
217 --(1)返回错误信息701,在SQL 的errorlog里能看到错误信息
218 --(2)在sys.sysprocesses里的waittype字段不等于0X0000,而和stolen内存相关的等待状态:
219 SELECT spid,
220 blocked,
221 waittype,
222 DB_NAME(dbid)AS dbname,
223 open_tran,hostname,
224 program_name,
225 hostprocess,
226 cmd,
227 loginame
228 FROM sys.sysprocesses
229 ORDER BY spid ,dbid ASC
230 SELECT @@SPID
231 --waittype代码:
232
233 --cmemthread(0x00B9)
234 --当多个用户同时往同一块缓存区里申请或释放内存时,在一个时间点,只有一个连接可以做申请或
235 --释放内存动作,其他连接必须等待。这些连接的等待状态,就是cmemthread。这种等待状态发生得
236 --比较少,通常只会发生在并发度非常高的SQL里。而这些并发连接,在大量地使用需要每次做编译的
237 --动态TSQL语句发生得比较多。
238 --这个等待一般不是因为内存数量少,而是同时申请的人太多。所以解决的方法不是增加内存,而是修改
239 --客户连接的行为,尽可能更多地使用存储过程,或者是参数化的TSQL语句调用,减少语句的编译量,
240 --增加执行计划的重用,避免大量连接同时申请内存做语句编译
241 SELECT spid,
242 blocked,
243 waittype,
244 DB_NAME(dbid)AS dbname,
245 open_tran,hostname,
246 program_name,
247 hostprocess,
248 cmd,
249 loginame
250 FROM sys.sysprocesses
251 WHERE waittype=0x00B9
252 ORDER BY spid ,dbid ASC
253
254 --select * from sys.sysprocesses 的waittype字段显示0x40或0x0040(resource_semaphore) 等待资源
255
256 --SOS_RESERVEDMEMBLOCKLIST(0x007B)
257 --当用户发过来的语句内含有大量的参数,或者有一个很长的"in"的子句,他的执行计划在8KB的single pages
258 --可能会放不下,需要用multi-page来存储。所以SQL需要在MEMTOLEAVE申请空间。造成的后果是随着缓存
259 --的执行计划越来越多,不但buffer pool里的stolen内存不断增长,memtoleave里用来存储执行计划的stolen
260 --内存也在不断增长。由于在32位buffer pool可以比memtoleave大很多,所以buffer pool还没有压力,
261 --lazy writer不会被触发。但是memtoleave里的内存已经比较紧张了。当用户要申请这块内存而暂时不能得到
262 --满足时,他的等待状态就是SOS_RESERVEDMEMBLOCKLIST
263 --解决这种等待方法有三种:
264 --(1)避免使用这种带有大量参数,或者“in”子句的语句。这种语句的运行需要消耗比正常语句多得多的内存
265 --以及CPU资源,不是一种合理的设计。可以先把参数值存储到临时表里,用join临时表代替直接引用这些值。
266 --这是一种从根本上解决这种问题的方法
267
268 --(2)扩展memtoleave的大小,推迟SQL遇到瓶颈的时间
269
270
271 --(3)定期运行DBCC freeproccache语句,手工清除缓存的执行计划,缓解内存瓶颈。虽然没有第一种理想,
272 --但是在现实使用中,效果还不错,对SQL整体性能的影响不大
273
274 --type:USERSTORE_SXC:暂时存放正在执行中的语句的参数。例如,客户调用存储过程,或者用sp_executesql
275 --调用动态TSQL语句时,需要带入过程参数。如果参数过长,这部分内存的使用量就会比较大
276
277
278
279
280 --RESOURCE_SEMAPHORE_QUERY_COMPILE(0x011A)
281 --当一个batch或存储过程非常长和复杂的时候,SQL编译他所需要的内存可能超过你的想象。
282 --为了防止太多内存被用来做编译,SQL为编译内存设了一个上限。当有太多复杂的语句同时
283 --在做编译时,可能编译内存使用会达到这个上限。后面的语句将不得不进入等待状态,等前面
284 --的语句编译完成,把内存释放出来以后,后面的语句才能继续编译。这个等待状态被称为
285 --RESOURCE_SEMAPHORE_QUERY_COMPILE SEMAPHORE(信号量)
286 --解决办法:
287 --是前两种的综合:
288 --(1)修改客户连接的行为,尽可能更多地使用存储过程,或者是使用参数化的TSQL语句调用,
289 --减少语句编译量,增加执行计划的重用,避免大量连接同时申请内存做语句编译的现象
290
291
292 --(2)简化每次需要编译的语句的复杂度,降低编译需要的内存量
293
294 --(3)当stolen内存使用总量比较大的时候,也可以考虑定期运行DBCC freeproccache语句,
295 --手工清除缓存的执行计划,保证stolen内存一直处在一个比较富裕的状态
296
297
298 --* DBCC FREEPROCCACHE
299 --从过程缓冲区删除所有元素
300
301
302 --虽然stolen内存没有database pages那么多,但也是SQL正常运行不可缺少的重要部分
303
304 --在64位系统,max server memory的设置仅对buffer pool起作用
305 --memtoleave默认是384,如果/g512 启动参数,那么memtoleave就是512+0.5*最大线程=640
306
307 --一般SQL根据负载量开启线程,大部分SQL的线程数目比256要少,如果地址空间被大量占用,-
308 --SQL可能无法再创建新线程,其中一个明显特征就是,新用户很难登入SQL
309
310 --使用大量multi-page的原因,multi-page只有384MB
311 --(1)SQL内部
312 --1大量参数或者长“in”
313
314 --2network packet size设成8KB或更高,而这种连接成百上千
315 --SQL默认的network packet size是4KB,这样每个连接的输入输出缓存都可以放在buffer pool里。
316 --在SSMS登录窗口,选项》 -》网络-》网络数据包大小:4096字节
317 --如果调高到8KB,或更高,每个网络包加上包头、包尾就要超过8KB。SQL只能把他们缓存在multi-pages里面
318 --如果连接非常多,会造成multi-pages大量使用
319
320 --有些应用例如:SAP .NET应用等,需要提高网络交互效率
321
322
323 --SQL的确提供了比较丰富的XML处理功能,但是类似的功能在应用程序也可以通过直接调用一些XML的API
324 --解决。那么是放在SQL里,还是放在应用程序里做呢,需要设计者平衡一下。放在SQL里做可能比较方便
325 --但是如果处理的XML串比较长,很复杂,那么处理所要花费的资源是比较昂贵的,内存只是其中一部分,
326 --这个SQL还有其他用户要服务,那么难免会影响到用户的响应速度。
327
328 --(2)非SQL自己的代码
329 --SQL CLR
330 --LINKED SERVER
331 --EXEC sys.sp_OACreate 调用COM对象
332 --扩展存储过程EXEC sys.xp_cmdshell
333
334 --对multi-page来讲,只要用户调用,SQL就只好申请,所以从SQL的角度没有什么积极的办法。
335 --使用/g 参数或者 升级到64位系统
336
337
338 --常见内存错误和解决办法:
339 --主要有三类,这些内存错误跟内存瓶颈不同,有内存瓶颈SQL会发起很多paging 和lazy writer,但是
340 --内存错误轻则某些操作不能完成,重则整个SQL没有响应,不仅仅是性能问题
341
342 --1、OOM
343 --701错误:基本在32位机器,跟stolen内存有关,发现地址空间可供申请
344 --1、memtoleave地址段没有连续空间可供使用
345 --2、第三方代码申请太多内存没有释放掉
346 --SQL CLR
347 --LINKED SERVER
348 --EXEC sys.sp_OACreate 调用COM对象
349 --扩展存储过程EXEC sys.xp_cmdshell
350 --由于这些代码不是SQL自带的,所以在SQL这里做调整是很难解决问题的
351
352 --3、buffer pool里的stolen内存申请不到
353 --常见的会使用很多stolen内存的memory clerk有如下几个:
354
355
356
357
358 --17803错误
359 --解决方法:
360 --使用/g 参数扩大memtoleave的大小,延缓问题发生频率
361 --定期重启SQL,延缓问题发生频率
362 --升级SQL到64位
363 --前两种不是长久之计
364
365 --memoryclerk_sqloptimizer和cachestore_phdr:语句指令做编译时使用的内存,如果用户一次发过来的批指令太长,这段内存使用会较多
366
367 --memoryclerk_sqloreservation:语句运行的时候在内存里存储临时数据的地方
368 --cachestore_sqlcp:缓存动态TSQL语句执行计划的地方。记得释放游标很unprepare语句
369
370 --user_tokenperm:缓存用户的安全上下文。SQL在buffer pool没有压力的时候通常是64位机器可以涨到上百MB。在SQL2005中
371 --维护这么大的安全上下文缓存会影响SQL的整体性能
372
373 --objectstore_lock_manager:SQL里的锁结构使用的内存。SQL里有严重的阻塞问题,一些用户申请了大量的锁却不释放
374 --而另一些用户想要申请大量的锁却拿不到,造成锁的数目上百万,这时这段内存的使用量会很多
375
376
377
378
379
380 --2、语句运行时没能及时申请到内存,最常见的错误是8645
381 --这个错误通常发生在一个需要申请内存做排序或者hash等操作的查询里,在规定时间里没有能得到足够内存
382 --申请的内存基本都是buffer pool里的内存
383
384 --SQL内存本身有压力,现在再发过来哪怕很小的内存申请数也不能满足
385 --用户突然发来一个或几个需要大量内存非常复杂的语句,一下子把SQL内存资源搞得非常紧张
386
387
388 --现象:
389 --login failed
390 --select * from sys.sysprocesses 的waittype字段显示0x40或0x0040(resource_semaphore) 等待资源
391 --性能计数器:sql mamager:memory grants pending对象值不为0
392
393
394 --解决办法:
395 --避免其他程序把SQL内存侵占
396 --收集buffer manager 和memory manager性能计数器
397 --检查一下SQL内存参数
398 --max server memory
399 --min server memory
400 --awe enabled
401 --min memory per query
402 --lock page in memory
403
404 --检查各个memory clerk的内存申请
405 --检查工作负荷,例如:并发会话数,当前执行的查询,有可能的话开启sqltrace
406
407 --为SQL提供更多内存:
408 --移开占用资源的应用程序到别的服务器
409 --设置max server memory
410 --运行下面DBCC命令释放SQL内存缓存
411 DBCC freesessioncache
412 DBCC freeproccache
413
414
415
416 --找出使用内存比较多的语句,简化他们,调整应用程序行为,减少工作负荷
417 --检查动态管理视图,了解每个查询资源信号量的状态信息。(SQL里默认有两个查询资源信号量,分别处理复杂度不一样
418 --的查询,这样的设计有助于防止几个超大的查询把整个SQL资源用尽,连一些很简单的查询都不能响应的现象发生)
419
420 --检查语句是:
421 SELECT CONVERT(VARCHAR(30),GETDATE(),121) AS runtime,
422 resource_semaphore_id,
423 target_memory_kb,
424 total_memory_kb,
425 available_memory_kb,
426 granted_memory_kb,
427 used_memory_kb,
428 grantee_count,
429 waiter_count,
430 timeout_error_count
431 from sys.dm_exec_query_resource_semaphores
432
433 --resource_semaphore_id:资源信号量的非唯一ID,0表示常规资源信号量,1表示小型查询资源信号量
434 --target_memory_kb:该资源信号量能够授予使用的内存目标,也就是当前的使用上限
435 --total_memory_kb:资源信号量现在所持有的总内存,是可用内存和被授予内存的和。如果系统内存不足或频繁强制缩小内存,该值可以
436 --大于target_memory_kb值,但意味着这个资源信号量有内存压力
437 --available_memory_kb:可用于新授予的内存
438 --granted_memory_kb:授予的总内存
439 --used_memory_kb:授予内存中实际使用的部分
440 --grantee_count:内存授予得到满足的活动查询数
441 --waiter_count:等待内存授予得到满足的查询数,如果不为0,意味着内存压力存在
442 --timeout_error_count:自服务器启动以来的超时错误总数,对于小型查询资源信号量,该值为null
443
444
445
446 --检查sys.dm_exec_query_memory_grants,返回已经获得内存授予的查询的有关信息,或依然在等待内存授予的查询的
447 --有关信息。无须等待就获得内存授予的查询将不会出现在此视图中。所以对一个没有内存压力的SQL,这个视图应该
448 --是空的
449
450 SELECT GETDATE() AS runtime,
451 session_id,
452 scheduler_id,
453 dop,
454 request_time,
455 grant_time,
456 requested_memory_kb,
457 granted_memory_kb,
458 used_memory_kb,
459 timeout_sec,
460 query_cost,
461 resource_semaphore_id,
462 wait_order,
463 is_next_candidate,
464 wait_time_ms,
465 REPLACE(REPLACE(CAST(s2.text AS VARCHAR(4000)),CHAR(10),''),CHAR(13),'') AS sql_statement
466 FROM sys.dm_exec_query_memory_grants
467 CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS s2
468
469 --session_id:正在运行查询的会话ID(spid)
470 --scheduler_id:正在计划查询的SQL Processor调度的ID
471 --dop:查询的并行度
472 --request_time:查询请求内存授予的日期和时间
473 --grant_time:向查询授予内存的日期和时间。如果尚未授予内存,则此值为null
474 --requested_memory_kb:请求的内存总量
475 --granted_memory_kb:实际授予的内存总量。如果尚未授予内存,该值为null。在典型情况下,该值应该与requested_memory_kb相同
476 --创建索引时,除了初始授予的内存外,服务器还允许增加按需分配的内存
477 --used_memory_kb:此刻使用的物理内存
478 --query_cost:估计查询开销
479 --timeout_sec:查询放弃内存授予请求前的超时时间
480 --resource_semaphore_id:查询正在等待的资源信号量的非唯一ID
481 --wait_order:等待查询在指定的queue_id中的顺序,如果其他查询获得内存授予或超时,则给定查询的该值可以更改。如果已授予内存,则为null
482 --is_next_candidate:下一个内存授予的候选对象:1:是 0:否 null:已授予内存
483 --wait_time_ms:等待时间。如果已经授予内存,则为null
484 --plan_handle:查询计划的标志符。使用sys.dm_exec_query_plan可提取实际的xml计划
485 --sql_handle:查询的TSQL文本标志符。查询中使用他链接sys.dm_exec_sql_text获取实际的TSQL文本
486
487
488
489 --3、SQL无法创建新线程供新连接使用
490 --当SQLSERVER上所有的进程都被用户请求占据,而又有新的客户端发出连接请求时,SQL需要创建一个新的线程
491 --来响应这个请求。如果连接创建不出来,会出现SQL断断续续地连接不上的现象,已经在SQL里的连接还能继续工作
492 --只要有线程创建不出来的问题发生,SQL errorlog里就会有记录
493
494 --17802错误
495 --17189错误
496
497 --解决办法:
498 --(1):检查SQL使用了多少线程,是不是的确到了上限
499 --运行下面的查询,检查有多少个KPID<>0的SPID。当一个SPID的KPID不为0的时候,就说明他正在使用线程
500 --运行中。再加上SQL自己运行所需要的线程(一般10到20个),就差不多是SQL使用的线程数目
501 SELECT COUNT(*) FROM sys.sysprocesses WHERE kpid<>0
502
503 --(2):如果线程的数目远小于设置的最大数,那就要考虑是不是memtoleave有压力了
504 --由于线程使用的是memtoleave的内存,确认SQL还有足够的memtoleave
505 SELECT type ,
506 SUM(virtual_memory_reserved_kb) AS [vm reserved] ,
507 SUM(virtual_memory_committed_kb) AS [vm commited] ,
508 SUM(awe_allocated_kb) AS [awe allocated] ,
509 SUM(shared_memory_reserved_kb) AS [sm reserved] ,
510 SUM(shared_memory_committed_kb) AS [sm committed] ,
511 SUM(single_pages_kb) AS [singlepage allocator],
512 SUM(multi_pages_kb) AS [multi page allocated]
513 FROM sys.dm_os_memory_clerks
514 GROUP BY type
515 ORDER BY type