暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

ODBA 技能 4实战执行计划

IT界数据库架构师的漂泊人生 2019-11-30
323

OBA技能1-获取执行计划

OBA技能2-执行计划顺序

OBA技能3-执行计划顺序表连接

往往实际工作中并不像上面样,乃至市面上的书也获得你想要的执行计划。

前几天 测试人员照常发来的测试AWR过来。AWR的HOST CPU IDLE 空闲率为50%。虽然测试最高要求是使用率为80%,也就是说空闲率不能低于20%。每个查询语句不能高于200毫秒。观察该AWR等待事件都耗在DB CPU上。也就是说大部分DB TIME 数据库服务时间都在DB CPU处理工作中。那我们直接去看SQL状态部分,看ORDER BY CPU TIME列表。

发现所有的SQL都700毫秒,而且都涉及到一张表。CUST_INFO!

该表有6千万数据,最近分析时间11月20日。

拿个简单的SQL 去看看是怎么回事! 

SELECT * 
FROM CUST_INFO 
WHERE PARE_ID=:1
   AND  PARE_TYPE=:2
   AND  CUST_NAME=:3;



拿到像这样的,工作环境只有SQLPLUS。这些:1:2:3都是绑定变量,如果你直接使用值替代方式,估计执行计划是不一样的。

一  首先我们要拿到绑定变量的值。

  从V$SQL_BING_DATA 一般拿不到。只有开启SQL_TRACE方可拿到

ALTER SYSTEM SET EVENTS 

'SQL_TRACE [SQL:&&SQL_ID] WAIT=TRUE,BIND=TURE,PLAN_STAT=ALL_EXECUTIONS,LEVEL=12';

##让测试人员在跑一次

ALTER SYSTEM SET EVENTS 'SQL_TRACE [SQL:&&SQL_ID] OFF';


##获取跟踪文件

1 SHOW PARAMETER USER_DUMP_DEST

 ls -ltr .../*.trc

最前几个其中就是我们的跟踪文件

2 从视图回话获得

select TRACEFILE 
FROM V$PROCESS 
WHERE ADDR IN
(
   SELECT PADDR 
    FROM V$SESSION 
   WHERE SID=&SESSID
   );

二 其次 我们要把它放进SQLPLUS里执行一遍

2.1

 SQL> SET LINESIZE 1000
 SQLSET AUTOT ON
var v_paertype varchar2(50);
var v_paperid  varchar2(50);
var v_custname varchar2(50);
exec :v_paertype:='A';
....
select /*+ gather_plan_statistics */
       *
FROM custinfo c
where  c.PARE_TYPE=:v_papertype
  and  c.PARE_ID=:v_paperid
  and  c.CUST_NAME=:v_custname;


这里我们就模拟了绑定变量的SQL执行。虽然我们增加了收集的提示,会形成不同的SQLID。不过如果环境没有变的话,基本上生产的执行计划是一样的。var 是给变量定义类型,一般有VARCHAR2和NUMBER日期也用VARCHAR2,EXEC :给变量赋值。

 2.2 我们可以直接通过SQLPLUS显示该执行计划 SET AUTOT ON;

 2.3 也可以通过查V$SQLAREA WEHRE SQL_TEXT LIKE '%gather%' 来获得SQLID,然后去V$SQL_PLAN, V$SQL_PLAN_STATISTICS_ALL;获得其详细计划。


你说如果既然知道原来的SQLID不直接去PLAN获得呢?其实也可以,省得那么绕。从V$SQL获得。如下

SELECT SQL_ID,
PLAN_HASH_VALUE,
CHILD_NUMBER,
EXECUTIONS,
DISK_READS,
BUFFER_GETS,
CPU_TIME,
ELAPSED_TIME,
LAST_ACTIVE_TIME
FROM V$SQL S 
WHERE SQL_ID='&SQLID'
ORDER BY S.LAST_ACTIVE_TIME DESC;


通过这个脚本可以看到该SQL最近使用哪个执行计划,并且你自己通过计算ELAPSED_TIME/EXECUTIONS 每次消耗时间与AWR是否符合。


2.4 我们这样对比下,JDBC执行和我们人工模拟执行,时间是否相差太大,执行计划是否一样?


三查看相关对象信息

问题,我们的表统计信息如何?表上有几个索引,上面条件字段上是否有索引,索引是否需要重建?字段列上的数据分布如何? 那么我们经历下面几个查询获得相关信息

--1 获得表大小
select segment_name,bytes/1024/1024 as size_mb  from dba_segments where segment_name=upper('&tablename');

--2 获得表行数
select count(*) from table_name;
--3 获得表统计信息
select owner,table_name,num_rows,avg_row_len,last_analyzed
from dba_tables where table_name=upper('&tablename');

--4 获得表列信息
select column_name,data_type,num_distinct,low_value,high_value,density,num_nulls,histogram,last_analyzed
from dba_tab_cols where owner='PLPM' AND TABLE_NAME='&TABLE_NAME';

--5 获得表索引
SELECT INDEX_NAME,COLUMN_NAME,COLUMN_POSITION,DESCEND
FROM DBA_IND_COLUMNS
WHERE INDEX_OWNER='PLPM' AND TABLE_OWNER='PLPM'
AND TABLE_NAME='&TABLE_NAME';

--6 哪些列被索引
SELECT CUSTNAME,COUNT(*)
FROM PLPM.CUST_INFO
GROUP BY CUSTNAME
HAVING COUNT(*) >1
ORDER BY 2 DESC;

--8 收集统计信息和直方图

 EXEC DBMS_STATS.GATHER_TABLE_STATS
(ownname => 'SCOTT',tabname => 'DEPT',estimate_percent => 100,
method_opt => 'for all columns size auto',
degree => 4, cascade=>TRUE);


发现 CUSTNAME 有8万是一样的,而测试人员就是使用该值。导致走CUSTNAME索引超慢,因为索引除了FAST扫描外都是单块读。读一次回表一次。所以第8步走全面收集该表重要字段的直方图。


疑问 为什么好好的PARE_ID索引不走,却走CUSTNAME索引呢?那么我们需要启动事件来调查

 10046 SQL执行过程

 10053 优化器执行过程 为什么选择这个执行计划

很显然我们选择10053事件。下面是启动事件的方法。

四  启动事件调研

 ALTER SYSTEM|SESSION SET EVENTS 

'[EVENTNUMBER|IMMEDIATE] TRAE NAME EVENTANME 

[FOREVER] [,LEVEL LEVELNUMBER];'

 --开启10053跟踪

 ALTER SESSION SET EVENTS='10053 trace name context forever,level 12';

 --执行需要跟踪的SQL语句:可以我们人工模拟方式

--查找trace文件路径

SQL> SELECT value FROM v$diag_info  WHERE name='Default Trace File';

 关闭10053事件:
ALTER SESSION SET EVENTS '10053 trace name context off';



调查其他回话:

SQL> EXEC SYS.DBMS_SYSTEM.SET_EV (22, 339, 10053,1, '');

其他回话执行

--关闭跟踪

SQL> EXEC SYS.DBMS_SYSTEM.SET_EV (22, 339, 10053,0, '');

11GR以上可以直接调研SQLID

SQL> execute DBMS_SQLDIAG.DUMP_TRACE(p_sql_id=>'5s4ny8pxtdkyf', p_child_number=>0, p_component=>'Optimizer', p_file_id=>'SQL_TRACE_10053');


从TRC文件分析得知PARE_ID索引有问题,至于什么问题呢?我也不知道,不过我做了如下操作,

1 重建PARE_ID

2 刷新SQL,把该SQL语句从内存中刷出,以便生成新的执行计划。

select address,hash_value,executions,parse_calls from v$sql where sql_id='01wa3qb3vd2qm';

sys:

exec dbms_shared_pool.purge('0000001F2F280AF8,3350629075', 'c') -- address,hash_value,

最后问题解决了,从700毫秒降低到50毫秒!



第五 使用更简便的MONITOR工具


###查看超过4秒的慢查询
declare
 v_report clob;
 begin
  select count(distinct sql_id) into n_sql_num from v\$sql_monitor where module='JDBC Thin Client';
SELECT DBMS_SQLTUNE.report_sql_monitor(sql_id => i.sql_id,type => 'TEXT', report_level => 'ALL'AS report into v_Report FROM dual;
DBMS_OUTPUT.put_line(dbms_lob.substr(v_Report,32767));
end;
/
##sql_monitor 收集信息每秒刷新一次,SQL执行完保留一分钟
##提高提示 /*+ monitor */ /*+ no_monitor */
##一般监控300行的执行计划 隐含参数控制数量_sqlmon_max_planlines
##一般监控超过4秒的SQL _sqlmon_threshold控制时间


对于低于4秒的只有通过提示获得 monitor

var v_paertype varchar2(50);
var v_paperid  varchar2(50);
var v_custname varchar2(50);
exec :v_paertype:='A';
....
select /*+ monitor */
       *
FROM custinfo c
where  c.PARE_TYPE=:v_papertype
  and  c.PARE_ID=:v_paperid
  and  c.CUST_NAME=:v_custname;


对于过了很久,只能从历史表获得信息

SELECT * FROM DBA_HIST_SQL_PLAN;

SELECT * FROM DBA_HIST_SQL_SUMMARY;

SELECT * FROM DBA_HIST_SQLBIND;

SELECT * FROM DBA_HIST_SQLSTAT;

select * from dba_hist_active_sess_history;

select * from DBA_HIST_SNAPSHOT

dba_hist_active_sess_history:

TM_DELTA_TIME
在其时间间隔(以微秒计)TM_DELTA_CPU_TIME
TM_DELTA_DB_TIME
被累积
TM_DELTA_CPU_TIME
此会话在过去TM_DELTA_TIME
几微秒内花在CPU上的时间
TM_DELTA_DB_TIME
此会话在过去TM_DELTA_TIME
几微秒内在数据库调用中花费的时间
DELTA_TIME
自上次采样或创建会话以来的时间间隔(以微秒为单位),累计接下来的五个统计信息
DELTA_READ_IO_REQUESTS
此会话在过去DELTA_TIME
几微秒内发出的读取I O请求数
DELTA_WRITE_IO_REQUESTS
此会话在过去DELTA_TIME
几微秒内发出的写入I O请求数
DELTA_READ_IO_BYTES
此会话在过去DELTA_TIME
几微秒内读取的I O字节数
DELTA_WRITE_IO_BYTES
此会话在过去DELTA_TIME
几微秒内写入的I / O字节数
DELTA_INTERCONNECT_IO_BYTES
在过去DELTA_TIME
几微秒内通过I / O互连发送的I / O字节数

TM_DELTA_TIMEDELTA_TIME属于间隔时间内统计其间的TM_DELTA_CPU_TIME等。


一个正确的执行计划该如何让其稳定,而不被其它情况给打乱呢?

下期请看

ODBA 技能五 执行计划稳定



最后修改时间:2020-10-12 12:17:55
文章转载自IT界数据库架构师的漂泊人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论