性能分析之子锁存器(latch)到SQL

一箫一剑平生意,负尽狂名十五年。这篇文章主要讲述性能分析之子锁存器(latch)到SQL相关的知识,希望能为你提供帮助。
这几天碰到一个事情,有必要记录一下。
在一个项目中,压力测试工具中一个业务响应时间变长,数据库(oracle)CPU使用率99%以上。

从AWR报告上看到如下信息:

在性能项目的沟通中,经常是在这样的时候,我们就去告诉开发说现在的状态是CPU使用率高,把AWR报告往开发那里一发,性能团队的人员就喝咖啡去了。
但是性能如果只是做到这里,沟通其实没有在同一个界面上。
在这个典型的沟通过程中,上述结果也确实发给开发了。
【性能分析之子锁存器(latch)到SQL】开发反馈说,是因为在场景执行过程中表的数据量发生了变化,导致了SQL的执行计划发生了变化,进而导致了SQL慢,进而导致了latch: cache buffers chains的出现。所以要解决的话,要对数据库进行一次analyze,然后再测试。
在上述的背景中,看似没有缺陷,合情合理。
但是,
恰好碰到我这偏执的人,我要看到证据链。
在沟通的来往中,我并没有看到证据链,这是我觉得不好的地方。哪里没有证据呢?就是数据量发生了变化导致了SQL的执行计划发生了变化。
如果有这样的怀疑,就要去证明这个怀疑是对的。
于是我查了SQL计划的历史变更记录。
根据SQLID查看执行计划的变更记录:
selectDISTINCT  SQL_ID,PLAN_HASH_VALUE,TO_CHAR(TIMESTAMP,yyyymmdd hh24:mi:ss)  TIMESTAMPfrom  dba_hist_sql_plan  where  sql_id=1wfsd2q8wc5uuorderbyTIMESTAMP;
                                SQL_ID    PLAN_HASH_VALUE                          TIMESTAMP
1                              1wfsd2q8wc5uu      4188019746                    2018040118:08:09
查询更详细的执行记录变更记录(我把结果中的时间戳删掉了,为了格式整齐点):
select  plan_hash_value,id,operation,options,object_name,depth,cost,TO_CHAR(TIMESTAMP,yyyymmdd hh24:mi:ss)  from  dba_hist_sql_plan  where  sql_id=1wfsd2q8wc5uuand  plan_hash_value  in  (4188019746)  orderbyID,TIMESTAMP;
PLAN_HASH_VALUE ID OPERATION OPTIONS OBJECT_NAME DEPTH COST
1 4188019746 0 SELECT STATEMENT0 2
2 4188019746 1 FILTER1
3 4188019746 2 FAST DUAL2 2
4 4188019746 3 TABLE ACCESS BY INDEX ROWID TABLE1 2 0
5 4188019746 4 INDEX RANGE SCAN TABLE1_IDX1 3 0
这个SQL的执行计划,只有4月1日一条记录,在最近几天的测试中,并没有发生变更。
这就是我要说的证据链,当给出执行计划发生变化这个结论时,就必须给出证明,而实际的数据证明这个结论是错的。


下面我们就来分析下怎么才是对的。
既然是latch: cache buffers chains,首先我们得知道这个值是什么意思。
简单回忆下latch的原理(如下部分是在网上抄的):
当一个数据块读入到SGA中时,该块的块头(BUFFERHEADER)会放置一个HASHBUCKET的链表(HASHCHAIN)中。该内存结构由一系列cachebuffers chains的子latch保护。对BUFFERCACHE中的块,要SELECT/UPDATE/INSERT/DELETER等操作都得先获得cachebuffers chains的子latch,以保证对CHAIN的排他访问。若在过程中发生争用,就会出现latch:cache buffers chains事件。
这个值的出现有两个含义:

  1. SQL的执行效率低:因为SQL低效,在并发会话时,无法得到相同的数据集,SQL执行时带有高BUFFER_GETS会导致latch争用。
  2. 数据热块。
这是两个完全不同的处理方向。
第一个处理方向,考虑到近期场景执行得比较频繁,数据库变更较多,所以先把数据库做个整体的分析,再来测试下。经过证明之后,发现果然分析了整库之后,时间刷刷的降低了很多,然后就把存储的IO压到80%以上了。
虽然开发说执行计划变更是错的,但是分析整库的处理方法是对的。
套用大话西游里说的:我猜中了结尾,但是没有蒙对过程。
看到这里,是不是觉得问题解决了?可以收工了?很开心的喝咖啡去了?
但是,我又偏执了。我考虑了下参数化的逻辑,我觉得数据热块应该是存在的。现在之所以没有暴露出来,是因为IO跟不上了。所以我要判断下,如果IO够用的话,热块会不会成为下一个瓶颈。于是,接着查。
先看看争用是否严重:
selectround((misses / gets) *  100) ||  %,
              round(100  * immediate_misses /(immediate_gets + immediate_misses))||  %
    from  v$latch
  wherename  =  cache buffers chains;
round((misses / gets) *  100) ||  %的值是3%。可见还是有一些热度的。
再来查下子锁存器视图,看是否有热块。看看倾斜度。


  select  *  from  (select  addr,child#,gets,misses,sleepsfrom  v$latch_children  wherename=cachebuffers chainsorderby  sleeps  desc)  whererownum< =20;

倾斜度是有的。再看下对象的热度:
select  hladdr,
            obj,
            (select  object_name
                    from  dba_objects
                  where  (data_object_id  isnull
                      andobject_id = x.obj)  or  data_object_id = x.obj  andrownum  =  1)  as  object_name, dbablk, tch
    from  x$bh x
  where  hladdr  in  (00000005FCE6EAC0)
  orderby  tch  desc;

TCH是touch count,此值越高,则热度越高。
查找引起热块的SQL。 
  select  *
      from  (selectcount(*),
                              sql_id,
                                nvl(o.object_name, ash.current_obj#) objn,
                                substr(o.object_type,  0,  10) otype,
                              CURRENT_FILE# fn,
                              CURRENT_BLOCK# blockn
                      from  v$active_session_history ash, all_objects o
                    where  event  likelatch: cache buffers chains
                        ando.object_id(+) = ash.CURRENT_OBJ#
                  GROUPBY  SQL_ID,
                                    current_obj#,
                                    current_file#,
                                    current_block#,
                                    o.object_name,
                                    o.object_type
                    orderbycount(*)  desc)
    whererownum  < =  10;

根据SQLID查一下SQL的文本,果然就是我们用到的那个查询业务SQL。


在性能分析中,我们太容易给自己定个范围或圈套了。有时觉得这个事情不该是自己做的。
如果单从职场的角度说,这样想并无不妥。
但是如果从技术角度说,这样想就必然会导致自己的能力受限。
所以不用给技术下个定义,在个人能力能达到的地方,都尽量去做,学无止境。


而从现象到瓶颈的性能分析是最需要一个人有足够的知识宽度的,因为你不知道在寻找瓶颈的过程中会遇到什么样的知识弱点。
今天碰到的是oracle,明天碰到mysql、HBase怎么办?啥也别想,二话不说,办它就对了。

    推荐阅读