• SQL Server2005性能调优(简化翻译版) ZT


    2006年12月11日 17:00 - (阅读:6556;评论:9)

    名次解释

    DMVs:dynamic management views

    三个点

    · 资源瓶颈: CPU、内存、I/O(这里面不考虑网络问题)

    · Tempdb瓶颈:

    · User query瓶颈,可能是统计信息的变化、不恰当的索引、阻塞或者死锁等

    上述三点,可能是相互影响的。

    资源瓶颈

     

    工具

    1. System Monitor (PerfMon):windows自带

    2. SQL Server Profiler: 2005继续有

    3. DBCC commands: 参考联机文档

    4. DMVs: 见上名次解释

     

    CPU瓶颈

    CPU瓶颈,是突然并且不可预料的。一般来讲,没有优化的查询计划、系统低配置、设计不合理等,很容易导致这些问题。

    在perfmon中,我们一般需要监视Processor:% Processor Time,如果每个CPU持续高于80%,CPU就是瓶颈了。当然,在强大的2005下我们也可监视sys.dm_os_schedulers ,如果有内容,表明有任务等待CPU来分配给它。如下面这个DMVs的查询:

    select scheduler_id,current_tasks_count,runnable_tasks_count from sys.dm_os_schedulers where scheduler_id < 255

    下面的查询,更高级点。分析方法是,看结果的number_of_statements,如果该值大于1,说明可能有问题,要进一步分析。

    select top 50 sum(qs.total_worker_time) as total_cpu_time, sum(qs.execution_count) as total_execution_count,count(*) as number_of_statements, qs.plan_handle from sys.dm_exec_query_stats qs group by qs.plan_handle order by sum(qs.total_worker_time) desc

    执行计划的编译与重新编译

    在sql2005中的一个改进,就是对于某个sp,进行recompile的时候,只需要针对改变的部分进行编译,sql2000只能是全部都搞一遍。

    Recompile的原因很多,如:

    · Schema的变更 changed

    · Statistics变更

    · 延迟编译

    · SET option的执行

    · 临时表的变化

    · Sp使用了RECOMPILE提示或者使用了OPTION (RECOMPILE)

     

    诊断方法,老朋友了,继续使用perfmon或者sql profiler。

    对于perfmon,监视下面的 计数器

    · SQL Server: SQL Statistics: Batch Requests/sec

    · SQL Server: SQL Statistics: SQL Compilations/sec

    · SQL Server: SQL Statistics: SQL Recompilations/sec

    对于profiler抓到的trace,分析这几个event:SP:Recompile / SQL:StmtRecompile / Showplan XML For Query Compile。如果我们抓到了trace,对于文件,可以这么做:

    select spid,StartTime,Textdata,EventSubclass,ObjectID,SQLHandle from fn_trace_gettable ( 'e:\recompiletrace.trc' , 1) where EventClass in(37,75,166)

    这里面,EventClass 37 = Sp:Recompile, 75 = CursorRecompile, 166=SQL:StmtRecompile

    如果你事先保存到了 table jq中,那么把上面的from修改为from jq即可。

    或者使用这个DMVs: sys.dm_exec_query_optimizer_info(注意一个技巧!多执行几次,看中间的差异)

    select * from sys.dm_exec_query_optimizer_info

    另外一个DMVs是:sys.dm_exec_query_stats如执行这个sql:

    select * from sys.dm_exec_query_stats CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS s2

    另外, plan_generation_num标示出了被recompile的所有query。如下面这个

    select top 25 sql_text.text,sql_handle,plan_generation_num, execution_count,dbid,objectid from sys.dm_exec_query_stats a cross apply sys.dm_exec_sql_text(sql_handle) as sql_text where plan_generation_num >1 order by plan_generation_num desc

    解决办法

    · 如果因为使用了Set option,那么通过profiler来观察是什么引起的变化。

    · 如果因为临时表,那么尽量使用表变量,而不是临时表。(对于表变量的限制,请查看联机文档)另一个解决办法,使用KEEP PLAN查询提示,这会把临时表当作普通表一样对待,会进行statistics的跟踪

    · 关闭对于索引或者索引视图上的状态自动更新(偶个人不建议)

    · 对象名称写全了,如dbo.Table1

    · 尽量少用延迟编译。如你的SP或者query里面,有N多的if/else之类的。

    · 运行索引调优向导(sql2000里面就有)

    · 看看sp是不是使用了WITH RECOMPILE来建立的,或者RECOMPILE查询提示。

     

    弱智的查询计划

    每个查询执行之前,sqlserver都会“试图”优化一个最快的查询计划出来。注意的是,这里的最快的,不代表I/O最小,也不代表CPU占用最小。它是一个权衡后的值。

    对于Hash join或者sort等,它们都是与CPU密切相关的。对于nested loop,很可能会因为大量的index lookups,导致I/O迅速上涨。如果search的数据散落在各个pages里面,很可能会导致缓冲命中率下降。

    诊断方法

    使用这个DMVs: sys.dm_exec_query_stats,它可以有效地监视CPU的使用情况。

    select highest_cpu_queries.plan_handle, highest_cpu_queries.total_worker_time,q.dbid,q.objectid,q.number,q.encrypted,q.[text] from (select top 50 qs.plan_handle, qs.total_worker_time from sys.dm_exec_query_stats qs order by qs.total_worker_time desc) as highest_cpu_queries cross apply sys.dm_exec_sql_text(plan_handle) as q order by highest_cpu_queries.total_worker_time desc

    解决办法

    · 使用优化向导调优

    · 检查一下,WHERE条件是不是限制的足够好?

     

    游标问题

    强烈建议,尽量减少使用游标。可以使用perfmon监视SQL Server:Cursor Manager By Type – Cursor Requests/Sec。或者使用DMVs:

    select cur.* from sys.dm_exec_connections con cross apply sys.dm_exec_cursors(con.session_id) as cur where cur.fetch_buffer_size = 1 and cur.properties LIKE 'API%'

    如果使用profiler,可以监视sp_cursorfetch(前提是包含了RPC:Completed这个event class)

    内存瓶颈

    对于VAS和AWE概念,请自行查找MSDN。我印象中,M$的人强烈建议不要在32bit windows上面使用AWE或者3BG之类的东西。

    检测内存问题

    打开taskmgr看物理内存中的Avaiable,如果持续低于10M,恭喜你,系统内存压力太大!通过perfmon,监视Memory: Available Mbytes,一样的效果。

    对于AWE使用,可以用这个DMVs来看:

    select sum(awe_allocated_kb) / 1024 as [AWE allocated, Mb]  from sys.dm_os_memory_clerks

    对于虚拟内存等,可以观察Commit Charge中的Total,与limit的对比。如果两者很接近,虚拟内存可能不够了。如果你好奇,想看看sqlserver自己的内存分配,可以执行DBCC MEMORYSTATUS。具体内容,见联机文档。

    对于内存问题,偶认为采用sql的默认设置,一般情况下足够了。

    I/O瓶颈

    除非操作系统能够,并且内存足够大,把你的db放到物理内存里,否则,I/O我们永远回避不过去。使用perfmon的话,可以监视

    · PhysicalDisk Object: Avg. Disk Queue Length,如果经常性的大于2*磁盘个数,磁盘有性能问题。

    · Avg. Disk Sec/Read,如果<10ms,很好。20以下,一般。50以下,密切观察。50以上,换硬盘吧!

    · Avg. Disk Sec/Write,这个和上面的两个值,如果持续大于物理磁盘的指标的85%,说明磁盘已经到极限了。

    · Physical Disk: %Disk Time,一般如果超过50%,I/O有瓶颈。

    如果用了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个磁盘,raid1,监测到的结果:

    Disk Reads/sec            80

    Disk Writes/sec           70

    Avg. Disk Queue Length    5

    那么I/O平均是80/2+70=110,队列长度上限是2*2=4

    解决办法

    · 检查sqlserver的内存配置

    · 增加或者替换更快的硬盘,读写缓存越高越好

    · 检查执行计划,找到I/O大的地方。如这个DMVs

    select top 50 (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, s2.text plan_handle from sys.dm_exec_query_stats cross APPLY sys.dm_exec_sql_text(sql_handle) AS s2 order by  (total_logical_reads + total_logical_writes) Desc

     

    小TIP,如果要清除缓存的作用,执行这个:

    checkpoint

    dbcc freeproccache

    dbcc dropcleanbuffers

    TempDB
       每个实例只有一个tempdb,所以这里很可能成为性能或者磁盘空间的瓶颈。

       常见的tempdb问题如下:

       · 把磁盘空间用光了 

       · 因为tempdb的瓶颈,导致I/O很差。参见第一部分。 

       · DDL带来的对系统表的瓶颈 

       · 内容分配

       

       诊断问题之前,先看看tempdb是如何利用空间的。

       用户对象

          · 表和索引 

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

          · 局部临时表和索引(#t1) and index. 

             · 当前连接的 

             · 存储过程内的 

          · 表变量(同上) 

       内部对象

          · Work file (hash join) 

          · Sort run 

          · Work table (cursor, spool和临时大对象)

       版本存储

          2005新增的

       空闲空间

          tempdb暂时没有用到的磁盘剩余空间.

       整个tempdb就是上述4个东西的和。

       监视tempdb剩余空间很简单,监测这个指标即可。Free Space in tempdb (KB)。下面这个DMVs很强大的说,上面四个都能看到。

       Select SUM (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_kb  From sys.dm_db_file_space_usage  Where database_id = 2

       这是一个输出结果(kb表示的)

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

    分析空间使用问题
    用户对象

       跑这个,能看出来到底谁干的。

       DECLARE userobj_cursor CURSOR FOR 
       select sys.schemas.name + '.' + sys.objects.name  from sys.objects, sys.schemas where object_id > 100 and  type_desc = 'USER_TABLE'and 
    sys.objects.schema_id = sys.schemas.schema_id  
       go 
       open userobj_cursor 
       go 
       
       declare @name varchar(256) 
       fetch userobj_cursor into @name 
       while (@@FETCH_STATUS = 0) 
          begin 
             exec sp_spaceused @objname = @name 
             fetch userobj_cursor into @name 
          end 
       close userobj_cursor

    版本存储

       select top 2 transaction_id, transaction_sequence_num, elapsed_time_seconds from sys.dm_tran_active_snapshot_database_transactions order by elapsed_time_seconds DESC

       这里显示一个带有XSN3的事务(ID是8609),已经激活了6523秒。

       transaction_id transaction_sequence_num elapsed_time_seconds 
       
    -------------------- ------------------------ -------------------- 
       8609 3 6523 
       20156 25 783

       Kill掉第一个trans,我们可以释放iding数量的version store。但是,没有办法能够估计出来,kill掉之后,究竟能释放多少。

    内部对象

       SQL Server 2005提供了两个DMVs: sys.dm_db_session_space_usage和 asys.dm_db_task_space_usage,用以跟踪sessions和tasks在tempdb中的空间变化。

    select session_id, internal_objects_alloc_page_count, internal_objects_dealloc_page_count from sys.dm_db_session_space_usage order by internal_objects_alloc_page_count DESC

       再如这个

       SELECT t1.session_id, (t1.internal_objects_alloc_page_count + task_alloc) as allocated, (t1.internal_objects_dealloc_page_count + task_dealloc) as deallocated from sys.dm_db_session_space_usage as t1, (select session_id, sum(internal_objects_alloc_page_count) as task_alloc, sum (internal_objects_dealloc_page_count) as task_dealloc from sys.dm_db_task_space_usage group by session_id) as t2 where t1.session_id = t2.session_id and t1.session_id >50 order by allocated DESC

       这里一个示例输出

       session_id allocated deallocated 
       
    ---------- -------------------- -------------------- 
       52 5120 5136 
       51 16 0

       对于tasks,可以执行下面的sql

       select t1.session_id, t1.request_id, t1.task_alloc, t1.task_dealloc, t2.sql_handle, t2.statement_start_offset, t2.statement_end_offset, t2.plan_handle from (Select session_id, request_id, sum(internal_objects_alloc_page_count) as task_alloc, sum (internal_objects_dealloc_page_count) as task_dealloc from sys.dm_db_task_space_usage group by session_id, request_id) as t1, sys.dm_exec_requests as t2 where t1.session_id = t2.session_id and (t1.request_id = t2.request_id) order by t1.task_alloc DESC

       session_id request_id task_alloc task_dealloc 
       --------------------------------------------------------- 
       52 0 1024 1024

       sql_handle statement_start_offset 
       -----------------------------------------------------------------------    0x02000000D490961BDD2A8BE3B0FB81ED67655EFEEB360172 356

       statement_end_offset plan_handle 
       --------------------------------- 
       -1 0x06000500D490961BA8C19503000000000000000000000000

       然后通过sql_handle和plan_handle,就可以得到sql的语句

       select text from sys.dm_exec_sql_text(@sql_handle) 
       select * from sys.dm_exec_query_plan(@plan_handle)

     

    龟速查询

    阻塞和索引问题,是常见的导致sql以龟速执行的罪魁。

    阻塞
    阻塞主要等待逻辑锁,如请求一个X锁。关于锁的信息,遍地都是,msdn或者google都可以。
    SQL Server 2005提供了125中等待类型(2000是76种)。

    假设我们sp_who看到了一个block在56号上,那么通过这个可以看到详细信息

    select * from sys.dm_os_waiting_tasks where session_id=56

    (在2000下,你可以通过dbcc inputbuffer(56)来看当前执行的文本)

    0x022A8898 56  0  1103500   LCK_M_S  0x03696820  0x022A8D48  53  NULL  ridlock fileid=1  pageid=143 dbid=9 id=lock3667d00 mode=X  associatedObjectId=72057594038321152

    这里显示,56被53阻塞,并且等待了1103500毫秒了。

    通过使用sys.dm_tran_locks,我们可以看到56被53以X模式锁住了,53持有1:143:3这个资源。

    select request_session_id as spid, resource_type as rt,  resource_database_id as rdb,  (case resource_type WHEN 'OBJECT' then object_name(resource_associated_entity_id) WHEN 'DATABASE' then ' ' ELSE (select object_name(object_id)  from sys.partitions  where  obt_id=resource_associated_entity_id)    END) as objname,  resource_description as rd,     request_mode as rm, request_status as rs from sys.dm_tran_locks

    输出如下

    spid     rt           rdb         objname       rd            rm           rs

    ----------------------------------------------------------------------------- 

    56    DATABASE     9                               S          GRANT

    53    DATABASE     9                              S          GRANT

    56    PAGE          9       t_lock      1:143       IS         GRANT

    53    PAGE        9       t_lock      1:143       IX         GRANT

    53    PAGE          9       t_lock      1:153       IX         GRANT

    56   OBJECT       9       t_lock                  IS         GRANT

    53   OBJECT       9        t_lock                 IX         GRANT

    53    KEY         9        t_lock      (a400c34cb X          GRANT

    53    RID         9        t_lock      1:143:3    X          GRANT

    56    RID         9        t_lock      1:143:3    S       WAIT

    而在SQL Server 2000里面,可以从sysprocesses看到。

    select * from master..sysprocesses where blocked <> 0.

    更详细的阻塞信息
    2005中提供了一个新的DMV:Sys.dm_db_index_operational_stats,它提供了针对每个表、索引、分区的详细阻塞情况,如:访问历史、锁、阻塞、waits等。详细信息如下:

    ·         页/行等持有锁的个数

    ·         页/行等锁或waits的个数

    ·         页/行等锁或waits的时间

    ·         页闩的waits个数(闩与hotspot,就是所谓的热点有关,下同)

    ·         页闩的waits时间

    ·         页I/O闩的waits时间

    除了阻塞的信息,还有一些索引的信息。

    ·         访问方式,如某个range,或者lookup

    ·         在叶子层的插入/更新/修改

    ·         叶子层之上的插入/更新/修改,就是索引维护。每个叶子页面的第一行,指向了该层的上一层。假如说在叶子上分配了一个新页面,那么上面那层的页面就包含一个指向该层第一行的指针信息。

     

    在原文的附录B中,包含了一系列的索引信息相关的存储过程。下面是使用该sp的步骤

    1.     使用init_index_operational_stats来初始化indexstats表

    2.     使用insert_indexstats建立一个基线

    3.     运行你的负载

    4.     到合时的实际,使用insert_indexstats捕获索引状态的快照

    5.     跑get_indexstats来分析索引状态。诸如很高的阻塞或者很高的waits,基本可以表明索引有问题。

    这里列出了一些使用上述sp的例子。

    ·         所有数据库中,使用最多的前5个索引

           exec get_indexstats @dbid=-1, @top='top 5',  @columns='index, usage',  @order='index usage'

    ·         锁增长最多的前5个索引

         exec get_indexstats @dbid=-1, @top='top 5', @order='index lock promotions', @threshold='[index lock promotion attempts] > 0'

    ·         递减模式,前5个最大的行锁waits时间的索引状态

           exec get_indexstats @dbid=-1, @top='top 5', @order='avg row lock wait ms'

    ·         前10个,在dbid=5的数据库中,所有阻塞率大于10%的索引状态

           exec get_indexstats @dbid=-1, @top='top 10', @order='block %', @threshold='[block %] > 0.1'

    注:sql2000没有提供任何关于对象或者索引使用状态

    监视索引使用情况
    2005中提供了一个非常有用的DMV:sys.dm_db_index_usage_stats,通过它我们可以找到哪些索引正在被当前query使用,或者没被使用。注意的是,这些数据仅保留在内存中,并没有被持久化存储。所以,如果sql发生down机了,这些数据就都丢掉了。当然,我们可以把这些save到表中,供日后分析。

    索引上的操作分为两种:用户方式和系统方式。一个索引,通过dbid、对象id和索引id三列信息唯一标示。索引id为0的时候,代表一个heap table;1的时候,聚集索引;大于1的时候,非聚集索引。

     

    2005下,seek/scan/lookup的规则与定义如下:

    ·         SEEK: 使用B-tree结构访问数据的次数。

    ·         SCAN: 不使用B-tree结构访问数据的次数。

    ·         LOOKUP: 使用不合适的非聚集索引配合聚集索引来寻找数据,如2000中的书签查找。

    下面这个DMV可以得到“当前”所有数据库所有对象的索引状态。

    select object_id, index_id, user_seeks, user_scans, user_lookups  from sys.dm_db_index_usage_stats  order by object_id, index_id

    假设是下面的结果

    object_id       index_id        user_seeks    user_scans    user_lookups

    ------------      ------------- --------------    --------------  ----------- ------

    521690298         1                  0                 251                 123

    521690298         2                123                 0                     0

     

    该结果表明,有251次的聚集索引scan,123次的书签查找,123次的非聚集索引seek。

     

    如果想知道从上次sql启动之后,到现在为止,某个表中没有被使用过的索引状况,执行下面的sql。

    select i.name from sys.indexes i where i.object_id=object_id('<table_name>') and  i.index_id NOT IN  (select s.index_id  from sys.dm_db_index_usage_stats s  where s.object_id=i.object_id and  i.index_id=s.index_id and database_id = <dbid> )

     

    所有未被使用的索引:

    select object_name(object_id), i.name  from sys.indexes i  where  i.index_id NOT IN (select  s.index_id  from sys.dm_db_index_usage_stats s  where s.object_id=i.object_id and  i.index_id=s.index_id and database_id = <dbid> ) order by object_name(object_id) asc

    结束了 

    原文后面有N多的sp,大家可以参考原文,在你的sql2005上跑一次。当然,能够在客户的生产环境中,用这些sp进行实际测试,会得到更好的体会。

    我个人认为,更好的利用这些DMVs和sp的前提是,对于sql的基本概念要有所了解。索引、锁、阻塞、死锁等,为什么会产生,他们在SqlServer这种数据库下面是如何处理的,等等。否则,看着那些DMVs,很容易发懵。

     

    (原文地址,点这里

  • 相关阅读:
    [LintCode] Maximum Subarray Difference
    [HDU 3415] Max Sum of Max-K-sub-sequence
    [LintCode] Count of Smaller Number before itself
    [LeetCode] Invert Binary Tree
    [LintCode] Max Tree
    [LeetCode] Implement Stack using Queues
    [LintCode] Maximum Subarray III
    [LeetCode] Basic Calculator & Basic Calculator II
    [LeetCode] Rectangle Area
    Tensorflow[目录结构]
  • 原文地址:https://www.cnblogs.com/liangqihui/p/972678.html
Copyright © 2020-2023  润新知