zoukankan      html  css  js  c++  java
  • Fake SQL Tuning, v$session, etc...

    随便起了个名字,因为不知道叫啥好。主要是想把最近遇到的一个事情简单记录下,仅此而已。为啥叫Fake SQL Tuning, 自然不是真正地谈SQL优化,因为我也说不出来啥玩意,毕竟SQL优化不是简单的一两句话就可以说出个所以然的。 

    最近遇到个问题,我负责维护的一个产品的用户说他们有一个process老是跑不完,卡在那里。这个产品前端用PB做,中间部分计算引擎用C++来写,还有一大部分的东东是放在数据库里面,通过PL/SQL来运行。跑一个Process就是在PB界面里面调用C++ DLL提供的接口,然后中间一部分过程又要跑到数据库里面来计算。卡的地方恰恰是在数据库里面执行的那一部分。

    1st Round:

    最开始,通过客户提供的AWR报表,定位到最time-consuming的SQL,因此就想当然地把这个SQL改改,然后丢给用户让他再试一下。该SQL还是蛮复杂的,而且它是在PL/SQL的运行过程中动态“拼接”而成,不是个相对静态的SQL,为了便于“有针对性地优化”,我把这个SQL显示地“hardcode"出来,而不是根据程序逻辑判断来动态拼接起来。我做的改动其实就是对SQL进行简单地改写,加了些hint, 改变表连接方式,由原来的Nested-Loop改成了Hash Join,(use_hash, blahhhhhh....)。 因为没有跟客户相当的环境,因此也没有怎么测试,而且主观态度上也没有想去解决这个问题,因为手头上还有其他乱七八糟的事情要做。

    结果,很快用户给反馈,说现在跑process不是跑不完的问题,而是跑着跑着就跑出了'unable to extend temp segment' 云云错误!! 我晕,居然把临时表空间给爆掉了! 我当时也没有细想,就跟那个客户说,你是不是临时表空间太小了,没有autoextend 啊。 给出了一些不痛不痒的建议,比如增大pga的大小啊之类,其实我也参考了Roger Schrag 写的一篇文章 What You Can Do When Your Database Runs out of Temp Space 。这篇文章提到了1652 事件,后来我开启了这个事件,发现导致临时表空间爆满的SQL就是我改的那条SQL。因为我用hint促使了CBO选择了hash join而不是nested-loop join,因此是会耗掉更多的内存,数据量太大,肯定是multi-pass的,造成临时表空间激增也是可以理解的。

    2nd Round:

    没有实际的运行环境来进行“盲调”看来是压根就行不通的,因此向客户要了他们数据库的dump文件,并导入等我的虚拟机里面的一个数据库里面。杯具的是,我的虚拟机硬盘30G的空间几乎要爆满了,导完dump之后,也就剩下5G左右剩余空间! 

    导完之后,我就开始run process, 果然没过多久,process就失败了,查看Log给出的错误信息就是临时表空间无法增长,我一看磁盘使用情况,god..剩下不到20M的空间!! 临时表空间果然增长速度惊人! 因为有限的磁盘空间,我就把当前临时表空间给drop掉,重新创建一个临时表空间,大小设置为4G,并且关闭自动增长的属性(autoextend off). 我也把之前改的SQL给改了回来,我要看看究竟process卡在什么地方。于是,我就在launch process之后,打开一个session,用来查询v$session的情况。 发现v$session真是包含了超多信息...

     SQL> desc v$session

     Name                                      Null?    Type
     
    ----------------------------------------- -------- -------------------------
     SADDR                                              RAW(4)
     SID                                                
    NUMBER
     SERIAL#                                            
    NUMBER
     AUDSID                                             
    NUMBER
     PADDR                                              
    RAW(4)
     
    USER#                                              NUMBER
     USERNAME                                           
    VARCHAR2(30)
     
    COMMAND                                            NUMBER
     OWNERID                                            
    NUMBER
     TADDR                                              
    VARCHAR2(8)
     LOCKWAIT                                           
    VARCHAR2(8)
     STATUS                                             
    VARCHAR2(8)
     SERVER                                             
    VARCHAR2(9)
     
    SCHEMA#                                            NUMBER
     SCHEMANAME                                         
    VARCHAR2(30)
     OSUSER                                             
    VARCHAR2(30)
     PROCESS                                            
    VARCHAR2(12)
     MACHINE                                            
    VARCHAR2(64)
     TERMINAL                                           
    VARCHAR2(16)
     PROGRAM                                            
    VARCHAR2(64)
     TYPE                                               
    VARCHAR2(10)
     SQL_ADDRESS                                        
    RAW(4)
     SQL_HASH_VALUE                                     
    NUMBER
     
    SQL_ID                                             VARCHAR2(13)
     
    SQL_CHILD_NUMBER                                   NUMBER
     PREV_SQL_ADDR                                      
    RAW(4)
     PREV_HASH_VALUE                                    
    NUMBER
     PREV_SQL_ID                                        
    VARCHAR2(13)
     PREV_CHILD_NUMBER                                  
    NUMBER
     PLSQL_ENTRY_OBJECT_ID                              
    NUMBER
     PLSQL_ENTRY_SUBPROGRAM_ID                          
    NUMBER
     PLSQL_OBJECT_ID                                    
    NUMBER
     PLSQL_SUBPROGRAM_ID                                
    NUMBER
     MODULE                                             
    VARCHAR2(48)
     MODULE_HASH                                        
    NUMBER
     ACTION                                             
    VARCHAR2(32)
     ACTION_HASH                                        
    NUMBER
     CLIENT_INFO                                        
    VARCHAR2(64)
     FIXED_TABLE_SEQUENCE                               
    NUMBER
     ROW_WAIT_OBJ#                                      
    NUMBER
     ROW_WAIT_FILE#                                     
    NUMBER
     ROW_WAIT_BLOCK#                                    
    NUMBER
     ROW_WAIT_ROW#                                      
    NUMBER
     LOGON_TIME                                         DATE
     LAST_CALL_ET                                       
    NUMBER
     PDML_ENABLED                                       
    VARCHAR2(3)
     FAILOVER_TYPE                                      
    VARCHAR2(13)
     FAILOVER_METHOD                                    
    VARCHAR2(10)
     FAILED_OVER                                        
    VARCHAR2(3)
     RESOURCE_CONSUMER_GROUP                            
    VARCHAR2(32)
     PDML_STATUS                                        
    VARCHAR2(8)
     PDDL_STATUS                                        
    VARCHAR2(8)
     PQ_STATUS                                          
    VARCHAR2(8)
     CURRENT_QUEUE_DURATION                             
    NUMBER
     CLIENT_IDENTIFIER                                  
    VARCHAR2(64)
     BLOCKING_SESSION_STATUS                            
    VARCHAR2(11)
     BLOCKING_INSTANCE                                  
    NUMBER
     BLOCKING_SESSION                                   
    NUMBER
     SEQ#                                               
    NUMBER
     
    EVENT#                                             NUMBER
     EVENT                                              VARCHAR2(64)
     P1TEXT                                             VARCHAR2(64)
     P1                                                 NUMBER
     P1RAW                                              RAW(4)
     P2TEXT                                             VARCHAR2(64)
     P2                                                 NUMBER
     P2RAW                                              RAW(4)
     P3TEXT                                             
    VARCHAR2(64)
     P3                                                 
    NUMBER
     P3RAW                                              
    RAW(4)
     WAIT_CLASS_ID                                      
    NUMBER
     WAIT_CLASS#                                        NUMBER
     WAIT_CLASS                                         VARCHAR2(64)
     WAIT_TIME                                          
    NUMBER
     SECONDS_IN_WAIT                                    
    NUMBER
     STATE                                              
    VARCHAR2(19)
     SERVICE_NAME                                       
    VARCHAR2(64)
     SQL_TRACE                                          
    VARCHAR2(8)
     SQL_TRACE_WAITS                                    
    VARCHAR2(5)
     SQL_TRACE_BINDS                                    
    VARCHAR2(5)

    我比较关注其中的几列,像COMMAND, SQL_ID, WAIT_CLASS,因为我要知道process当前的数据库操作是啥(command), 运行的哪条SQL(SQL_ID), 在等待什么资源(WAIT_CLASS). 关于command列的值,可以参见oracle 文档。 比较常见的command 数值有...

     3  -- SELECT

    47 -- PL/SQL Execute 

    还有 6 -- Update,因为在跑process的时候,大部分时间v$session.comomand都是显示出要么是3,要么是6。 

    刚开始发现process运行到一条SQL语句的时候就卡在那里了,等了半天还是没有动静,一直在run啊run的,通过select * from table(dbms_xplan.display_cursor(...))查询了下该SQL的执行计划,再结合等待事件为"db file sequential read", 在通过如下SQL语句定位到改SQL再等待哪个segmenet,


    SELECT
      segment_name,
      segment_type
    from
       dba_extents
    where
       
    file_id = file# 
    AND
      
    block# BETWEEN block_id and (block_id + blocks - 1)

    file# 和 block# 可以从v$session中的字段P1和P2获得。最后发现等待资源是一个索引段,因此初步想法是给改SQL加上full hint,让优化器选择全表扫描而不是索引扫描操作。再做了上述改动之后,重新启动Process,但是发现并没有多大效果!!! 

    后来仔细瞅了下这条SQL语句,发现这个SQL语句有两个子查询基本上是一模一样的,而且该SQL语句的bottleneck(等待的索引段就是这个子查询中涉及到的表)就存在这个子查询中,因此想到通过WITH字句将该SQL进行重写,抽出公共部分放到WITH字句里面。做了上述改动,重新launch其中的一个process,发现还是蛮有效果的,一个process很快就跑完了! 由于时间很晚,我赶着下班了,所以并没有把所有的process都跑了,就欣欣然就回信给客户,云云怎么一番修改。 

    3rd Round:

    Very frustrated!  第二天回到office, 查看mail, 发现客户说没有任何效果,那个耗时的process依旧没有任何能运行完的征兆!我了个擦擦....我又重新跑了那个process, 发现确实一直卡着...GOD! 这次我一直监视着v$session中SQL_ID的变化,发现之前的那天SQL确实不像之前那么耗时间,但是另外一条SQL come out in the way! 而且这次不是SELECT语句,而是一条UPDATE语句!这条update语句涉及到一张大表和一个GTT(Global Temporary Table)的关联update操作。由于是涉及到临时表,因此在其他session没法查询到该表的数据信息啥的。仔细看了下这条SQL,发现没有什么可以“简化”的余地,不知道如何下手。等待事件显示是对其中那个非临时表的segment的sequential访问。虽然执行计划显示是全表扫描,但是等待事件并不是'db file scattered file'. 一般理解好像是db file sequential read 是对索引段的访问,但是这里看到对普通表的segment访问也会出现这种情况!

    后来,实在不晓得该如何去更改这条update语句,我就突发奇想,反正性能不好,我就索性一不做二不休,把代码改成用PL/SQL来一条一条更新数据!理论上这样做肯定不如用SQL来的快,不用涉及到PL/SQL 与 SQL context的切换操作。不过考虑到我已经黔驴技穷了,不如活马当死马医了。 这样一番更改之后,重新launch process试一下。

    惊喜的是,几个process一一通过了,之前一直跑不出的Process现在只需要3小时48分钟!真是诡异呀!

     

    Sumup:

    这件事情给我的最大感触就是,理论跟现实还是有差距的!

    在SQL“调优”或者是“伪调优” 的时候,要充分运行v$session提供的信息,及时定位到问题所在。 通过dbms_xplan.display_cursor来查看执行计划,找出可能存在的问题。还要“敢想敢做”,不断尝试。

      




    --------------------------------------
    Regards,
    FangwenYu
  • 相关阅读:
    UIButton图文上下对齐
    安装cocoapods
    mac忘记密码的解决办法
    css3圆角边框,边框阴影
    input元素的padding border margin的区别
    css font-family 字体全介绍,5b8b4f53 宋体 随笔
    mysql数据库 thinkphp连贯操作where条件的判断不正确的问题
    php获取客户端ip get_client_ip()
    php session小节
    ajax返回值中有回车换行、空格解决方法
  • 原文地址:https://www.cnblogs.com/fangwenyu/p/1820923.html
Copyright © 2011-2022 走看看