笔记32-徐 内存压力分析

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 --一、32位服务器开启AWE
21 --二、增长物理内存
22 --三、若是scale up向上扩展不行,考虑scale out向外扩展,把SQL中的多个数据库移到其余服务器上,只留一个应用数据库或者分库处理
23 --四、使用表索引,减小没必要要的读和表扫描
24
25 --一、二、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 --一、32位系统没有开AWE,使用 /g 参数增大了multi-page的上限值
40 --二、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 --一、增长物理内存
55 --二、归档历史数据,把表里的数据降下来
56 --三、调整应用程序设计,避免没必要要的大数据量查询
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 --一、OOM
343 --701错误:基本在32位机器,跟stolen内存有关,发现地址空间可供申请
344 --一、memtoleave地址段没有连续空间可供使用
345 --二、第三方代码申请太多内存没有释放掉
346 --SQL CLR
347 --LINKED SERVER
348 --EXEC sys.sp_OACreate 调用COM对象
349 --扩展存储过程EXEC sys.xp_cmdshell
350 --因为这些代码不是SQL自带的,因此在SQL这里作调整是很难解决问题的
351
352 --三、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 --二、语句运行时没能及时申请到内存,最多见的错误是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 --三、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 typesql

相关文章
相关标签/搜索