催人泪下,拖垮生产系统的超长慢SQL

一.基本信息介绍

dcc_sys_log和dcc_ne_log两表皆无主键
统计信息都已收集
analyze table dcc_sys_log compute statistics for table for all indexes for all indexed columns;
analyze table dcc_ne_log compute statistics for table for all indexes  for all indexed columns;
两表的PEER_ID都为非空列
alter table DCC_SYS_LOG modify PEER_ID  not null;
alter table DCC_NE_LOG  modify peer_id  not null;
表索引情况如下:
create index IDX_DCC_SYS_LOG_PEER on DCC_SYS_LOG (PEER_ID);
create index IDX_DCC_NE_LOG_peer  on DCC_NE_LOG  (PEER_ID);
create index IDX_DCC_NE_LOG_time  on DCC_NE_LOG  (log_time);

SQL语句最终返回记录数特点:该复杂SQL返回记录不超过100条:
一般而言,peer_id为监控主机的标识,一般不超过100个,所以最终复杂SQL的查询结果一般不超过100个,如本案例监控的主机是41个,
最终复杂SQL的查询值就是为41条记录!

数据及记录分布情况
select count(*),
        count(peer_id),
        count(distinct(peer_id)),
        count(case
                when log_time between trunc(sysdate) and sysdate then
                 1
                else
                 null
              end) as current_day,
        count(log_time),
        count(distinct(log_time))
from dcc_ne_log;
 
  COUNT(*) COUNT(PEER_ID) COUNT(DISTINCT(PEER_ID)) CURRENT_DAY COUNT(LOG_TIME) COUNT(DISTINCT(LOG_TIME))
---------- -------------- ------------------------ ----------- --------------- -------------------------
     87154          87154                        7        9016           87154                       380
 
select count(*),
       count(peer_id),
       count(distinct(peer_id)),
       count(log_time),
       count(distinct(log_time))
from dcc_sys_log;
 
  COUNT(*) COUNT(PEER_ID) COUNT(DISTINCT(PEER_ID)) COUNT(LOG_TIME) COUNT(DISTINCT(LOG_TIME))
---------- -------------- ------------------------ --------------- -------------------------
   4899834        4899834                       41         4899834                     27943
   

 
 
 
 
二.逐步提升性能的如下4次改造

原始老脚本及老执行计划

select distinct ne_state.peer_id peer_name,
                         to_char(ne_state.ne_state) peer_state,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (select distinct to_char(nvl(ne_active.active, 0))
                               from dcc_sys_log,
                                    (select peer_id,
                                            decode(action,
                                                   'active',
                                                   1,
                                                   'de-active',
                                                   0,
                                                   0) active,
                                            max(log_time)
                                       from dcc_sys_log
                                      where action = 'active'
                                         or action = 'de-active'
                                      group by (peer_id, action)) ne_active
                              where dcc_sys_log.peer_id = ne_active.peer_id(+)
                                and dcc_sys_log.peer_id = ne_state.peer_id)
                         end) peer_active,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (to_char(nvl((select count(*)
                                           from dcc_ne_log
                                          where dcc_ne_log.result <> 1
                                            and peer_id = ne_state.peer_id
                                            and log_time between
                                                trunc(sysdate) and sysdate
                                          group by (peer_id)),
                                         0)))
                         end) err_cnt,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (to_char(nvl((select count(*)
                                           from dcc_ne_log in_dnl
                                          where in_dnl.direction = 'recv'
                                            and in_dnl.peer_id =
                                                ne_state.peer_id
                                            and log_time between
                                                trunc(sysdate) and sysdate),
                                         0)))
                         end) recv_cnt,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (to_char(nvl((select sum(length)
                                           from dcc_ne_log in_dnl
                                          where in_dnl.direction = 'recv'
                                            and in_dnl.peer_id =
                                                ne_state.peer_id
                                            and log_time between
                                                trunc(sysdate) and sysdate),
                                         0)))
                         end) recv_byte,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (to_char(nvl((select count(*)
                                           from dcc_ne_log in_dnl
                                          where in_dnl.direction = 'send'
                                            and in_dnl.peer_id =
                                                ne_state.peer_id
                                            and log_time between
                                                trunc(sysdate) and sysdate),
                                         0)))
                         end) send_cnt,
                         (case
                           when ne_state.ne_state = 0 then
                            to_char(0)
                           else
                            (to_char(nvl((select sum(length)
                                           from dcc_ne_log in_dnl
                                          where in_dnl.direction = 'send'
                                            and in_dnl.peer_id =
                                                ne_state.peer_id
                                            and log_time between
                                                trunc(sysdate) and sysdate),
                                         0)))
                         end) send_byte
           from dcc_ne_log,
                (select distinct dsl1.peer_id peer_id,
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state
                   from dcc_sys_log dsl1,
                        (select distinct dnl.peer_id peer_id,
                                         decode(action,
                                                'disconnect',
                                                0,
                                                'connect',
                                                0,
                                                1) ne_state
                           from dcc_sys_log dsl, dcc_ne_log dnl
                          where dsl.peer_id = dnl.peer_id
                            and ((dsl.action = 'disconnect' and
                                dsl.cause = '关闭对端') or
                                (dsl.action = 'connect' and
                                dsl.cause = '连接主机失败'))
                            and dsl.log_time =
                                (select max(log_time)
                                   from dcc_sys_log
                                  where peer_id = dnl.peer_id
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+)) ne_state
          where ne_state.peer_id = dcc_ne_log.peer_id(+)

执行计划

 SELECT STATEMENT, GOAL = ALL_ROWS			120155	7	483		
 HASH UNIQUE			6001	119421	6448734		
  MERGE JOIN OUTER			4414	119421	6448734		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421	2866104		
   SORT JOIN			3818	2	60		
    VIEW	IDEPTEST		3817	2	60		
     SORT GROUP BY			3817	2	84		
      TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3816	30	1260		
       INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421			
 SORT GROUP BY NOSORT			302	1	31		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	1	31		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	33		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	540	17820		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	37		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	540	19980		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	33		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	503	16599		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	37		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	503	18611		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 HASH UNIQUE			120155	7	483		
  HASH JOIN RIGHT OUTER			52583	908514324	62687488356		
   VIEW	IDEPTEST		28574	10	240		
    HASH UNIQUE			28574	10	1160		
     HASH JOIN			28573	1561	181076		
      HASH JOIN			28486	3874	368030		
       VIEW	SYS	VW_SQ_1	14365	41	1353		
        HASH GROUP BY			14365	41	2624		
         TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14092	4895849	313334336		
       TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14107	2462339	152665018		
      INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
   HASH JOIN RIGHT OUTER			18969	908514324	40883144580		
    INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
    INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	5347	4896255	117510120		


----第1次改造---- 

动作:将NE_STATE单独成WITH子句,并用ROWNUM>=0来告知ORACLE中间记录数的情况
      不过我觉的有点奇怪,以前用ROWNUM是为了让视图不要拆开与其他表关联,做
      为一个整体,和今天的效果不一样,今天感觉是起到改变表驱动顺序的作用。
效果:改进了表驱动的顺序,WITH部分的视图结果集小,在前面驱动才是正确的,原先没有ROWNUM时变成在后面驱动。
      具体可以通过UE的比较工具来比较老脚本的执行计划可清晰看出,改变驱动顺序后速度从3000秒提升到50秒。
第1次改造后的SQL语句如下:
with ne_state as                                                                 
(  select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败'))                      
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+) and rownum>=0)                                                                                 
select distinct ne_state.peer_id peer_name,                                       
                         to_char(ne_state.ne_state) peer_state,                   
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (select distinct to_char(nvl(ne_active.active, 0))    
                               from dcc_sys_log,                                  
                                    (select peer_id,                              
                                            decode(action,                        
                                                   'active',                      
                                                   1,                             
                                                   'de-active',                   
                                                   0,                             
                                                   0) active,                     
                                            max(log_time)                         
                                       from dcc_sys_log                           
                                      where action = 'active'                     
                                         or action = 'de-active'                  
                                      group by (peer_id, action)) ne_active       
                              where dcc_sys_log.peer_id = ne_active.peer_id(+)    
                                and dcc_sys_log.peer_id = ne_state.peer_id)       
                         end) peer_active,                                        
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log                        
                                          where dcc_ne_log.result <> 1            
                                            and peer_id = ne_state.peer_id        
                                            and log_time between                  
                                                trunc(sysdate) and sysdate        
                                          group by (peer_id)),                    
                                         0)))                                     
                         end) err_cnt,                                            
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log in_dnl                 
                                          where in_dnl.direction = 'recv'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id                  
                                            and log_time between                  
                                                trunc(sysdate) and sysdate),      
                                         0)))                                     
                         end) recv_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log in_dnl                 
                                          where in_dnl.direction = 'recv'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id                  
                                            and log_time between                  
                                                trunc(sysdate) and sysdate),      
                                         0)))                                     
                         end) recv_byte,                                          
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log in_dnl                 
                                          where in_dnl.direction = 'send'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id                  
                                            and log_time between                  
                                                trunc(sysdate) and sysdate),      
                                         0)))                                     
                         end) send_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log in_dnl                 
                                          where in_dnl.direction = 'send'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id                  
                                            and log_time between                  
                                                trunc(sysdate) and sysdate),      
                                         0)))                                     
                         end) send_byte                                           
           from dcc_ne_log,ne_state                                               
          where ne_state.peer_id = dcc_ne_log.peer_id(+);                         
          
          
第1次改造后的SQL执行计划如下:
SELECT STATEMENT, GOAL = ALL_ROWS			34310	7	336		
 HASH UNIQUE			6001	119421	6448734		
  MERGE JOIN OUTER			4414	119421	6448734		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421	2866104		
   SORT JOIN			3818	2	60		
    VIEW	IDEPTEST		3817	2	60		
     SORT GROUP BY			3817	2	84		
      TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3816	30	1260		
       INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421			
 SORT GROUP BY NOSORT			302	1	31		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	1	31		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	33		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	540	17820		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	37		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	540	19980		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	33		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	503	16599		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 SORT AGGREGATE				1	37		
  FILTER							
   TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	503	18611		
    INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
 HASH UNIQUE			34310	7	336		
  HASH JOIN OUTER			34309	1299	62352		
   VIEW	IDEPTEST		34222	7	189		
    HASH UNIQUE			34222	7	336		
     COUNT							
      FILTER							
       HASH JOIN RIGHT OUTER			33949	4896255	235020240		
        VIEW	IDEPTEST		28574	10	240		
         HASH UNIQUE			28574	10	1160		
          HASH JOIN			28573	1561	181076		
           HASH JOIN			28486	3874	368030		
            VIEW	SYS	VW_SQ_1	14365	41	1353		
             HASH GROUP BY			14365	41	2624		
              TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14092	4895849	313334336		
            TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14107	2462339	152665018		
           INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
        INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	5347	4896255	117510120		
   INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		




---第2次改造----

动作:构造dcc_ne_log_time的WITH子句.
效果:多次调用这个后,系统内部优化,产生自带临时表SYS_TEMP_0FD9D661A_2F9A0F1,
      多次调用SYS_TEMP_0FD9D661A_2F9A0F1而非调用dcc_ne_log是有差别的,调用SYS_TEMP_0FD9D661A_2F9A0F1
      极大的提升了性能,从50秒缩短为14秒!
第2次改造后SQL语句如下:
with ne_state as                                                                 
(select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败'))                      
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+) and rownum>=0),
dcc_ne_log_time as  (select * from dcc_ne_log where  log_time between   trunc(sysdate) and sysdate )                                                                                
select distinct ne_state.peer_id peer_name,                                       
                         to_char(ne_state.ne_state) peer_state,                   
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (select distinct to_char(nvl(ne_active.active, 0))    
                               from dcc_sys_log,                                  
                                    (select peer_id,                              
                                            decode(action,                        
                                                   'active',                      
                                                   1,                             
                                                   'de-active',                   
                                                   0,                             
                                                   0) active,                     
                                            max(log_time)                         
                                       from dcc_sys_log                           
                                      where action = 'active'                     
                                         or action = 'de-active'                  
                                      group by (peer_id, action)) ne_active       
                              where dcc_sys_log.peer_id = ne_active.peer_id(+)    
                                and dcc_sys_log.peer_id = ne_state.peer_id)       
                         end) peer_active,                                        
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time                        
                                          where dcc_ne_log_time.result <> 1            
                                            and peer_id = ne_state.peer_id              
                                          group by (peer_id)),                    
                                         0)))                                     
                         end) err_cnt,                                            
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'recv'   
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) recv_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'recv'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) recv_byte,                                          
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'send'    
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) send_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'send'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) send_byte                                           
           from dcc_ne_log_time,ne_state                                               
          where ne_state.peer_id = dcc_ne_log_time.peer_id(+); 
          
          
第2次改造后的SQL执行计划如下:
SELECT STATEMENT, GOAL = ALL_ROWS			34584	7	336		
 HASH UNIQUE			6001	119421	6448734		
  MERGE JOIN OUTER			4414	119421	6448734		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421	2866104		
   SORT JOIN			3818	2	60		
    VIEW	IDEPTEST		3817	2	60		
     SORT GROUP BY			3817	2	84		
      TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3816	30	1260		
       INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421			
 SORT GROUP BY NOSORT			58	7	238		
  VIEW	IDEPTEST		58	7302	248268		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	53		
  VIEW	IDEPTEST		58	7302	387006		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	57		
  VIEW	IDEPTEST		58	7302	416214		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	53		
  VIEW	IDEPTEST		58	7302	387006		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	57		
  VIEW	IDEPTEST		58	7302	416214		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		
 TEMP TABLE TRANSFORMATION							
  LOAD AS SELECT							
   COUNT							
    FILTER							
     FILTER							
      TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	7302	1891218		
       INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
  HASH UNIQUE			34282	7	336		
   HASH JOIN OUTER			34281	7	336		
    VIEW	IDEPTEST		34222	7	189		
     HASH UNIQUE			34222	7	336		
      COUNT							
       FILTER							
        HASH JOIN RIGHT OUTER			33949	4896255	235020240		
         VIEW	IDEPTEST		28574	10	240		
          HASH UNIQUE			28574	10	1160		
           HASH JOIN			28573	1561	181076		
            HASH JOIN			28486	3874	368030		
             VIEW	SYS	VW_SQ_1	14365	41	1353		
              HASH GROUP BY			14365	41	2624		
               TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14092	4895849	313334336		
             TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14107	2462339	152665018		
            INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
         INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	5347	4896255	117510120		
    VIEW	IDEPTEST		58	7302	153342		
     TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D661A_2F9A0F1	58	7302	1891218		




----第3次改进-----

动作:部分SQL进行等价改写
将 select distinct to_char(nvl(ne_active.active, 0))    
                               from dcc_sys_log,                                  
                                    (select peer_id,                              
                                            decode(action,                        
                                                   'active',                      
                                                   1,                             
                                                   'de-active',                   
                                                   0,                             
                                                   0) active,                     
                                            max(log_time)                         
                                       from dcc_sys_log                           
                                      where action = 'active'                     
                                         or action = 'de-active'                  
                                      group by (peer_id, action)) ne_active       
                              where dcc_sys_log.peer_id = ne_active.peer_id(+)    
                                and dcc_sys_log.peer_id = ne_state.peer_id
修改为:
NVL((select '1' from dcc_sys_log where peer_id = ne_state.peer_id and action = 'active' and rownum=1),'0')  

效果:改写的写法dcc_sys_log表的扫描1次数,而未改写时是扫描dcc_sys_log表2次
      性能提升,执行速度由15秒变为10秒
第3次改造后的SQL语句如下:
with ne_state as                                                                 
(select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败'))                      
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+) and rownum>=0),
dcc_ne_log_time as  (select * from dcc_ne_log where  log_time between   trunc(sysdate) and sysdate )                                                                                
select distinct ne_state.peer_id peer_name,                                       
                         to_char(ne_state.ne_state) peer_state,                   
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                         NVL((select '1' from dcc_sys_log where peer_id = ne_state.peer_id and action = 'active' and rownum=1),'0')      
                         end) peer_active,                                        
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time                        
                                          where dcc_ne_log_time.result <> 1            
                                            and peer_id = ne_state.peer_id              
                                          group by (peer_id)),                    
                                         0)))                                     
                         end) err_cnt,                                            
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'recv'   
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) recv_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'recv'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) recv_byte,                                          
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select count(*)                         
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'send'      
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) send_cnt,                                           
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                            (to_char(nvl((select sum(length)                      
                                           from dcc_ne_log_time in_dnl                 
                                          where in_dnl.direction = 'send'         
                                            and in_dnl.peer_id =                  
                                                ne_state.peer_id),      
                                         0)))                                     
                         end) send_byte                                           
           from dcc_ne_log_time,ne_state                                               
          where ne_state.peer_id = dcc_ne_log_time.peer_id(+); 


第3次改造后的SQL执行计划如下:
SELECT STATEMENT, GOAL = ALL_ROWS			34584	7	336		
 COUNT STOPKEY							
  TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3816	1	35		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421			
 SORT GROUP BY NOSORT			58	7	238		
  VIEW	IDEPTEST		58	7302	248268		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	44		
  VIEW	IDEPTEST		58	7302	321288		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	57		
  VIEW	IDEPTEST		58	7302	416214		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	44		
  VIEW	IDEPTEST		58	7302	321288		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		
 SORT AGGREGATE				1	57		
  VIEW	IDEPTEST		58	7302	416214		
   TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		
 TEMP TABLE TRANSFORMATION							
  LOAD AS SELECT							
   FILTER							
    TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	302	7302	1891218		
     INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	21	7302			
  HASH UNIQUE			34282	7	336		
   HASH JOIN OUTER			34281	7	336		
    VIEW	IDEPTEST		34222	7	189		
     HASH UNIQUE			34222	7	336		
      COUNT							
       FILTER							
        HASH JOIN RIGHT OUTER			33949	4896255	235020240		
         VIEW	IDEPTEST		28574	10	240		
          HASH UNIQUE			28574	10	1160		
           HASH JOIN			28573	1561	181076		
            HASH JOIN			28486	3874	368030		
             VIEW	SYS	VW_SQ_1	14365	41	1353		
              HASH GROUP BY			14365	41	2624		
               TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14092	4895849	313334336		
             TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14107	2462339	152665018		
            INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
         INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	5347	4896255	117510120		
    VIEW	IDEPTEST		58	7302	153342		
     TABLE ACCESS FULL	SYS	SYS_TEMP_0FD9D662A_2F9A0F1	58	7302	1891218		



---第4次改造----
动作:将所有标量子查询改造为单独一个表关联写法
效果:将标量子查询的多次扫描,降低为将表扫描次仅1次,执行时间从10秒缩短为7秒


with ne_state as                                                                 
(select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败'))                      
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+) and rownum>=0),
dcc_ne_log_time as (select peer_id
                          ,COUNT(CASE WHEN RESULT <> 1 THEN 1 END) err_cnt
                          ,COUNT(CASE WHEN direction = 'recv' THEN 1 END) recv_cnt
                          ,SUM(CASE WHEN direction = 'recv' THEN length END) recv_byte
                          ,COUNT(CASE WHEN direction = 'send' THEN 1 END) send_cnt
                          ,SUM(CASE WHEN direction = 'send' THEN length END) send_byte
                     from dcc_ne_log 
                     where log_time >=trunc(sysdate) ---- between trunc(sysdate) and sysdate 
                     GROUP BY peer_id)                                                                    
select distinct ne_state.peer_id peer_name,                                       
                         to_char(ne_state.ne_state) peer_state,                   
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                         NVL((select '1' from dcc_sys_log where peer_id = ne_state.peer_id and action = 'active' and rownum=1),'0')  
                         end) peer_active,   
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.ERR_CNT,0)) ERR_CNT, ---注意NVL改造
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.recv_cnt,0)) recv_cnt, 
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.recv_byte,0)) recv_byte,
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.send_cnt,0)) send_cnt, 
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.send_byte,0)) send_byte                                      
           from ne_state ,dcc_ne_log_time dnlt  
           where    ne_state.peer_id=dnlt.peer_id(+)

                                                    
第4次改造后的SQL执行计划
SELECT STATEMENT, GOAL = ALL_ROWS			34231	7	791		
 COUNT STOPKEY							
  TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3816	1	35		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	596	119421			
 HASH UNIQUE			34231	7	791		
  HASH JOIN OUTER			34230	7	791		
   VIEW	IDEPTEST		34222	7	189		
    HASH UNIQUE			34222	7	336		
     COUNT							
      FILTER							
       HASH JOIN RIGHT OUTER			33949	4896255	235020240		
        VIEW	IDEPTEST		28574	10	240		
         HASH UNIQUE			28574	10	1160		
          HASH JOIN			28573	1561	181076		
           HASH JOIN			28486	3874	368030		
            VIEW	SYS	VW_SQ_1	14365	41	1353		
             HASH GROUP BY			14365	41	2624		
              TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14092	4895849	313334336		
            TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14107	2462339	152665018		
           INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	86	85428	1793988		
        INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	5347	4896255	117510120		
   VIEW	IDEPTEST		8	7	602		
    HASH GROUP BY			8	7	280		
     TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	7	108	4320		
      INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	2	108			








---第5次改造

 如下部分(即我改造后语句的第一个WITH的地方),觉的非常怪异
        外面用 action和cause,里面在查最大时间用的是log_type = '对端交互'
        根据我的经验常识来看,需求应该是log_type = '对端交互'同时作用于内外,即找出log_type = '对端交互'的最近一条记录,
        然后看它的action和cause是否满足要求,难道不是这样吗,这样才顺畅嘛。
select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败'))                      
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+)    and rownum>=0
                  
     如果我猜测的是对的,代码就应该改写为如下(增加and log_type = '对端交互' ):
     把数据限于log_type = '对端交互'的那些。如果有哪个peer_id不存在 log_type = '对端交互',
     那么这个peer_id不在新写法中出现。之前的旧写法包括所有。
     
     select distinct dsl1.peer_id peer_id,                                          
                                 nvl(ne_disconnect_info.ne_state, 1) ne_state     
                   from dcc_sys_log dsl1,                                         
                        (select distinct dnl.peer_id peer_id,                     
                                         decode(action,                           
                                                'disconnect',                     
                                                0,                                
                                                'connect',                        
                                                0,                                
                                                1) ne_state                       
                           from dcc_sys_log dsl, dcc_ne_log dnl                   
                          where dsl.peer_id = dnl.peer_id                         
                            and ((dsl.action = 'disconnect' and                   
                                dsl.cause = '关闭对端') or                        
                                (dsl.action = 'connect' and                       
                                dsl.cause = '连接主机失败')) 
                            and log_type = '对端交互'                     
                            and dsl.log_time =                                    
                                (select max(log_time)                             
                                   from dcc_sys_log                               
                                  where peer_id = dnl.peer_id                     
                                    and log_type = '对端交互')) ne_disconnect_info
                  where dsl1.peer_id = ne_disconnect_info.peer_id(+)    and rownum>=0
     
     以下改写可以完善取最大日期的记录的方法,可进一步减少扫描次数
     

     
SELECT a.peer_id,
CASE WHEN dnl.peer_id IS NOT NULL AND str IN ('disconnect关闭对端','connect连接主机失败') THEN '0' ELSE '1' END ne_state
FROM (SELECT peer_id,MIN(action||cause) KEEP(DENSE_RANK LAST ORDER BY log_time) str 
      FROM dcc_sys_log dsl
      WHERE log_type = '对端交互'
      GROUP BY peer_id
) a,(SELECT DISTINCT peer_id FROM dcc_ne_log) dnl 
WHERE a.peer_id = dnl.peer_id(+)

   
    经过5次改造后,最终完善优化版代码如下:

with ne_state as                                                                 
(SELECT a.peer_id,
CASE WHEN dnl.peer_id IS NOT NULL AND str IN ('disconnect关闭对端','connect连接主机失败') THEN '0' ELSE '1' END ne_state
FROM (SELECT peer_id,MIN(action||cause) KEEP(DENSE_RANK LAST ORDER BY log_time) str 
      FROM dcc_sys_log dsl
      WHERE log_type = '对端交互'
      GROUP BY peer_id
) a,(SELECT DISTINCT peer_id FROM dcc_ne_log) dnl 
WHERE a.peer_id = dnl.peer_id(+)),
dcc_ne_log_time as (select peer_id
                          ,COUNT(CASE WHEN RESULT <> 1 THEN 1 END) err_cnt
                          ,COUNT(CASE WHEN direction = 'recv' THEN 1 END) recv_cnt
                          ,SUM(CASE WHEN direction = 'recv' THEN length END) recv_byte
                          ,COUNT(CASE WHEN direction = 'send' THEN 1 END) send_cnt
                          ,SUM(CASE WHEN direction = 'send' THEN length END) send_byte
                     from dcc_ne_log 
                     where log_time >=trunc(sysdate) ---- between trunc(sysdate) and sysdate 
                     GROUP BY peer_id)                                                                    
select distinct ne_state.peer_id peer_name,                                       
                         to_char(ne_state.ne_state) peer_state,                   
                         (case                                                    
                           when ne_state.ne_state = 0 then                        
                            to_char(0)                                            
                           else                                                   
                         NVL((select '1' from dcc_sys_log where peer_id = ne_state.peer_id and action = 'active' and rownum=1),'0')  
                         end) peer_active,   
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.ERR_CNT,0)) ERR_CNT, ---注意NVL改造
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.recv_cnt,0)) recv_cnt, 
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.recv_byte,0)) recv_byte,
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.send_cnt,0)) send_cnt, 
                         decode(ne_state.ne_state,0,'0',nvl(dnlt.send_byte,0)) send_byte                                      
           from ne_state ,dcc_ne_log_time dnlt  
           where    ne_state.peer_id=dnlt.peer_id(+)
     
     
执行计划:           
SELECT STATEMENT, GOAL = ALL_ROWS			14880	71	19667		
 COUNT STOPKEY							
  TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_SYS_LOG	3235	1	35		
   INDEX RANGE SCAN	IDEPTEST	IDX_DCC_SYS_LOG_PEER	505	100809			
 HASH UNIQUE			14880	71	19667		
  HASH JOIN OUTER			14878	19100	5290700		
   HASH JOIN RIGHT OUTER			14657	49	12446		
    VIEW	IDEPTEST		65	10	880		
     HASH GROUP BY			65	10	430		
      TABLE ACCESS BY INDEX ROWID	IDEPTEST	DCC_NE_LOG	64	1388	59684		
       INDEX RANGE SCAN	IDEPTEST	IDX_DCC_NE_LOG_TIME	7	1388			
    VIEW	IDEPTEST		14592	49	8134		
     SORT GROUP BY			14592	49	3479		
      TABLE ACCESS FULL	IDEPTEST	DCC_SYS_LOG	14316	4939225	350684975		
   INDEX FAST FULL SCAN	IDEPTEST	IDX_DCC_NE_LOG_PEER	220	176424	4057752		

 

转载于:https://my.oschina.net/rosetta/blog/747180

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值