• SQL Server 2008性能故障排查(三)——IO


    接着上一章:CPU瓶颈
    I/O瓶颈(I/O Bottlenecks):
    SQLServer的性能严重依赖I/O子系统。除非你的数据库完全加载到物理内存中,否则SQLServer会不断地把数据库文件从缓存池中搬进搬出,这会引起大量的I/O传输。同样地,日志记录在事务被声明为已提交前必须写入磁盘。最后,SQLServer基于许多原因使用tempdb,比如存储临时结果、排序和保持行版本。所以一个好的I/O子系统是SQLServer性能关键。
    除非数据文件包括tempdb需要回滚事务,否则日志文件是顺序访问的。而数据文件和tempdb是随机访问的。所以作为常规规则,你应该把日志文件与数据文件分离到独立的磁盘中。本文不是关注于如何配置你的I/O设备,但关注于如何识别你的系统是否有I/O瓶颈。在I/O瓶颈被识别之后,你应该重新配置你的I/O子系统。
    如果你的I/O子系统很慢,你的用户将体验得到性能问题,响应时间过慢和因为超时而导致任务失败。
    可以使用以下的性能计数器去识别I/O瓶颈。但是要注意,如果你的收集间隔过短,那么平均值会趋向倾斜于低值那段。比如,很难说明为什么I/O会每60秒涨跌。同时,你也不能仅仅根据一个计数器的值来确定是否有瓶颈。需要通过多个值来反复验证你的想法:
    PhysicalDisk Object:Avg.Disk Queue:物理读写请求锁等待的平均队列值。当该值长期超过2时,你的系统可能存在I/O瓶颈了。
    Avg.Disk Sec/Read:是一个平均秒数,是每秒从磁盘上读取数据的次数,下面是值及其代表意思:
    • 小于10ms ——非常好
    • 10~20ms——OK
    • 20~50ms——慢,需要重视
    • 大于50ms——严重的I/O瓶颈。
    Avg.Disk Sec/Write:与Avg.Disk Sec/Read相对应。
    Physical Disk:%Disk Time:是针对被选定的磁盘忙于读写请求所运行时间的百分数。一般的指标线是大于50%就意味着有I/O瓶颈。
    Avg.Disk Reads/Sec:是读操作在磁盘上的频率。确保这个频率低于磁盘极限的85%,当超过了85%后,访问时间就会以指数速度增加。
    Avg.Disk Writes/Sec:于Avg.Disk Reads/Sec相对应。
    当你使用这些计数器时,你需要对于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
    比如,如果你有一个RAID-1,使用两个物理磁盘,计数器值为:
    Disk Reads/sec 80
    Disk Writes/sec 70
    Avg.Disk Queue length 5
    这样通过公式计算:(80 + (2 * 70))/2 = 110 I/Os 每个磁盘,而你的磁盘等待队列长度等于5/2=2.5。意味着已经到达了I/O瓶颈边界。
    你也可以检查lacth等待来识别I/O瓶颈。这种等待说明当一些页面用于读或者写访问时,同时这些页面在缓冲池中不可用(或者不存在)而造成的物理I/O等待。当页面在缓冲池中找不到时。就会产生一个异步的I/O,然后检查这个I/O的状态。当I/O状态已经被标注为已完成时,此时工作负载趋于平衡。否则,将会等待PAGEIOLATCH_EX 或者PAGEIOLATCH_SH,这根据请求类型而定。可以使用一下DMV来发现I/O闩锁的等待统计信息:
    
    [sql] view plain copy
     print?
    1.    Select  wait_type,   
    2.            waiting_tasks_count,   
    3.            wait_time_ms  
    4.    from    sys.dm_os_wait_stats    
    5.    where   wait_type like 'PAGEIOLATCH%'    
    6.    order by wait_type  
    
    
    
    下面是结果的例子:
    wait_type       waiting_tasks_count  wait_time_ms   signal_wait_time_ms
    -----------------------------------------------------------------------
    PAGEIOLATCH_DT  0                    0                    0
    PAGEIOLATCH_EX  1230                 791                  11
    PAGEIOLATCH_KP  0                    0                    0
    PAGEIOLATCH_NL  0                    0                    0
    PAGEIOLATCH_SH  13756                7241                 180
    PAGEIOLATCH_UP  80                   66                   0
    当I/O完成时,工作线程将被至于可运行队列。I/O完成到工作线程确实被排程的时间在signal_wait_time_ms列中可以看到,如果你的waiting_task_counts和wait_time_ms有偏离常值,证明有I/O问题。对于这种情况,有必要在SQLServer运行正常时,建立性能基线和关键的DMV查询输出。这些等待类型能显示出你的I/O子系统是否有严重的瓶颈。但它们不提供任何可见的物理磁盘问题
    你可以通过下面的DMV查询来找到目前正在挂起的I/O请求。你可以定期执行下面语句来检查I/O子系统的健康情况和隔离那些有I/O瓶颈的物理磁盘:
    [sql] view plain copy
     print?
    1.    select   
    2.        database_id,   
    3.        file_id,   
    4.        io_stall,  
    5.        io_pending_ms_ticks,  
    6.        scheduler_address   
    7.    from    sys.dm_io_virtual_file_stats(NULL, NULL)t1,  
    8.            sys.dm_io_pending_io_requests as t2  
    9.    where   t1.file_handle = t2.io_handle  
    
    
    
    
    下面是一个输出例子,是对特定的数据库输出,在运行查询的时刻,有3个被挂起的I/O请求。你可以使用database_id 和file_id列来查找文件所映射的物理磁盘。Io_pending_ms_ticks值表示单个I/O在挂起队列中等待的总时间。
    
    
    Database_id File_Id io_stallio_pending_ms_ticksscheduler_address
    -------------------------------------------------------------
    6 1 10804780x0227A040
    6 1 10804780x0227A040
    6 2 101451310x02720040
    
    
    
    
    解决方案:
    当你发现有I/O瓶颈时,你第一本能反应可能是升级I/O子系统,以应对目前的工作负载。这种方式当然有效,但是在此之前,你要考虑在硬件投入上的开销,要检查I/O瓶颈是否因为不正确的配置和/或查询计划导致的。我们建议你根据以下步骤去检查:
    1、 配置(Configuration):检查SQLServer的内存配置。如果SQLServer配置中存在内存不足的问题,这会引起更多I/O开销。你可以检查下面的计数器来识别是否存在内存压力:
     Buffer Cache hit ratio
     Page Life Expectancy
     Checkpoint Pages/sec
     Lazywrites/sec
    关于内存压力将在内存篇详细说明
    2、 查询计划:检查执行计划和识别哪步导致了更多的I/O消耗。尽可能选择更好的方法比如索引来最小化I/O。如果存在丢失索引,可以使用DTA来找到。
    下面的DMV查询可以用于发现批处理或者请求产生最多的I/O的查询。注意这里不统计物理写,如果你懂得数据库是如何运作的,就会知道为什么。在同一个请求中DML和DDL语句,不是直接把数据页写入磁盘,而只有已经提交的事务才会被写入磁盘。通常物理写只在checkpoint或者lazywriter发生时才出现。可以使用下面的DMV来查找产生最多I/O的5个查询,优化这些查询以便实现更少的逻辑读,并进一步缓解缓存池的压力。这样你能提高其他请求在缓存池中直接找到数据的机会(特别在重复执行时),从而替代物理I/O的性能。因此,这个系统的性能都能得到改进。
    下面是通过hash join来做两表关联的例子:
    [sql] view plain copy
     print?
    1.    create table t1 (c1 int primary key, c2 int, c3 char(8000))  
    2.       create table t2  (C4 int, c5 char(8000))  
    3.    go  
    4.      
    5.      
    6.       --load the data  
    7.    declare @i int  
    8.    select @i = 0  
    9.    while (@i < 6000)   
    10.    begin  
    11.        insert into t1 values (@i, @i + 1000, 'hello')  
    12.       insert into t2 values (@i,'there')  
    13.       set @i = @i + 1  
    14.    end  
    15.    --now run the following query  
    16.    select c1, c5  
    17.    from t1 INNER HASH JOIN t2 ON t1.c1 = t2.c4  
    18.    order by c2   
    19.      
    20.      
    21.    Run another query so that there are two queries to look at for I/O stats  
    22.      
    23.      
    24.    select SUM(c1) from t1  
    
    
    这两个查询在一个批处理中运行,接下来。使用下面的DMV来检查查询引起的I/O:
    
    
    [sql] view plain copy
     print?
    1.    SELECT TOP 5   
    2.        (total_logical_reads/execution_count) AS avg_logical_reads,  
    3.        (total_logical_writes/execution_count) AS avg_logical_writes,  
    4.        (total_physical_reads/execution_count) AS avg_phys_reads,  
    5.        execution_count,   
    6.        statement_start_offset as stmt_start_offset,   
    7.        (SELECT SUBSTRING(text, statement_start_offset/2 + 1,  
    8.            (CASE WHEN statement_end_offset = -1   
    9.                THEN LEN(CONVERT(nvarchar(MAX),text)) * 2   
    10.                    ELSE statement_end_offset   
    11.                END - statement_start_offset)/2)  
    12.         FROM sys.dm_exec_sql_text(sql_handle)) AS query_text,   
    13.          (SELECT query_plan from sys.dm_exec_query_plan(plan_handle)) as query_plan  
    14.    FROM sys.dm_exec_query_stats    
    15.    ORDER BY (total_logical_reads + total_logical_writes)/execution_count DESC  
    
    
    
    
    你当然可以通过改变查询语句来获得不同的数据显示。比如你可以按(total_logical_reads + total_logical_writes)/execution_count 来排序。作为选择,你可能想去按物理I/O来排序等,但是,逻辑读写书对判断是否有I/O问题是很有用的。输出类似这样:
    avg_logical_reads    avg_logical_writes   avg_phys_reads       
    -----------------    ------------------   ---------------
    16639 10    1098
    6023 0    0
    execution_count      stmt_start_offset
    ---------------      -----------------
    1 0
    1 154
    
    
    Query_text      Query_plan                        
    -----------------------------------          -----------
    select c1, c5  from t1 INNER HASH JOIN<link to query plan>
    select SUM(c1) from t1                       <link to query plan>
    
    
    这些输出告诉你一些重要的信息,第一,显示最多的I/O。你也可以通过SQL Text列来查看是否可以通过重写语句来降低I/O。验证这些执行计划是否已经最佳的。比如,一个新的索引可能有帮助。第二、第二个批处理不引起任何物理I/O因为所有需要的表的页面已经缓存到缓冲区。第三、执行次数能用于识别是否它是一个一次性查询或者它是否频繁执行,因此需要对此详细考量。
    3、 数据压缩:从2008开始,你能使用数据压缩来降低表和索引的体积。压缩程度完全取决于架构和数据分布。一般情况下,可以达到50~60%的压缩率。一些特殊情况下可以达到90%。意味着当你能压缩到50%时,你已经比较有效地降低了I/O。数据压缩会引起CPU增加。这里有一些策略:
    为什么不把整个数据库压缩?对此,给出一个极端的例子:如果你有一个大表,叫做T,有10页,而整个数据库有1000万页。压缩T没有多大好处。即使SQLServer能把10页压缩到1页,你努力减少数据库的大小,但你可能会造成CPU的负担增加。在现实的工作负载中,不能很明显地作出选择。但是这个例子只是你在压缩前要考虑的情况而已。我们的建议是:在你压缩一个对象之前,使用sp_estimate_data_compression_savings存储过程来评估它的大小、利用情况和预估压缩等信息。注意以下信息:
     对象的大小是否比数据库总体大小小很多,这样的情况不会给你带来太多好处。
     如果对象经常被用于DML或者SELECT操作,你将面临比较大的CPU消耗。特别是在CPU已经存在瓶颈时,你可以使用sys.dm_db_index_operational_stats去发现对象使用情况来判断表、索引、分区等等的命中情况。
     压缩预留情况是基于架构和基于数据的。实际上,一些对象,压缩后可能会更大。或者节省的空间会微不足道。
    如果你有一个分区表,且某些分区的数据不经常访问。你可以使用页压缩来压缩分区和重组索引。这适用在不长使用的分区上。相信信息可以看:(http://blogs.msdn.com/sqlserverstorageengine/archive/tags/Data+Compression/default.aspx)
    4、 升级I/O子系统:如果你确保SQLServer的配置合理,并且检查执行计划后仍然存在I/O瓶颈,最后的选择就只能升级I/O带宽了:
     增加更多的物理驱动或者更换更快的磁盘。
     增加更快的I/O控制器。
    
    
    下一章:tempdb
  • 相关阅读:
    SharePoint 2013 图文开发系列之自定义字段
    SharePoint 2013 图文开发系列之Visual Studio 创建母版页
    SharePoint 2013 图文开发系列之代码定义列表
    SharePoint 2013 图文开发系列之计时器任务
    SharePoint 2013 图文开发系列之应用程序页
    SharePoint 2013 图文开发系列之事件接收器
    SharePoint 2013 图文开发系列之可视化WebPart
    SharePoint 2013 图文开发系列之WebPart
    SharePoint 2013 对二进制大型对象(BLOB)进行爬网
    SharePoint 2013 状态机工作流之日常报销示例
  • 原文地址:https://www.cnblogs.com/binghou/p/9109452.html
Copyright © 2020-2023  润新知