SQL Server 2005性能排错白皮书(Part 3)---From MS Customer Support Service部门

每个组件的详细信息可以通过下列语句获取(这包括从缓存池内和缓存池外分配的内存)。

declare @total_alloc bigint declare @tab table (type nvarchar(128) collate database_default ,allocated bigint,virtual_res bigint,virtual_com bigint,awe bigint,shared_res bigint,shared_com bigint,topFive nvarchar(128),grand_total bigint);-- note that this total excludes buffer pool committed memory as it represents the largest consumer which is normalselect@total_alloc = sum(single_pages_kb + multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN virtual_memory_committed_kb ELSE 0 END) + shared_memory_committed_kb)from sys.dm_os_memory_clerks print 'Total allocated (including from Buffer Pool): ' + CAST(@total_alloc as varchar(10)) + ' Kb'insert into @tabselecttype,sum(single_pages_kb + multi_pages_kb) as allocated,sum(virtual_memory_reserved_kb) as vertual_res,sum(virtual_memory_committed_kb) as virtual_com,sum(awe_allocated_kb) as awe,sum(shared_memory_reserved_kb) as shared_res ,sum(shared_memory_committed_kb) as shared_com,case  when  ((sum(single_pages_kb + multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN virtual_memory_committed_kb ELSE 0 END) + shared_memory_committed_kb))/

(@total_alloc + 0.0)) >= 0.05 then type else 'Other' end as topFive,(sum(single_pages_kb + multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN virtual_memory_committed_kb ELSE 0 END) + shared_memory_committed_kb)) as grand_total from sys.dm_os_memory_clerks group by typeorder by (sum(single_pages_kb + multi_pages_kb + (CASE WHEN type <> 'MEMORYCLERK_SQLBUFFERPOOL' THEN virtual_memory_committed_kb ELSE 0 END) + shared_memory_committed_kb)) descselect  * from @tab

注意之前的查询将Buffer Pool与通过单页分配器提供给组件的内存视为不同的部分。通过下列查询可以确定在缓存池中耗费内存最多的10个组件(通过单页分配器)。

-- top 10 consumers of memory from BPoolselect top 10 type, sum(single_pages_kb) as [SPA Mem, Kb]from sys.dm_os_memory_clerksgroup by type order by sum(single_pages_kb) desc

你通常不会控制内部组件对内存的占用。然而,确定哪个组件占用最多的内存可以帮助对问题的后续研究。

系统监视器(Perfmon)

你也可以通过检查下列计数器来确定内存压力(SQL Server联机丛书中有详细地描述):

SQL Server: Buffer Manager 对象

◆Low Buffer cache hit ratio
◆Low Page life expectancy
◆High number of Checkpoint pages/sec
◆High number Lazy writes/sec

不足的内存和I/O开销经常会导致瓶颈。请查看本文的I/O 瓶颈部分。

高速缓存和内存压力

查看外部和内部内存压力的方法是查看内存中高速缓存的行为。

SQL Server 2005与SQL Server 2000的高速缓存设计上有一些细微的不同,其中之一就是统一了高速缓存的框架。为了从高速缓存中删除最近很少使用的项,该框架实现了一套时钟算法。现在它使用2支不同的时钟指针,一个是内部时钟指针,一个是外部时钟指针。

内部时钟指针控制与其他高速缓存相关的缓存大小。当框架预测到高速缓存要使用到尽头是它开始移动。当SQL Server总体上陷入内存压力时,外部时钟指针开始移动。外部时钟指针的移动可以导致外部和内部的内存压力。在内部和外部内存压力时不会混乱的移动外部时钟和内部时钟。

关于时钟移动的信息可以通过sys.dm_os_memory_cache_clock_hands DMV显示,如下代码所示。每个高速缓存项在内部和外部时钟指针都有不同的行。如果你看到rounds count和removed all rounds count增加,说明服务器遇到内部/外部内存压力。

select *from sys.dm_os_memory_cache_clock_handswhere rounds_count > 0and removed_all_rounds_count > 0

通过如下所示,通过通过sys.dm_os_cache_counters DMV你可以获得更多关于缓存的信息。

select distinct cc.cache_address, cc.name, cc.type,cc.single_pages_kb + cc.multi_pages_kb as total_kb, cc.single_pages_in_use_kb + cc.multi_pages_in_use_kb as total_in_use_kb, cc.entries_count, cc.entries_in_use_count,ch.removed_all_rounds_count, ch.removed_last_round_countfrom sys.dm_os_memory_cache_counters cc join sys.dm_os_memory_cache_clock_hands ch on (cc.cache_address = ch.cache_address)/*--uncomment this block to have the information only for moving hands cacheswhere ch.rounds_count > 0and ch.removed_all_rounds_count > 0*/order by total_kb desc

注意USERSTORE项,正在使用的页面数量将不会被报告,因为结果将是NULL。

Ring buffers

更多有意义的内存调试信息可以通过sys.dm_os_ring_buffers的ring buffers DMV获取。每个ring buffer保留了之前几次某种类型的通知。指定ring buffer的详细信息将在下面描述。RING_BUFFER_RESOURCE_MONITOR

你可以使用从资源监视器的通知识别内存改变的状态。在内部,SQL Server有一个监视不同内存压力的架构。当内存状态改变,资源监视器任务生成一个通知。这个通知用于内部组件根据内存状态调整它们内存使用并通过sys.dm_os_ring_buffers DMV来暴露,如下列代码所示。

select record from sys.dm_os_ring_buffers where ring_buffer_type = 'RING_BUFFER_RESOURCE_MONITOR'

结果类似于:

<Record id="1701" type="RING_BUFFER_RESOURCE_MONITOR" time="149740267"><ResourceMonitor><Notification>RESOURCE_MEMPHYSICAL_LOW</Notification>

<Indicators>2</Indicators> <NodeId>0</NodeId> </ResourceMonitor><MemoryNode id="0"><ReservedMemory>1646380</ReservedMemory>

<CommittedMemory>432388</CommittedMemory> <SharedMemory>0</SharedMemory> <AWEMemory>0</AWEMemory> <SinglePagesMemory>26592</SinglePagesMemory>

<MultiplePagesMemory>17128</MultiplePagesMemory> <CachedMemory>17624</CachedMemory>

</MemoryNode><MemoryRecord><MemoryUtilization>50</MemoryUtilization> <TotalPhysicalMemory>3833132</TotalPhysicalMemory>

<AvailablePhysicalMemory>3240228</AvailablePhysicalMemory> <TotalPageFile>5732340</TotalPageFile> <AvailablePageFile>5057100</AvailablePageFile>

<TotalVirtualAddressSpace>2097024</TotalVirtualAddressSpace> <AvailableVirtualAddressSpace>336760</AvailableVirtualAddressSpace>

<AvailableExtendedVirtualAddressSpace>0</AvailableExtendedVirtualAddressSpace> </MemoryRecord></Record>

从这些记录来看,你可以减少服务器收到的低物理内存的通知。你也可以查看内存总量(KB为单位)。你可以通过使用SQL Server的XML能力来查询这些信息,例如下列代码。

select x.value('(//Notification)[1]', 'varchar(max)') as [Type],x.value('(//Record/@time)[1]', 'bigint') as [Time Stamp],x.value('(//AvailablePhysicalMemory)[1]', 'int') as [Avail Phys Mem, Kb],x.value('(//AvailableVirtualAddressSpace)[1]', 'int') as [Avail VAS, Kb]from (select cast(record as xml)from sys.dm_os_ring_buffers where ring_buffer_type = 'RING_BUFFER_RESOURCE_MONITOR') as R(x)order by[Time Stamp] desc


上面收到了低内存的通知,缓存池重新计算target。注意target数量保持在指定的min server memory 和max server memory选项限制中。如果缓存池中心的提交比当前的提交缓存还小,缓存池将开始收缩直到外部内存压力被移除。注意,当运行在启用AWE时SQL Server 2000不是这样来缓解物理内存压力的。

RING_BUFFER_OOM

下列代码示例,展示了ring buffer中包含预示服务器内存不足的记录。

select record from sys.dm_os_ring_buffers where ring_buffer_type = 'RING_BUFFER_OOM'

结果类似于:

<Record id="7301" type="RING_BUFFER_OOM" time="345640123"><OOM><Action>FAIL_VIRTUAL_COMMIT</Action> <Resources>4096</Resources> </OOM>

这条记录告诉我们那个操作失败了(提交,保留或页面分配)并表明了请求的内存数量。

RING_BUFFER_MEMORY_BROKER and Internal Memory Pressure

当监测到内部内存压力时,为组件在缓存池分配内存的低内存通知将被打开。打开低内存通知允许从使用缓存池的高速缓存和其他组件中回收页面。内部内存压力可以通过调整max server memory选项或当stolen页面与缓存池的比例超过80%时触发。内部内存压力通知(‘Shrink’)能通过使用下列代码查询ring buffer的调用来发现。

selectx.value('(//Record/@time)[1]', 'bigint') as [Time Stamp],x.value('(//Notification)[1]', 'varchar(100)') as [Last Notification]from(select cast(record as xml)from sys.dm_os_ring_buffers where ring_buffer_type = 'RING_BUFFER_MEMORY_BROKER') as R(x)order by[Time Stamp] desc

RING_BUFFER_BUFFER_POOL

ring buffer将包含预示严重的缓存池失败的记录,包括缓存池溢出的条件。

select record from sys.dm_os_ring_buffers where ring_buffer_type = 'RING_BUFFER_BUFFER_POOL'

结果类似于:

<Record id="1234" type="RING_BUFFER_BUFFER_POOL" time="345640123">< BufferPoolFailure id="FAIL_OOM"><CommittedCount>84344 </CommittedCount>

<CommittedTarget>84350 </CommittedTarget > <FreeCount>20</FreeCount> <HashedCount>20345</HashedCount> <StolenCount>64001 </StolenCount> <ReservedCount>64001 </ReservedCount> </ BufferPoolFailure >

这条记录告诉我们出现了什么样的失败 (FAIL_OOM, FAIL_MAP,FAIL_RESERVE_ADJUST,FAIL_LAZYWRITER_NO_BUFFERS) 基当时的缓存池状态。

内部虚拟内存压力

VAS的占用可以使用sys.dm_os_virtual_address_dump DMV来跟踪。VAS汇总可以使用下列视图来查询。

-- virtual address space summary view-- generates a list of SQL Server regions-- showing number of reserved and  ree regions of a given size CREATE VIEW VASummary

ASSELECTSize = VaDump.Size,Reserved =  SUM(CASE(CONVERT(INT, VaDump.Base)^0) WHEN 0 THEN 0 ELSE 1 END),Free = SUM(CASE(CONVERT(INT, VaDump.Base)^0) WHEN 0 THEN 1 ELSE 0 END)FROM(--- combine all allocation according with allocation base, don't take into--- account allocations with zero

allocation_baseSELECT CONVERT(VARBINARY, SUM(region_size_in_bytes)) AS Size, region_allocation_base_address AS BaseFROM sys.dm_os_virtual_address_dump WHERE region_allocation_base_address <> 0x0GROUP BY region_allocation_base_address UNION  --- we shouldn't be grouping allocations with zero allocation base--- just get

them as isSELECT CONVERT(VARBINARY, region_size_in_bytes), region_allocation_base_addressFROM sys.dm_os_virtual_address_dump WHERE region_allocation_base_address  = 0x0)AS VaDumpGROUP BY Size 

下列查询可以用于访问VAS状态:

-- available memory in all free regionsSELECT SUM(Size*Free)/1024 AS [Total avail mem, KB] FROM VASummary WHERE Free <> 0-- get size of largest availble regionSELECT

CAST(MAX(Size) AS INT)/1024 AS [Max free size, KB] FROM VASummary WHERE Free <> 0

如果最大可用区域小于4MB,我们可能遇到了VAS压力。SQL Server 2005监视和响应VAS压力。SQL Server 2000不会监视从VAS带来的压力,但是当出现虚拟内存不足错误是,它会清理高速缓存。

一般的内存错误排错步骤

下列是一些常规步骤,有助于你排除内存错误。

1.验证是否服务器运行在外部内存压力。如果出现外部内存压力,尝试先解决它,然后再看是否依然存在这个问题或错误。
2.开始收集性能计数器:SQL Server: Buffer Manager, SQL Server: Memory Manager
3.确认内存配置参数(sp_configure), min memory per query,min/max server memory,awe enabled和  Lock Pages in Memory 权利。观察不正常的值。纠正配置。提供为SQL
Server 2005增加内存的理由。
4.检查所有可能影响服务器的非默认sp_configure参数。
5.检查内部内存压力。
6.当你见到内存错误消息时,观察DBCC MEMORYSTATUS输出和改变的方法。
7.检查负载(并发会话数量,并发执行查询数量)。

内存错误

701 - There is insufficient system memory to run this query.

原因

这是服务器内存不足的典型现象。它预示了内存分配失败。它可以有多种原因导致,包括在当前工作负载上的内存提示。通过增加SQL Server 2005需要的内存和一些配置上的设置(例如max server memory选项)用户可能会看到这种错误。通常失败的事务不是因为这个错误。

排错

不论是否错误是持续的和可重复的(同样状态)或是随机的(显示为随机的不同状态),当你看到这个错误时你需要研究服务器内存分布。当这个错误出现时,可能导致诊断查询失败。可以开始从外部进行评估。接下来的步骤在一般的内存错误排错步骤中描述。

可能的解决方案包括:除去外部内存压力;增加max server memory设置。使用下列语句释放缓存DBCC FREESYSTEMCACHE,DBCC FREESESSIONCACHE,或 DBCC FREEPROCCACHE。如果问题还是出现,则应减少工作负载。

802 - There is insufficient memory available in the buffer pool.

原因

这个错误不是引起内存不足的必要条件。它可能预示缓存池内存被其他的一些组件使用。在SQL Server 2005中很少出现。

排错

使用常规的排错步骤和701错误的建议。

8628 - A time out occurred while waiting to optimize the query. Rerun the query.

原因

这个错误指出查询编译过程失败因为它不能获取完成编译所需的内存。当查询经历了这种编译过程,包括解析,代数求解和优化,它的内存需求会增加。因而查询将和其他的查询争夺内存资源。如果查询超过的预定的超时时间(查询增加内存的占用) 当等待资源时,这个错误会返回。这种情况的最可能的原因是在服务器上出现很多大型查询的编译。

排错

1.在常规的排错步骤后查看是否服务器内存占用正常。
2.检查负载。确认被不同组件占用的内存数量(具体信息请参考内部物理内存压力)。
3.检查DBCC MEMORYSTATUS输出的在每个gateway上的等待数量(这些信息将告诉你是否有其他的查询在占用大量内存)。

Small Gateway                  Value------------------------------ --------------------Configured Units               8Available Units                8Acquires                       0Waiters                        0Threshold

Factor               250000Threshold                      250000(6 row(s) affected)Medium Gateway                 Value------------------------------ --------------------Configured Units               2Available

Units                2Acquires                       0Waiters                        0Threshold Factor               12(5 row(s) affected)Big Gateway                    Value------------------------------ --------------------

Configured Units               1Available Units                1Acquires                       0Waiters                        0Threshold Factor               8


4.尽可能减少工作负载。

8645 - A time out occurred while waiting for memory resources to execute the query. Rerun the query.

原因

这个错误指出在服务器上有很多内存敏感的查询被同时执行。使用排序(ORDER BY)和连接的查询可以在执行过程中会占用大量的内存。如果有高度并行或如果查询操作一个非联合索引的分区表时,查询内存需求会有很大的增加。无法或取所需内存资源的查询在预先确定的超时时间时(默认,超时是25倍的评估查询时间或sp_configure 设置的’query wait’)将收到这个错误。通常,收到这个错误的查询占用内存的那个查询。

排错

1.根据一般步骤评估内存的情况。
2.识别有问题的查询:确认有大量的在分区表上的查询,检查是否他们使用非联合索引,检查是否有大量包括连接和排序的查询。
3.检查sp_configure参数中degree of parallelism 和min memory per query 。尝试降低并行度并验证是否min memory per query没有设置很高的值。如果设置了很高的值,即使少量
的查询也会占用指定查询的数量。
4.为了找到查询是否等待RESOURCE_SEMAPHORE,请查看本白皮书中阻塞的部分。

8651 - Could not perform the requested operation because the minimum query memory is not available. Decrease the configured value for the 'min memory per query' server

configuration option.

原因

原因和8645错误类似;它也预示服务器内存过低。min memory per query太高也可能导致这个错误。

排错

1.根据一般内存错误排错步骤。
2.验证sp_configure min memory per query 选项设置。

I/O 瓶颈

SQL Server性能非常依赖于I/O子系统。除非你的数据库适合物理内存,SQL Server经常地会有数据库页面进出缓存池。这样就发生了实质的I/O流量。同样,在事务被明确的提交前,日志记录需要写入磁盘。SQL Server为各种目的可以使用tempdb,例如存储中间结果,排序,保持行的版本或其他。所以好的I/O子系统对于SQL Server性能非常重要。

除了当事务需要回滚时,访问日志文件是非常频繁的,而包括访问数据文件,包括tempdb,是随机访问的。所以作为一个通常的规则,为获取更好的性能,你最好将日志文分布不到不同的物理磁盘。本文重点于不是在如何配置你的I/O设备,而是描述识别你的I/O瓶颈的方法。一旦识别了I/O瓶颈,你也许需要重新配置你的I/O子系统。

如果你有一个慢速的I/O子系统,你的用户会遇到性能问题,例如很长的响应时间,任务由于超时而中断。

你可以使用下列性能参数来识别I/O瓶颈。注意,如果你的收集间隔过长,这些平均值可能会倾向于过小。例如,很难通过60秒一次的快照获取正确的I/O值。所以,你不能依赖于一个计数器来确定瓶颈;要考虑多个技术起来反复校验。

◆PhysicalDisk Object: Avg. Disk Queue Length表现在采样周期中所选择的物理磁盘队列中的物理读和写平均请求数量。如果你的I/O系统过载,更多的读/写操作将被等待。如果在很少使用SQL Server时,你的磁盘队列长度经常超过2,这样你可能遇到了I/O瓶颈
◆Avg. Disk Sec/Read 是平均每次从磁盘读取数据的时间

小于10 ms – 很好
在 10 - 20 ms 之间- 正常
在20 - 50 ms 之间- 缓慢,需要注意
大于 50 ms – 严重的I/O 瓶颈

◆Avg. Disk Sec/Write 是平均每次从磁盘读取数据的时间。请引用之前的指导数据。
◆Physical Disk: %Disk Time 是所选磁盘驱动器用于服务于读或写请求的总共时间的百分比。一般推荐是如果该值大于50%,则表现为I/O瓶颈。
◆Avg. Disk Reads/Sec 表现磁盘上读操作的速度。你需要确认该值小于85%的磁盘设计能力。磁盘访问时间指数高于能力的85%。
◆Avg. Disk Writes/Sec表现在磁盘上写操作的速度。确认该值小于85%的磁盘设计能力。磁盘访问时间指数高于能力的85%。

当使用以上计数器,你在使用RAID配置时,你需要使用下列公式调整结果值。

Raid 0 -- I/Os per disk = (reads + writes) / number of disks
Raid 1 -- I/Os per disk = [reads + (2 * writes)] / 2
Raid 5 -- I/Os per disk = [reads + (4 * writes)] / number of disks
Raid 10 -- I/Os per disk = [reads + (2 * writes)] / number of disks

例如,你有带有2个物理磁盘的RAID-1系统和下列计数器值。

Disk Reads/sec            80
Disk Writes/sec           70
Avg. Disk Queue Length    5

这种情况下,你遇到了(80 + (2 * 70))/2 = 110 I/Os per disk,你的磁盘队列长度为5/2=2.5,表明系统接近于I/O瓶颈。

你也可以通过考察锁等待来识别I/O瓶颈。当数据页通过读或写访问并且在缓存池中页不可用时,这些锁等待占用了大量的物理I/O等待。当页面没有在缓存池中找到时,一个异步I/O请求被发出,I/O的状态是被选中的。如果I/O已经完成,工作进程处理正常。否则,依赖于请求的类型,它会等待PAGEIOLATCH_EX 或 PAGEIOLATCH_SH。从下列的DMV查询可以用于找到I/O锁等待的统计。

Select  wait_type, waiting_tasks_count, wait_time_msfrom sys.dm_os_wait_stats  where wait_type like 'PAGEIOLATCH%'  order by wait_typewait_type       waiting_tasks_count 

wait_time_ms   signal_wait_time_ms-----------------------------------------------------------------------PAGEIOLATCH_DT  0                    0                    0PAGEIOLATCH_EX  1230                 791    

             11PAGEIOLATCH_KP  0                    0                    0PAGEIOLATCH_NL  0                    0                    0PAGEIOLATCH_SH  13756                7241                

180PAGEIOLATCH_UP  80                   66                   0


下划线的是有兴趣的锁等待。当I/O完成时,工作者被放置到可用队列中。从I/O完成到工作者被实际的调度的时间在signal_wait_time_ms列说明。如果你得waiting_task_counts and wait_time_ms偏离正常值太多,你可以识别为I/O问题。为了这样,通过使用性能计数器和关键DMV建立正常时运行时的性能基线就十分重要。wait_types可以识别是否你的I/O子系统处在瓶颈状态,但是他们不能提供任何关于物理磁盘遇到问题的信息。

你可以使用下列DMV查询找到当前等待的I/O请求。你可以周期性的执行这些请求检查I/O子系统的健康情况,并找到涉及I/O瓶颈的物理磁盘。

select database_id, file_id, io_stall,io_pending_ms_ticks,scheduler_address from sys.dm_io_virtual_file_stats(NULL, NULL)t1,sys.dm_io_pending_io_requests as t2where t1.file_handle = t2.io_handle


下面是示例的输出。它展示当时在给定的数据库上有3个未决定的I/O 。你可以使用database_id 和 file_id 来找到映射的物理磁盘文件。

io_pending_ms_ticks 表现了所有等待在未决定队列中的个别的I/O。

Database_id File_Id io_stall io_pending_ms_ticks scheduler_address
----------------------------------------------------------------------
6  1  10804  78   0x0227A040
6  1  10804  78   0x0227A040
6  2  101451 31   0x02720040

解决

当你识别到I/O瓶颈时,你可以下列方法解决:

◆检查SQL Server的内存配置。如果SQL Server配置的内存不足,将导致更多的I/O开销。你可以考察下列计数器来识别内存压力。
◆Buffer Cache hit ratio
◆Page Life Expectancy
◆Checkpoint pages/sec
◆Lazywrites/sec

更多关于内存压力的信息,请查看内存瓶颈。

◆增加I/O带宽。
◆为当前的磁盘阵列添加更多的物理驱动器和/或使用更快的磁盘代替当前的磁盘。这帮助提升读写访问时间。但是添加的磁盘数量不能比当前I/O控制器所支持的数量大。
◆添加快速或额外的I/O控制器。考虑为当前的控制器添加更多的缓存(如果有可能)
◆考察执行计划并查看那个计划占用了更多的I/O。这样可以找到更好的查询进化(例如,索引)可以减少I/O。如果缺少索引,你可以运行Database Engine Tuning Advisor找到缺
失的索引。

下列DMV查询能用于找到那个批处理/请求生成了最多的I/O。你将注意到我们没有说明物理写入。如果你了解数据库如何工作,这也没有问题。请求中的DML/DDL并不是直接将数据页写入磁盘。只有通过提交事务语句才能触发将页面物理写入磁盘。通常物理写通过Checkpoint语句完成或通过SQL Server lazy writer完成。下列DMV查询可以用于找到生成

最多I/O的5个请求。调整这些查询,使他们使用较少的逻辑度来完成操作,这样可以减少在缓存池上的压力。这允许其他的查询在缓存池中找到重复执行中所需要的重复数据(而不是通过物理I/O完成)。因此整体系统性能被提高了。

select top 5 (total_logical_reads/execution_count) as avg_logical_reads,(total_logical_writes/execution_count) as avg_logical_writes,(total_physical_reads/execution_count) as avg_phys_reads,Execution_count, statement_start_offset as stmt_start_offset, sql_handle, plan_handlefrom sys.dm_exec_query_stats  order by (total_logical_reads + total_logical_writes) Desc


当然你可以改变查询或取数据的不同视角。例如,在一个查询中声称最多I/O请求的5个请求,你可以通过下列表达式排序:

(total_logical_reads + total_logical_writes)/execution_count

除此之外,你可能希望通过物理I/O排序或其他的操作。然而,逻辑读/写数量来帮助决定是否查询选择的计划被优化过。例如,它可以做一个表扫描而不是使用索引。一些查询,例如使用嵌套循环连接可能有很高的逻辑计数器但是能更好的适应缓存,因为他们重新访问了同样的页面。

例如:让我们考察下列2个有2个SQL查询的批,每个表由1000行并且行大小大于8000(每页1行)。

批-1
select
c1,
c5
from t1 INNER HASH JOIN t2 ON t1.c1 = t2.c4
order by c2

批-2
select * from t1

为了这个示例,在运行DMV查询前,我们利用下列命名清理了缓存池和过程的缓存。

checkpoint
dbcc freeproccache
dbcc dropcleanbuffers

这是DMV查询的输出。你将注意到2行表现的这2个批。

Avg_logical_reads Avg_logical_writes Avg_phys_reads Execution_count stmt_start_offset --------------------------------------------------------------------------------------2794                1               

385                1                     0      1005                0                0                  1                     146    sql_handle                                         plan_handle--------------------------------------------------------------

----

0x0200000099EC8520EFB222CEBF59A72B9BDF4DBEFAE2B6BBx0600050099EC8520A86198030000000000000000000000000x0200000099EC8520EFB222CEBF59A7

2B9BDF4DBEFAE2B6BB x0600050099EC8520A8619803000000000000000000000000

你将注意到第2个批只发生了逻辑读但是没有物理I/O。这是因为需要的数据已经由第1个查询缓存到了高速缓存中(有充分的内存)。

你可以通过下列查询或取执行查询的文本。

select text from sys.dm_exec_sql_text(0x0200000099EC8520EFB222CEBF59A72B9BDF4DBEFAE2B6BB)Here is the output.select c1, c5from t1 INNER HASH JOIN t2 ON t1.c1 = t2.c4order by c2


你也可以通过下列语句找到独立语句的字符串。

select substring(text, (<statement_start_offset>/2),(<statement_end_offset> -<statement_start_offset>)/2)  from sys.dm_exec_sql_text  (0x0200000099EC8520EFB222CEBF59A72B9BDF4DBEFAE2B6BB)


statement_start_offest 和statement_end_offset的值需要除以2来补偿SQL Server使用Unicode来存储这种数据的。statement_end_offse值为-1指出语句先前到了批的最后。而substring()函数不识别-1,并将其作为非法值。使用(<statement_end_offset> -<statement_start_offset>)/2,代替-1,一个需要输入64000的值,来确认语句覆盖了所有的情况。通过这种方法,长时间运行或资源消费语句可以被过滤出大型的存储过程或批。

同样的你可以运行下列查询,找到查询计划确定是否当选择了较差的计划时有大量的I/O。

select * from sys.dm_exec_query_plan (0x0600050099EC8520A8619803000000000000000000000000)

Tempdb

Tempdb用于全局存储内部或用户对象,临时表,对象和在SQL Server操作是创建的存储过程。每鯯QL Server 实例只有1个单一的tempdb。它可能是一个性能和磁盘空间的瓶颈。有限可用空间和过多的DDL/DML会使Tempdb超过负载。这能导致运行在同一个服务器中的其他无关应用变得运行缓慢或失败。

下面列出一些tempdb的常规问题:

◆Tempdb磁盘空间不足
◆由于tempdb中的I/O瓶颈,导致查询运行缓慢。这在I/O瓶颈部分提到过。
◆过多DDL操作导致系统表的瓶颈
◆分配争夺

Before we start diagnosing problems with tempdb, let us first look at how the space in tempdb is used. It can be grouped into four main categories.当开始调试tempdb问题前,让我们先看一下在tempdb中如何使用空间。它可以分为4个主要的类别。

用户对象
 这些对象被用户会话显示创建并在系统目录中被跟踪。这包括:

      表和索引

      全局临时表(##t1)和索引

     本地临时表(#t1)和索引

     会话范围

     存储过程范围内创建

     表变量(@t1).

     会话范围

     存储过程范围内创建

 
 内部对象
 这有语句范围的对象,通过SQL Server处理的查询创建和销毁。这些对象不能被系统目录跟踪。这包括:

     工作文件(hash join)

     排序

     工作表 (游标, 池 和临时大对象数据类型 (LOB)存储 ) 作为优化,当工作表被删除,一个IAM页和一个扩展被保存到一个新的工作表。这有2个例外:临时LOB存储是批范围的和游标工作表示会话范围的版本存储用于存储行版本。MARS,在索引因操作,触发器和快照隔离级别都是基于行版本。这是SQL Server 2005中新的特性。
 
可用空间
 这表示在tempdb数据库可用的磁盘空间。
tempdb使用的总空间等于用户对象加内部对象加版本存储+可用空间。这个可用空间等于性能计数器中tempdb的可用空间。监视tempdb空间阻止问题更好的方法是随后马上解决它。你可以使用下列性能监视器来监视使用中的tempdb空间。

◆Free Space in tempdb (KB).这个计数器以KB为单位跟踪空闲空间的数量。管理员可以使用这个计数器确定是否tempdb运行在较低的磁盘空间上。

然而,识别前面所定义的不同类别如何使用在tempdb中使用的磁盘空间,是很有兴趣的并有效率的。

下列查询返回了tempdb用户使用的空间和内部组件对象。当前它提供了仅有tempdb中的信息。

SelectSUM (user_object_reserved_page_count)*8 as user_objects_kb,SUM (internal_object_reserved_page_count)*8 as internal_objects_kb,SUM (version_store_reserved_page_count)*8  as version_store_kb,SUM (unallocated_extent_page_count)*8 as freespace_kbFrom sys.dm_db_file_space_usageWhere database_id = 2


这里有一些示例的输出(空间使用KB为单位)。

user_objets_kb   internal_objects_kb   version_store_kb   freespace_kb---------------- -------------------- ------------------ ------------8736               128                    64                    448


注意这些技术不包括其中的混合扩展。页面在混合扩展中可以被分配个别用户和内部对象。

磁盘空间问题排错

用户对象,内部对象和存储的版本可以都可以导致在tempdb中的空间问题。这节我们会考虑针对每个类别如何排错。

用户对象

因为用户对象不被特定的会话拥有,你需要理解创建该对象的应用程序的规范并调整需要的tempdb大小。你可以通过执行exec sp_spaceused @objname='<user-object>'来找到个别用户对象使用的。例如,你可以运行下列脚本枚举所有tempdb对象。

DECLARE userobj_cursor CURSOR FOR select sys.schemas.name + '.' + sys.objects.name from sys.objects, sys.schemaswhere object_id > 100 and type_desc = 'USER_TABLE'and sys.objects.schema_id = sys.schemas.schema_idgoopen userobj_cursorgodeclare @name varchar(256)fetch userobj_cursor into @namewhile (@@FETCH_STATUS = 0) beginexec sp_spaceused @objname = @namefetch userobj_cursor into @name endclose userobj_cursor

你可能感兴趣的:(sql server 2005)