陈老师:1415968548 郑老师:2735197625 乐老师:354331153
客服热线:
19941464235/19906632509(微信同号)

客服微信

plsql developer代码助手卡顿分析

作者:云贝学院
发布时间:2024-01-15 17:34
浏览量:453

作者:刘晓峰


1.背景

查询 select  * from  fnd_lookup_values_vl flv where flv.LOOKUP_TYPE 时,当你输入flv.lo ,此时PLSQL developer会自动弹出代码助手,

但是我选择代码助手提供的字段时,等待卡顿时间有5-10秒,而且每次使用弹出的字段都会卡顿

我本地同版本的plsql developer访问本地数据库不会卡顿,首先排除软件的问题,而网络问题即使查出来我也无法解决

考虑到代码助手会频繁访问数据字段视图,所以尝试使用trace看看到底查询那个数据字典产生了问题

理论上来说,如果用idea navicat连接也能进行优化

当然mysql也是一样的,自行搜索mysql trace


2.确认process id 并开启trace

SELECT p.tracefile, p.SPID--6533
  FROM v$session s, v$process p
 WHERE p.addr = s.paddr
   AND s.sid = userenv('sid');
   
ALTER SESSION SET EVENTS '10046 trace name context forever, level 12';

3.不执行查询,只是用代码助手弹框并选择

select * from flv.LOOKUP_TYPE flv.DESCRIPTION
select * from fnd_lookup_values  f where f.description= and f.start_date_active=
select  * from  fnd_lookup_values_vl flv where flv.ROW_ID= and flv.ATTRIBUTE_CATEGORY= and flv.ATTRIBUTE1= and flv.ATTRIBUTE2 and flv.ATTRIBUTE3 and flv.DESCRIPTION
select * from dba_objects d where d.OBJECT_NAME=
select * from fnd_lookup_values_vl
select * from f.lookup_type f.attribute13

4.关闭追踪


ALTER SESSION SET EVENTS '10046 trace name context off';


5.获取trace文件并解析

这一步出了问题,开发环境用的WINSCP获取文件,不稳定,取不到文件,因此使用我的本地环境分析(虽然我自己的服务器并不卡)


6.本地trace信息(由于拿不到自己开发环境的trace,所以只能试试看,拿本地的日志去优化开发环境,各位如果使用优化脚本效果不好,建议用自己的6分析,可能会发现新的性能问题)

6.1

select column_name, nullable, data_type, data_type_mod, data_type_owner, data_length, data_precision, data_scale, char_used, char_length
from sys.all_tab_columns
where owner = 'SYS'
and table_name = 'V_$SESSION'
order by column_id


Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       6        0.00          0.00
  SQL*Net message from client                     6        0.01          0.06
  direct path read                              154        0.05          0.38


********************************************************************************

6.2

select comments from sys.all_tab_comments
 where owner      = :object_owner
   and table_name = :object_name
and origin_con_id in (1, sys_context('userenv', 'con_id'))
Elapsed times include waiting on following events:
  Event waited on                             Times   Max. Wait  Total Waited
  ----------------------------------------   Waited  ----------  ------------
  SQL*Net message to client                       4        0.00          0.00
  SQL*Net message from client                     4        5.83          9.33
  db file sequential read                         2        0.00          0.00

*******************************************************************************

6.3

select column_name, nullable, data_type, data_type_mod, data_type_owner, data_length, data_precision, data_scale, char_used, char_length
from sys.all_tab_columns
where owner = 'SYS'
and table_name = 'DBA_OBJECTS'
order by column_id

*******************************************************************************

6.4拿到开发环境的已有缓存的trace

select column_name, nullable, data_type, data_type_mod, data_type_owner, data_length, data_precision, data_scale, char_used, char_length
from sys.all_tab_columns
where owner = 'SYS'
and table_name = 'DBA_COL_COMMENTS'
order by column_id

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.63       0.63          0          8          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      0.00       0.00          0         46          0           5
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.63       0.63          0         54          0           5

Misses in library cache during parse: 1


7.分析(目前全是基于本地环境的,非真实开发环境,因为拿不到trace)

看过来主要是sys.all_tab_columns视图的问题,那么问题简单了,直接一把梭,我们尝试一下对比执行计划看看

我的本地环境

---------------------------------------------------------------------------------------------------------------
| Id   | Operation                                       | Name              | Rows | Bytes | Cost | Time     |
---------------------------------------------------------------------------------------------------------------
|    0 | SELECT STATEMENT                                |                   |    1 |   257 |   33 | 00:00:01 |
|    1 |   TABLE ACCESS BY INDEX ROWID BATCHED           | OBJ$              |    1 |    40 |    3 | 00:00:01 |
|  * 2 |    INDEX RANGE SCAN                             | I_OBJ1            |    1 |       |    2 | 00:00:01 |
|    3 |    TABLE ACCESS BY INDEX ROWID BATCHED          | OBJ$              |    1 |    40 |    3 | 00:00:01 |
|  * 4 |     INDEX RANGE SCAN                            | I_OBJ1            |    1 |       |    2 | 00:00:01 |
|    5 |     TABLE ACCESS BY INDEX ROWID BATCHED         | OBJ$              |    1 |    40 |    3 | 00:00:01 |
|  * 6 |      INDEX RANGE SCAN                           | I_OBJ1            |    1 |       |    2 | 00:00:01 |
|    7 |      TABLE ACCESS BY INDEX ROWID BATCHED        | OBJ$              |    1 |    40 |    3 | 00:00:01 |
|  * 8 |       INDEX RANGE SCAN                          | I_OBJ1            |    1 |       |    2 | 00:00:01 |
|    9 |       TABLE ACCESS BY INDEX ROWID BATCHED       | OBJ$              |    1 |    40 |    3 | 00:00:01 |
| * 10 |        INDEX RANGE SCAN                         | I_OBJ1            |    1 |       |    2 | 00:00:01 |
|   11 |   NESTED LOOPS                                  |                   |    1 |    31 |    3 | 00:00:01 |
| * 12 |    INDEX RANGE SCAN                             | I_OBJ1            |    1 |     8 |    2 | 00:00:01 |
|   13 |    TABLE ACCESS CLUSTER                         | USER$             |    1 |    23 |    1 | 00:00:01 |
| * 14 |     INDEX UNIQUE SCAN                           | I_USER#           |    1 |       |    0 | 00:00:01 |
|   15 |   SORT ORDER BY                                 |                   |    1 |   257 |   33 | 00:00:01 |
| * 16 |    FILTER                                       |                   |      |       |      |          |
|   17 |     NESTED LOOPS OUTER                          |                   |    1 |   257 |   14 | 00:00:01 |
|   18 |      NESTED LOOPS                               |                   |    1 |   222 |   13 | 00:00:01 |
|   19 |       NESTED LOOPS OUTER                        |                   |    1 |   209 |   12 | 00:00:01 |
|   20 |        NESTED LOOPS OUTER                       |                   |    1 |   199 |   11 | 00:00:01 |
|   21 |         NESTED LOOPS OUTER                      |                   |    1 |   155 |    7 | 00:00:01 |
|   22 |          NESTED LOOPS OUTER                     |                   |    1 |   127 |    6 | 00:00:01 |
|   23 |           NESTED LOOPS                          |                   |    1 |   122 |    5 | 00:00:01 |
|   24 |            NESTED LOOPS                         |                   |    1 |    74 |    4 | 00:00:01 |
|   25 |             TABLE ACCESS BY INDEX ROWID         | USER$             |    1 |    18 |    1 | 00:00:01 |
| * 26 |              INDEX UNIQUE SCAN                  | I_USER1           |    1 |       |    0 | 00:00:01 |
| * 27 |             TABLE ACCESS BY INDEX ROWID BATCHED | OBJ$              |    1 |    56 |    3 | 00:00:01 |
| * 28 |              INDEX RANGE SCAN                   | I_OBJ5            |    1 |       |    2 | 00:00:01 |
| * 29 |            TABLE ACCESS CLUSTER                 | COL$              |    1 |    48 |    1 | 00:00:01 |
| * 30 |             INDEX UNIQUE SCAN                   | I_OBJ#            |    1 |       |    0 | 00:00:01 |
|   31 |           TABLE ACCESS CLUSTER                  | TAB$              |    1 |     5 |    1 | 00:00:01 |
| * 32 |            INDEX UNIQUE SCAN                    | I_OBJ#            |    1 |       |    0 | 00:00:01 |
| * 33 |          TABLE ACCESS CLUSTER                   | COLTYPE$          |    1 |    28 |    1 | 00:00:01 |
| * 34 |         TABLE ACCESS BY INDEX ROWID BATCHED     | OBJ$              |    1 |    44 |    4 | 00:00:01 |
| * 35 |          INDEX RANGE SCAN                       | I_OBJ3            |   21 |       |    1 | 00:00:01 |
| * 36 |        INDEX RANGE SCAN                         | I_HH_OBJ#_INTCOL# |    1 |    10 |    1 | 00:00:01 |
| * 37 |       INDEX RANGE SCAN                          | I_USER2           |    1 |    13 |    1 | 00:00:01 |
|   38 |      TABLE ACCESS CLUSTER                       | USER$             |    1 |    35 |    1 | 00:00:01 |
| * 39 |       INDEX UNIQUE SCAN                         | I_USER#           |    1 |       |    0 | 00:00:01 |
| * 40 |     TABLE ACCESS CLUSTER                        | TAB$              |    1 |    13 |    2 | 00:00:01 |
| * 41 |      INDEX UNIQUE SCAN                          | I_OBJ#            |    1 |       |    1 | 00:00:01 |
|   42 |     NESTED LOOPS SEMI                           |                   |    1 |    12 |    2 | 00:00:01 |
|   43 |      FIXED TABLE FULL                           | X$KZSRO           |    2 |     6 |    0 | 00:00:01 |
| * 44 |      INDEX RANGE SCAN                           | I_OBJAUTH2        |    1 |     9 |    1 | 00:00:01 |
| * 45 |     FIXED TABLE FULL                            | X$KZSPR           |    1 |     7 |    0 | 00:00:01 |
| * 46 |     TABLE ACCESS BY INDEX ROWID BATCHED         | USER_EDITIONING$  |    1 |     7 |    2 | 00:00:01 |
| * 47 |      INDEX RANGE SCAN                           | I_USER_EDITIONING |    9 |       |    1 | 00:00:01 |
| * 48 |     TABLE ACCESS BY INDEX ROWID BATCHED         | USER_EDITIONING$  |    1 |     7 |    2 | 00:00:01 |
| * 49 |      INDEX RANGE SCAN                           | I_USER_EDITIONING |    9 |       |    1 | 00:00:01 |
|   50 |     NESTED LOOPS SEMI                           |                   |    1 |    18 |    3 | 00:00:01 |
| * 51 |      INDEX RANGE SCAN                           | I_OBJ4            |    1 |     9 |    2 | 00:00:01 |
| * 52 |      INDEX RANGE SCAN                           | I_USER2           |    3 |    27 |    1 | 00:00:01 |
---------------------------------------------------------------------------------------------------------------
当前的开发环境(来源于共享池缓存,非trace)
------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                    | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                             |                   |       |       |  1635 (100)|          |
|   1 |  TABLE ACCESS BY INDEX ROWID BATCHED         | OBJ$              |     1 |    28 |     4   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN                           | I_OBJ1            |     1 |       |     3   (0)| 00:00:01 |
|   3 |   TABLE ACCESS BY INDEX ROWID BATCHED        | OBJ$              |     1 |    28 |     4   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN                          | I_OBJ1            |     1 |       |     3   (0)| 00:00:01 |
|   5 |    TABLE ACCESS BY INDEX ROWID BATCHED       | OBJ$              |     1 |    28 |     4   (0)| 00:00:01 |
|*  6 |     INDEX RANGE SCAN                         | I_OBJ1            |     1 |       |     3   (0)| 00:00:01 |
|   7 |     TABLE ACCESS BY INDEX ROWID BATCHED      | OBJ$              |     1 |    28 |     4   (0)| 00:00:01 |
|*  8 |      INDEX RANGE SCAN                        | I_OBJ1            |     1 |       |     3   (0)| 00:00:01 |
|   9 |      TABLE ACCESS BY INDEX ROWID BATCHED     | OBJ$              |     1 |    28 |     4   (0)| 00:00:01 |
|* 10 |       INDEX RANGE SCAN                       | I_OBJ1            |     1 |       |     3   (0)| 00:00:01 |
|  11 |  NESTED LOOPS                                |                   |     1 |   160 |     4   (0)| 00:00:01 |
|* 12 |   INDEX RANGE SCAN                           | I_OBJ1            |     1 |    10 |     3   (0)| 00:00:01 |
|  13 |   TABLE ACCESS CLUSTER                       | USER$             |     1 |   150 |     1   (0)| 00:00:01 |
|* 14 |    INDEX UNIQUE SCAN                         | I_USER#           |     1 |       |     0   (0)|          |
|  15 |  SORT ORDER BY                               |                   |     1 |   357 |  1635   (1)| 00:00:01 |
|* 16 |   FILTER                                     |                   |       |       |            |          |
|  17 |    NESTED LOOPS OUTER                        |                   |    63 | 22491 |  1610   (1)| 00:00:01 |
|* 18 |     HASH JOIN OUTER                          |                   |    63 | 21861 |  1483   (1)| 00:00:01 |
|  19 |      NESTED LOOPS OUTER                      |                   |    63 | 12411 |  1476   (1)| 00:00:01 |
|  20 |       NESTED LOOPS OUTER                     |                   |    63 | 10395 |   278   (0)| 00:00:01 |
|  21 |        NESTED LOOPS                          |                   |    63 |  8568 |   277   (0)| 00:00:01 |
|  22 |         NESTED LOOPS OUTER                   |                   |     1 |    84 |   275   (0)| 00:00:01 |
|* 23 |          HASH JOIN                           |                   |     1 |    78 |   273   (0)| 00:00:01 |
|* 24 |           TABLE ACCESS BY INDEX ROWID BATCHED| OBJ$              |     5 |   215 |   269   (0)| 00:00:01 |
|* 25 |            INDEX SKIP SCAN                   | I_OBJ5            |     5 |       |   266   (0)| 00:00:01 |
|  26 |           NESTED LOOPS                       |                   |   579 | 20265 |     4   (0)| 00:00:01 |
|  27 |            TABLE ACCESS BY INDEX ROWID       | USER$             |     1 |    12 |     2   (0)| 00:00:01 |
|* 28 |             INDEX UNIQUE SCAN                | I_USER1           |     1 |       |     1   (0)| 00:00:01 |
|  29 |            INDEX FAST FULL SCAN              | I_USER2           |   579 | 13317 |     2   (0)| 00:00:01 |
|  30 |          TABLE ACCESS CLUSTER                | TAB$              |     1 |     6 |     2   (0)| 00:00:01 |
|* 31 |           INDEX UNIQUE SCAN                  | I_OBJ#            |     1 |       |     0   (0)|          |
|* 32 |         TABLE ACCESS CLUSTER                 | COL$              |    68 |  3536 |     2   (0)| 00:00:01 |
|* 33 |          INDEX UNIQUE SCAN                   | I_OBJ#            |     1 |       |     0   (0)|          |
|  34 |        TABLE ACCESS BY INDEX ROWID           | COLTYPE$          |     1 |    29 |     1   (0)| 00:00:01 |
|* 35 |         INDEX UNIQUE SCAN                    | I_COLTYPE2        |     1 |       |     0   (0)|          |
|* 36 |       TABLE ACCESS BY INDEX ROWID BATCHED    | OBJ$              |     1 |    32 |    19   (0)| 00:00:01 |
|* 37 |        INDEX RANGE SCAN                      | I_OBJ3            |    85 |       |     1   (0)| 00:00:01 |
|  38 |      TABLE ACCESS FULL                       | USER$             |   579 | 86850 |     7   (0)| 00:00:01 |
|* 39 |     INDEX RANGE SCAN                         | I_HH_OBJ#_INTCOL# |     1 |    10 |     2   (0)| 00:00:01 |
|* 40 |    TABLE ACCESS CLUSTER                      | TAB$              |     1 |    14 |     3   (0)| 00:00:01 |
|* 41 |     INDEX UNIQUE SCAN                        | I_OBJ#            |     1 |       |     1   (0)| 00:00:01 |
|* 42 |    HASH JOIN SEMI                            |                   |     1 |    23 |     3   (0)| 00:00:01 |
|  43 |     FIXED TABLE FULL                         | X$KZSRO           |     2 |    26 |     0   (0)|          |
|* 44 |     INDEX RANGE SCAN                         | I_OBJAUTH1        |     6 |    60 |     3   (0)| 00:00:01 |
|* 45 |    FIXED TABLE FULL                          | X$KZSPR           |     1 |    20 |     0   (0)|          |
|* 46 |    TABLE ACCESS FULL                         | USER_EDITIONING$  |     1 |     7 |     2   (0)| 00:00:01 |
|  47 |    NESTED LOOPS SEMI                         |                   |     1 |    31 |     4   (0)| 00:00:01 |
|* 48 |     INDEX RANGE SCAN                         | I_OBJ4            |     1 |    11 |     3   (0)| 00:00:01 |
|* 49 |     INDEX RANGE SCAN                         | I_USER2           |     1 |    20 |     1   (0)| 00:00:01 |
|* 50 |    TABLE ACCESS FULL                         | USER_EDITIONING$  |     1 |     7 |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------

(补充一下,这里收集数据字典统计信息没有用的,因为数据字典表都是有定时任务自动收集,我后面的附件统计信息也可以体现,统计信息估算的是正确的)


可以看到成本差了50倍,下面说一下细节问题

1.全表扫描USER$的字节数较高,86KB,这个表是是一个聚簇表,查询该聚簇下所有的表可以用如下sql

   SELECT d2.object_name, d2.object_type
     FROM dba_objects d1, dba_objects d2
    WHERE d1.object_name = 'USER$'
      AND d1.data_object_id = d2.data_object_id


不过我感觉这个不是核心,因为在开始频繁使用代码助手后,USER$肯定是会缓存的,USER$虽然是全表扫描,但是这个表才几千行,不是瓶颈


2.代码助手每次都是硬编码(所以每个数据库的美化文件一定要一样,每个SQL文本会进行hash函数运算,空1格,空2格都会导致硬解析,最好的方式是尽可能使用函数封装对基表的调用),导致解析时间较长,次要问题,因为今天看到解析时间0.6秒,而5秒的时间要么是闩锁问题,要么是初次执行的物理读问题,关于IO问题我们可以尝试使用keep pool缓存,但是数据字典表还是慎重一点,这里不去测试了

上面分析只是猜测各自情况,我拿不到开发环境的trace,没办法分析

看下开发环境数据字典表关于table_name的直方图怎么样,通过数据倾斜程度判断是否能够共享游标(这个只是方法,我认为table name列返回行数的数据分布应该非常平均,这一步没必要验证,这里只是演示)

 SELECT dtc.column_name, --列名
         dtc.num_distinct, --相异基数
         dtc.num_nulls, --空值
         dtc.num_buckets, --直方图桶个数
         dtc.last_analyzed, --上次分析时间
         dtc.histogram --直方图类型
    FROM dba_tab_col_statistics dtc
   WHERE dtc.table_name = 'OBJ$'
   AND DTC.COLUMN_NAME in ('NAME' ,'OBJ#');
    COLUMN_NAME NUM_DISTINCT  NUM_NULLS NUM_BUCKETS LAST_ANALYZED HISTOGRAM
1 NAME  319520  0 254 2023/1/23 22:14:45  HYBRID
2 OBJ#  497879  0 254 2023/1/23 22:14:45  HYBRID


其中HYBRID 是混合直方图,查看其分布如下:

SELECT column_name,
       endpoint_value,
       endpoint_number
      
  FROM dba_tab_histograms
 WHERE table_name = 'OBJ$'
   AND column_name in ('NAME' ,'OBJ#')
 ORDER BY column_name,endpoint_value

桶数默认254,不同的值有32万,所以采用了混合直方图,先不讨论直方图的问题,(混合直方图经过测试发现没有其它直方图效果好)

select count(1) from (select  name , count(1)from sys.OBJ$ group by NAME order by 2 desc)

所以直接修改,为了清楚的演示我要干什么,请看下面的例子(核心就是这个代码助手没有使用绑定变量)

请在自己的测试环境测试

----=========================================================----

alter system flush shared_pool;

SELECT column_name
      ,nullable
      ,data_type
      ,data_type_mod
      ,data_type_owner
      ,data_length
      ,data_precision
      ,data_scale
      ,char_used
      ,char_length
  FROM sys.all_tab_columns
 WHERE owner = 'SYS'
   AND table_name = 'V_$SESSION'
 ORDER BY column_id;
 
 SELECT column_name
      ,nullable
      ,data_type
      ,data_type_mod
      ,data_type_owner
      ,data_length
      ,data_precision
      ,data_scale
      ,char_used
      ,char_length
  FROM sys.all_tab_columns
 WHERE owner = 'SYS'
   AND table_name = 'V$SESSION'
 ORDER BY column_id
 
 select  s.sql_text,s.sql_id,s.child_number,s.is_bind_sensitive,s.is_bind_aware,s.bind_data from v$sql s where s.sql_text like '%all_tab_columns%' and s.sql_text like '%''SYS''%' and s.sql_text not like '%v$sql%';

可以看到 代码助手的的SQL有2个子游标,而我自己查询的会产生两条解析

SQL_ID  CHILD_NUMBER  IS_BIND_SENSITIVE IS_BIND_AWARE BIND_DATA
cqzr43zn3w0ma 0 N N --代码助手产生
cqzr43zn3w0ma 1 N N --代码助手产生
cbttrjzr90dra 0 N N --对应'V_$SESSION'
8v3bp2h9c93pg 0 N N --对应'V$SESSION'

开启共享游标

alter system flush shared_pool;
alter session set "_optimizer_adaptive_cursor_sharing"=false
alter session set cursor_sharing='FORCE';--此命令放入AfterConnect.sql 则可以每次开启新窗口可以自动执行,当然你也可以把会话语言选项放入

测试代码助手

select * from v$parameter p where p.name='cursor_sharing' and p.default_value and p.display_value and p.description
select * from v$session s where s.blocking_session_status=  and s.blocking_session_status and s.wait_time_micro and s.blocking_instance

再次查询游标,期间多用不同表的代码助手

select  s.sql_text,s.sql_id,s.child_number,s.is_bind_sensitive,s.is_bind_aware,s.bind_data from v$sql s where s.sql_text like '%all_tab_columns%' ;

结果如下

SQL_ID  CHILD_NUMBER  IS_BIND_SENSITIVE IS_BIND_AWARE
8fqtv1mu1y2fv 0 Y N
8fqtv1mu1y2fv 1 Y N

可以看到开启共享游标之后,仍然产生了新的执行计划,我们要避免硬解析,至少能把硬解析改成软解析,所以在代码助手不使用绑定变量的前提下,

不仅要开启游标共享,还需要关闭自适应游标共享

alter session set "_optimizer_extended_cursor_sharing_rel"=none ;
alter session set "_optimizer_extended_cursor_sharing"=none ;
alter session set "_optimizer_adaptive_cursor_sharing"=false;
alter session set cursor_sharing=FORCE;

检查修改是否正确

SELECT a.ksppinm "Parameter", b.KSPPSTDF "Default Value",
       b.ksppstvl "Session Value", 
       c.ksppstvl "Instance Value",
       decode(bitand(a.ksppiflg/256,1),1,'TRUE','FALSE') IS_SESSION_MODIFIABLE,
       decode(bitand(a.ksppiflg/65536,3),1,'IMMEDIATE',2,'DEFERRED',3,'IMMEDIATE','FALSE') IS_SYSTEM_MODIFIABLE
FROM   x$ksppi a,
       x$ksppcv b,
       x$ksppsv c
WHERE  a.indx = b.indx
AND    a.indx = c.indx
AND    a.ksppinm in ('_optimizer_adaptive_cursor_sharing','cursor_sharing','_optimizer_extended_cursor_sharing_rel','_optimizer_extended_cursor_sharing')

再次检查,发现游标只剩下一个

SQL_ID  CHILD_NUMBER  IS_BIND_SENSITIVE IS_BIND_AWARE
8fqtv1mu1y2fv 0 N N

IS_BIND_SENSITIVE='Y':--绑定变量更改导致执行计划更改的更改的潜在候选者
IS_BIND_AWARE='Y':--传入不同的绑定变量,导致执行计划真正改变了

小节:通过开启共享游标,并关闭自适应游标共享,能大大缓解硬编码导致的硬解析问题

我这么操作是因为数据字典表数据不倾斜,同时代码助手全部是硬编码,正式环境切不可使用,因为会影响其它SQL的执行计划

目前我只知道有这么一种防止SQL硬编码的硬解析问题,如果各位有更好的硬编码但是不硬解析的方法可以告诉我,这个优化就能更进一步,或者让PLSQL developer软件使用绑定变量

----=============================================================----------

3.两者执行计划差距较大,核心问题

我的环境,是一个索引范围扫描

| * 27 |             TABLE ACCESS BY INDEX ROWID BATCHED | OBJ$              |    1 |    56 |    3 | 00:00:01 |
| * 28 |              INDEX RANGE SCAN                   | I_OBJ5            |    1 |       |    2 | 00:00:01 |
* 27 - filter(BITAND("O"."FLAGS",128)=0)
* 28 - access("O"."SPARE3"="U"."USER#" AND "O"."NAME"='V_$SESSION')

而开发环境是一个索引跳跃扫描

|* 24 |           TABLE ACCESS BY INDEX ROWID BATCHED| OBJ$              |     5 |   215 |   269   (0)| 00:00:01 |
|* 25 |            INDEX SKIP SCAN                   | I_OBJ5            |     5 |       |   266   (0)| 00:00:01 |
  24 - filter(BITAND("O"."FLAGS",128)=0)
  25 - access("O"."NAME"='FND_LOOKUP_VALUES_VL')
       filter("O"."NAME"='FND_LOOKUP_VALUES_VL')

且驱动顺序不一样,更多细节不进行讨论(主要我也没时间分析)


按平时我的操作,肯定是慢慢调整,但是这个是标准数据字典视图,我可以把这个SQL在本地开发环境调整的非常好,但是花费非常多的时间,而且失去了普遍性,因为每个环境的数据字典表的数据分布不一样,有没有“快速”,且“自动化调优”的方式?即使性能差一点也可以接受

1.把我的本地环境的执行计划基线,使用数据泵和DBMS_SPM导出,然后导入到开发环境

(本地是云桌面,文件导入不进来,导入进来也没有权限导入到数据库中,所以可以是可以,不建议,这个方法可以在DBA协助下,把测试环境的性能较好的执行计划写入正式环境中,使用场景不高,我也不演示了)

2.使用自动调优助手,生成性能较好的执行计划,然后固定。

详细过程见文件《优化代码助手查询数据字典的执行计划.sql》

验证结果略,因为我拿不到trace,分析不了,可以用SPM性能分析器分析对比前后执行计划的性能提升,但是比较花费时间去写