笔记32-徐 内存压力分析
笔记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 --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