Oracle-AWR报告 <整>

Oracle-AWR报告

一. 简述

1.1 什么是AWR报告?

AWR (Automatic Workload Repository) 是自动负载信息库的英文缩写,是Oracle 10g以后版本提供的一种性能收集和分析工具,能提供一个时间段内整个系统资源使用情况的报告,通过报告可以了解一个系统的整个运行情况,生成的报告包括多个部分。

AWR每小时对 v$active_session_history 视图(内存中的ASH采集信息,理论为1小时)进行采样一次,并将信息保存到磁盘中,并且保留7天,7天后旧的记录才会被覆盖。这些采样信息被保存在 wrh$_active_session_history 视图(写入AWR库中的ASH信息,理论为1小时以上)中。而这个采样频率(1小时)和保留时间(7天)是可以根据实际情况进行调整的。

1.2 使用场景

  • 查看数据库详细运行状态
  • 测试过程中发现数据库出现瓶颈但无法定位到具体原因时,可以借用AWR报告进行分析定位。

数据库出现性能问题,一般都在三个地方:IO、内存、CPU,这三个地方又是息息相关的。当IO负载增大时,肯定需要更多的内存来存放,同时也需要CPU花费更多的时间来过滤这些数据。相反,CPU时间花费多的话,有可能是解析SQL语句,也可能是过滤太多的数据,倒不一定是和IO或内存有关系。

  • CPU:解析SQL语句,生成的执行计划。
  • 内存:SQL语句和执行计划都需要在内存保留一段时间,还有取到的数据,根据LRU算法也会尽量在内存中保留,在执行SQL语句过程中,各种表之间的连接,排序等操作也要占用内存。
  • IO:如果需要的数据不在内存中,则需要到磁盘中去取,就会涉及到物理IO了;还有表之间的连接数据太多,以及排序等操作内存放不下的时候,需要用到临时表空间,也会消耗物理IO。

ORACLE分配的内存中PGA一般只占20%,对于专用服务器模式,每次执行SQL语句、表数据的运算等操作,都在PGA中进行的,也就是说只能用ORACL分配内存的20%左右。如果多个用户都执行多表关联,而且表数据又多,再加上关联不当的话,内存就成为瓶颈了,所以优化SQL很重要的一点就是,减少逻辑读和物理读。

1.3 生成AWR报告

Linux使用sqlplus生成AWR报告,安装:Instant Client for Linux x86-64

下载basic和sqlplus的rpm包后,上传到tmp目录:

1
2
3
cd /tmp
rpm -ivh oracle-instantclientXXX-basic-XXX-1.x86_64.rpm
rpm -ivh oracle-instantclientXXX-sqlplus-XXX-1.x86_64.rpm

安装完毕后,生成的客户端目录在 /usr/lib/oracle/12.2/client64 ,在该目录下新建network目录,并编写tnsnames.ora文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir network
vi tnsnames.ora

testdb =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = XXX.XXX.XXX.XXX)(PORT = 1521))
)
(CONNECT_DATA =
(SID = testdb)
(SERVER = DEDICATED)
)
)

修改完成后按下 esc + :wq 保存退出。

配置环境变量:

1
2
3
4
5
6
7
8
9
10
11
vi ~/.bash_profile

export ORACLE_HOME=/usr/lib/oracle/12.2/client64
export TNS_ADMIN=$ORACLE_HOME/network
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/local/lib:$LD_LIBRARY_PATH:.
export ORABIN=$ORACLE_HOME/bin
export NLS_LANG=AMERICAN_AMERICA.ZHS16GBK
export ORACLE_SID=orcl
PATH=$PATH:$ORACLE_HOME/bin:$ORABIN

source ~/.bash_profile

登陆sqlplus:

1
2
3
4
5
6
7
8
9
sqlplus sys@XXX.XXX.XXX.XXX:1521/orcl AS SYSDBA
sql> @/u01/app/oracle/12c/rdbms/admin/awrrpt.sql

Enter value of report_type
# 意思是生成报告的格式有两种,html和txt,这里选择html
Enter value of num_days
# 收集几天的报告信息,数字,可以输入1
Enter value of begin_snap
# 输入开始快照id,要根据日志打印的快照id范围来填

生成的 .lst 文件直接拷贝为html打开即可。

二. 相关知识点

2.1 硬解析和软解析

(1)SQL执行过程

Oracle中SQL的执行过程:

  1. 语法检查(syntax check):检查此SQL的拼写是否符合语法。如关键字书写错误等。
  2. 语义检查(semantic check):诸如检查SQL语句中的访问对象是否存在及该用户是否具备相应的权限。如 ORA-00942: table or view does not exist 等。
  3. 对SQL语句进行解析(prase):利用内部算法对SQL进行解析,生成解析树(parse tree)及执行计划(execution plan)。
  4. 执行SQL,返回结果(execute and return)。

(2)什么是软、硬解析?

硬解析和软解析就发生在第三步。Oracle利用hash算法来取得SQL的hash值,然后在library cache里查找是否存在该hash值;

  • 假设存在,则将此SQL与cache中的进行比较;
  • 假设“相同”,就将利用已有的解析树与执行计划,而省略了优化器的相关工作。这也就是软解析的过程。

当然,如果上面的2个假设中任有一个不成立,那么优化器都将进行创建解析树、生成执行计划的动作。这个过程就叫硬解析。创建解析树、生成执行计划对于SQL的执行来说是开销昂贵的动作;以及会占据重要的门闩(latch)资源,严重的影响系统的规模的扩大(即限制了系统的并发行), 而且引起的问题不能通过增加内存条和cpu的数量来解决。所以应当极力避免硬解析,尽量使用软解析。

闩是锁的细化,可以理解为是一种轻量级的串行化设备。当进程申请到闩后,则这些闩用于保护共享内存的数在同一时刻不会被两个以上的进程修改。在硬解析时,需要申请闩的使用,而闩的数量在有限的情况下需要等待。大量的闩的使用由此造成需要使用闩的进程排队越频繁,性能则逾低下。

绝大多数latch问题都与没有使用绑定变量有关:

  • library-cache latch(库缓存latch)
  • 重做日志生成问题(redo-allocation latch,重做日志的分配latch )
  • 缓存竞争问题(cache-buffers LRU-chain latch,缓存的最近最少使用链latch)
  • 缓存中的热块(cache-buffers chain latch,缓存链latch)

软解析执行过程

  • 语法、语义及权限检查;
  • 将整条SQL hash后从库缓存中执行计划。

硬解析执行过程

  • 语法、语义及权限检查;
  • 查询转换,通过应用各种不同的转换技巧,会生成语义上等同的新的SQL语句,如 count(1) 会转为 count(*)
  • 根据统计信息生成执行计划,找出成本最低的路径,这一步比较耗时
  • 将游标信息(执行计划)保存到库缓存(library cache)。

还包括一种软软解析,指设置了session_cursor_cache后,Cursor被直接Cache在当前Session的PGA中的,在解析的时候只需要对其语法分析、权限对象分析之后就可以转到PGA中查找。

DDL和DML:

  • DDL:CREATE,DROP,ALTER等,必然进行硬解析。
  • DML:INSERT,UPDATE,DELETE,SELECT等,进行硬解析或软解析。

硬解析和软解析模拟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 硬解析,不使用绑定变量
select * from test where object_id=20;
select * from test where object_id=30;
select * from test where object_id=40;
select * from test where object_id=50;

# 软解析,使用绑定变量
declare
sql_stat varchar2(200);
p00 number(10);
p01 varchar2(20);
p02 varchar2(20);
begin
p00 := 3;
p01 := '321';
p02 := '34030';
sql_stat := 'update xxx_table set XXX_time = TO_NUMBER(TO_CHAR(SYSDATE,''YYYYMMDD.HH24MISS'')), XXX_no = '|| p00 ||', XXX_desc = '|| p01 ||' where XXX_ID = '|| p02 ||' and XXX_no != ''4'' ';
dbms_output.put_line(sql_stat);
execute immediate sql_stat;
end;

(3)Version Count

首次硬解析时同时创建Parent Cursor和Child Cursor(二者都是Shared Cursor,作为Library cache object存放于Hash Buckets中)。

当SQL再次执行时,首先计算Hash Code,与存放在Parent Cursor的Bucket中的Hash Value进行匹配。匹配到则遍历Child Cursor,可以重用则继续使用,否则重新生成Child Cursor。一个父游标下的子游标个数可以看作Version Count。

当Version Count很高时,默认超过20就会显示在AWR报告中的SQL ordered by Version Count中,只要超过100,就需要额外注意该SQL。

查看Cursor不能共享原因的方法:

  1. 查看parent cursor 的hash value 和address:

    1
    select sql_text, hash_value,address from v$sqlarea where sql_text like 'XXX%';
  2. 检查child cursor:显示结果为Y表示不能共享的原因。

    1
    select * from v$sql_shared_cursor where address = '0000000386BC2E58'

cursor的不可重用,也可能与cursor_sharing参数值设置有关。

V$SQL_SHARED_CURSOR explains why a particular child cursor is not shared with existing child cursors. Each column identifies a specific reason why the cursor cannot be shared.

Column Datatype Description
SQL_ID VARCHAR2(13) SQL identifier
ADDRESS `RAW(4 8)`
CHILD_ADDRESS `RAW(4 8)`
CHILD_NUMBER NUMBER Child number
UNBOUND_CURSOR VARCHAR2(1) (`Y
SQL_TYPE_MISMATCH VARCHAR2(1) (`Y
OPTIMIZER_MISMATCH VARCHAR2(1) (`Y
OUTLINE_MISMATCH VARCHAR2(1) (`Y
STATS_ROW_MISMATCH VARCHAR2(1) (`Y
LITERAL_MISMATCH VARCHAR2(1) (`Y
SEC_DEPTH_MISMATCH VARCHAR2(1) (`Y
EXPLAIN_PLAN_CURSOR VARCHAR2(1) (`Y
BUFFERED_DML_MISMATCH VARCHAR2(1) (`Y
PDML_ENV_MISMATCH VARCHAR2(1) (`Y
INST_DRTLD_MISMATCH VARCHAR2(1) (`Y
SLAVE_QC_MISMATCH VARCHAR2(1) (`Y
TYPECHECK_MISMATCH VARCHAR2(1) (`Y
AUTH_CHECK_MISMATCH VARCHAR2(1) (`Y
BIND_MISMATCH VARCHAR2(1) (`Y
DESCRIBE_MISMATCH VARCHAR2(1) (`Y
LANGUAGE_MISMATCH VARCHAR2(1) (`Y
TRANSLATION_MISMATCH VARCHAR2(1) (`Y
ROW_LEVEL_SEC_MISMATCH VARCHAR2(1) (`Y
INSUFF_PRIVS VARCHAR2(1) (`Y
INSUFF_PRIVS_REM VARCHAR2(1) (`Y
REMOTE_TRANS_MISMATCH VARCHAR2(1) (`Y
LOGMINER_SESSION_MISMATCH VARCHAR2(1) (`Y
INCOMP_LTRL_MISMATCH VARCHAR2(1) (`Y
OVERLAP_TIME_MISMATCH VARCHAR2(1) Mismatch caused by setting session parameter ERROR_ON_OVERLAP_TIME
SQL_REDIRECT_MISMATCH VARCHAR2(1) SQL redirection mismatch
MV_QUERY_GEN_MISMATCH VARCHAR2(1) Internal, used to force a hard-parse when analyzing materialized view queries
USER_BIND_PEEK_MISMATCH VARCHAR2(1) Cursor is not shared because value of one or more user binds is different and this has a potential to change the execution plan
TYPCHK_DEP_MISMATCH VARCHAR2(1) Cursor has typecheck dependencies
NO_TRIGGER_MISMATCH VARCHAR2(1) Cursor and child have no trigger mismatch
FLASHBACK_CURSOR VARCHAR2(1) Cursor non-shareability due to flashback
ANYDATA_TRANSFORMATION VARCHAR2(1) Is criteria for opaque type transformation and does not match
INCOMPLETE_CURSOR VARCHAR2(1) Cursor is incomplete: typecheck heap came from call memory
TOP_LEVEL_RPI_CURSOR VARCHAR2(1) Is top level RPI cursor
DIFFERENT_LONG_LENGTH VARCHAR2(1) Value of LONG does not match
LOGICAL_STANDBY_APPLY VARCHAR2(1) Logical standby apply context does not match
DIFF_CALL_DURN VARCHAR2(1) If Slave SQL cursor/single call
BIND_UACS_DIFF VARCHAR2(1) One cursor has bind UACs and one does not
PLSQL_CMP_SWITCHS_DIFF VARCHAR2(1) PL/SQL anonymous block compiled with different PL/SQL compiler switches
CURSOR_PARTS_MISMATCH VARCHAR2(1) Cursor was compiled with subexecution (cursor parts were executed)
STB_OBJECT_MISMATCH VARCHAR2(1) STB has come into existence since cursor was compiled
ROW_SHIP_MISMATCH VARCHAR2(1) Session does not support row shipping, but cursor built in one that did
PQ_SLAVE_MISMATCH VARCHAR2(1) Top-level slave decides not to share cursor
TOP_LEVEL_DDL_MISMATCH VARCHAR2(1) Is top-level DDL cursor
MULTI_PX_MISMATCH VARCHAR2(1) Cursor has multiple parallelizers and is slave-compiled
BIND_PEEKED_PQ_MISMATCH VARCHAR2(1) Cursor based around bind peeked values
MV_REWRITE_MISMATCH VARCHAR2(1) Cursor needs recompilation because an SCN was used during compile time due to being rewritten by materialized view
ROLL_INVALID_MISMATCH VARCHAR2(1) Marked for rolling invalidation and invalidation window exceeded
OPTIMIZER_MODE_MISMATCH VARCHAR2(1) Parameter OPTIMIZER_MODE mismatch (for example, all_rows versus first_rows_1)
PX_MISMATCH VARCHAR2(1) Mismatch in one parameter affecting the parallelization of a SQL statement. For example, one cursor was compiled with parallel DML enabled while the other was not.
MV_STALEOBJ_MISMATCH VARCHAR2(1) Cursor cannot be shared because there is a mismatch in the list of materialized views which were stale at the time the cursor was built
FLASHBACK_TABLE_MISMATCH VARCHAR2(1) Cursor cannot be shared because there is a mismatch with triggers being enabled and/or referential integrity constraints being deferred
LITREP_COMP_MISMATCH VARCHAR2(1) Mismatch in use of literal replacement

(4)绑定变量

日常使用中,硬解析过高常常因为SQL未使用绑定变量导致。

1
2
3
4
5
6
-- 使用绑定变量
select xxx from table_a where a = :a and b = :b;

-- 不使用绑定变量
select xxx from table_a where a = 1 and b = 'A';
select xxx from table_a where a = 2 and b = 'A';

Oracle接收SQL后,首先根据Hash算法得到Hash值,然后在共享池寻找是否有匹配的SQL。如果存在则直接用已有的SQL执行计划来执行,最后返回结果。如果不存在,则需要执行硬解析。

绑定变量只是起到占位的作用,让Oracle每次对用户发来的SQL做hash运算时,运算出的结果都是同样的Hash值,于是将所有的用户发来的SQL看作是同一个SQL来对象。

所以合理的使用绑定变量后,Oracle可以在共享池中命中相同SQL,然后从而避免了高昂的硬解析操作,以及减少latch争用,特别是在这个SQL调用次数较多和频繁的场景下。

(5)Oracle排查硬解析SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
-- 查看系统解析统计信息
select * from v$sysstat where name like '%parse%';
-- 硬解析统计信息,通过观察value变化可以来确定SQL是否走了硬解析
select name,class,value from v$sysstat where name = 'parse count (hard)';

select a.value,b.name from v$mystat a, v$statname b
where a.STATISTIC#=b.STATISTIC# and b.name like '%parse%';

-- 查看指定SQL内容的执行信息,如解析次数,加载次数,执行次数等
select sql_text,s.parse_calls,loads,executions,FORCE_MATCHING_SIGNATURE
from v$sql s where sql_text like 'update XXX_table%';

select sql_id,sql_text,executions,last_load_time from v$sqlarea
where sql_text like 'update XXX_table%'
and last_load_time > trunc(sysdate-1/24) order by last_load_time desc;

-- 按解析次数排序,找出解析次数较多的SQL
SELECT sql_id,substr(sql_text,1,40) sql, parse_calls, executions, hash_value,address FROM V$SQLAREA WHERE parse_calls > 10 order by parse_calls desc;
-- 变种1
SELECT sql_id,sql_text, parse_calls,loads, executions, hash_value,address FROM V$SQLAREA
WHERE parse_calls > 100
and kept_versions = 0
and executions < 2*parse_calls
order by parse_calls desc;
-- 变种2
SELECT sql_id,sql_text, parse_calls,loads, executions, hash_value,address FROM V$SQLAREA
WHERE executions > 10000
order by executions desc;

-- 查找指定的SQL内容,Oracle根据hash_value来查找SQL,所以不同hash值会解析多次
SELECT sql_id,sql_text, parse_calls,loads, executions, hash_value,address
FROM V$SQLAREA
WHERE sql_text like '%SELECT xxx FROM xxx%'

硬解析的SQL会在 v$sql_shared_cursor 返回字段为Y:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
SELECT * from v$sql t where t.SQL_ID = '5auzajv4fy8gm';
select * from v$sqlarea t where t.SQL_ID = '5auzajv4fy8gm';
select * from v$sql_shared_cursor where SQL_ID = '5auzajv4fy8gm';

-- 分组汇总硬解析次数最多的SQL内容
SELECT substr (sql_text,0, 40), COUNT(*) FROM v$sql_shared_cursor c, v$sql s
where c.sql_id = s.sql_id
and (UNBOUND_CURSOR = 'Y' OR
SQL_TYPE_MISMATCH = 'Y' OR
OPTIMIZER_MISMATCH = 'Y' OR
OUTLINE_MISMATCH = 'Y' OR
STATS_ROW_MISMATCH = 'Y' OR
LITERAL_MISMATCH = 'Y' OR
FORCE_HARD_PARSE = 'Y' OR
EXPLAIN_PLAN_CURSOR = 'Y' OR
BUFFERED_DML_MISMATCH = 'Y' OR
PDML_ENV_MISMATCH = 'Y' OR
INST_DRTLD_MISMATCH = 'Y' OR
SLAVE_QC_MISMATCH = 'Y' OR
TYPECHECK_MISMATCH = 'Y' OR
AUTH_CHECK_MISMATCH = 'Y' OR
BIND_MISMATCH = 'Y' OR
DESCRIBE_MISMATCH = 'Y' OR
LANGUAGE_MISMATCH = 'Y' OR
TRANSLATION_MISMATCH = 'Y' OR
BIND_EQUIV_FAILURE = 'Y' OR
INSUFF_PRIVS = 'Y' OR
INSUFF_PRIVS_REM = 'Y' OR
REMOTE_TRANS_MISMATCH = 'Y' OR
LOGMINER_SESSION_MISMATCH = 'Y' OR
INCOMP_LTRL_MISMATCH = 'Y' OR
OVERLAP_TIME_MISMATCH = 'Y' OR
EDITION_MISMATCH = 'Y' OR
MV_QUERY_GEN_MISMATCH = 'Y' OR
USER_BIND_PEEK_MISMATCH = 'Y' OR
TYPCHK_DEP_MISMATCH = 'Y' OR
NO_TRIGGER_MISMATCH = 'Y' OR
FLASHBACK_CURSOR = 'Y' OR
ANYDATA_TRANSFORMATION = 'Y' OR
PDDL_ENV_MISMATCH = 'Y' OR
TOP_LEVEL_RPI_CURSOR = 'Y' OR
DIFFERENT_LONG_LENGTH = 'Y' OR
LOGICAL_STANDBY_APPLY = 'Y' OR
DIFF_CALL_DURN = 'Y' OR
BIND_UACS_DIFF = 'Y' OR
PLSQL_CMP_SWITCHS_DIFF = 'Y' OR
CURSOR_PARTS_MISMATCH = 'Y' OR
STB_OBJECT_MISMATCH = 'Y' OR
CROSSEDITION_TRIGGER_MISMATCH = 'Y' OR
PQ_SLAVE_MISMATCH = 'Y' OR
TOP_LEVEL_DDL_MISMATCH = 'Y' OR
MULTI_PX_MISMATCH = 'Y' OR
BIND_PEEKED_PQ_MISMATCH = 'Y' OR
MV_REWRITE_MISMATCH = 'Y' OR
ROLL_INVALID_MISMATCH = 'Y' OR
OPTIMIZER_MODE_MISMATCH = 'Y' OR
PX_MISMATCH = 'Y' OR
MV_STALEOBJ_MISMATCH = 'Y' OR
FLASHBACK_TABLE_MISMATCH = 'Y' OR
LITREP_COMP_MISMATCH = 'Y' OR
PLSQL_DEBUG = 'Y' OR
LOAD_OPTIMIZER_STATS = 'Y' OR
ACL_MISMATCH = 'Y' OR
FLASHBACK_ARCHIVE_MISMATCH = 'Y' OR
LOCK_USER_SCHEMA_FAILED = 'Y' OR
REMOTE_MAPPING_MISMATCH = 'Y' OR
LOAD_RUNTIME_HEAP_FAILED = 'Y' OR
HASH_MATCH_FAILED = 'Y' OR
PURGED_CURSOR = 'Y' OR
BIND_LENGTH_UPGRADEABLE = 'Y' OR
USE_FEEDBACK_STATS = 'Y')
group by substr (sql_text,0, 40)
order by count (*) desc;

2.2 Latch和Mutex

(1)Latch

未完待续……

(2)Mutex

2.3 PGA

Program Global Area 程序全局区,对当前运行程序可见的内存。主要由排序工作区和临时结果集组成,产生场景:

  • 临时结果集作为SQL操作(表关联或子查询)中间步骤创建。
  • 在SORT-MERGE、ORDER BY或GROUP BY操作中需要使用内存来对数据进行排序。
  • 为了进行散列联结等操作,使用内存来创建散列结构。为联结中的一个表创建一个临时的散列表。部分如GROUP BY操作也可能使用散列区域。

如果临时数据过大无法放入PGA,会被写入临时表空间的临时段中。

三. AWR解析

3.1 数据库信息

  • 数据库的版本
  • 数据库 DBID
  • 数据库实例名称及实例号
  • 数据库最近一次启动时间
  • 数据库版本
  • 数据库是否为rac

3.2 服务器信息

  • 数据库主机名
  • 数据库主机平台
  • 服务器CPU及核数
  • 服务器CPU个数
  • 服务器内存大小

3.3 SnapShot信息

  • awr报告的起止时间以及当时的session数量等
  • awr报告持续时间
  • DB 时间

DB Time= session time spent in database.

DB Time= CPU Time + Non IDLE wait time.

可以看到DB Time比 Elapsed大,如果大很多并且有性能问题,需再进一步分析

3.4 Shared Pool Statistics

% SQL with executions>1指的是执行次数大于1的SQL比例,越大越好,如过小则可能是为使用绑定变量导致

  • Memory Usage %

    • 该指标指的是Oracle数据库Shared Pool的使用率,适用于OLTP系统。

    • 该指标一般要求在70%-85%之间。

    • 过高说明Shared Pool 剩余空间不足,我们需要查找具体原因或者增加其空间。

    • 过低说明Shared Pool 剩余空间过多,造成内存的浪费需要减少Shared Pool 的大小。

  • % SQL with executions>1 :

    • 该指标指的是Shared Pool 中的SQL语句执行次数大于1的比例,适用于OLTP系统。
    • 该指标越高越好,如过低说明SQL 未被复用,请检查绑定变量的问题。
    • 该指标可和上节Instance Efficiency Percentages 部分中的Parse相关指标作对比。
  • % Memory for SQL w/exec>1

    • 该指标指的是执行次数大于1的SQL语句占用的Shared Pool内存比例。
    • 该指标越低说明Shared Pool内存更多的被用在存储不能复用的语句上。从侧面反映出硬解析比较严重。

3.5 Load Profile

了解系统负载的情况:

  • DB CPU(s) per second:说明的是每秒钟同时工作的CPU数量,从主机配置可以看到共24个虚拟cpu,而DB CPU(s) per second只有0.2则说明cpu没有瓶颈

其次关注hard parses和 parses的比例,如硬解析率非常高则需要查看cursor_sharing参数和应用程序的绑定变量问题,一般都是由于绑定变量引起的。

3.6 Instance Efficiency Percentages

上面的百分比越高越好,% Non-Parse CPU 指的是数据的CPU资源有78.03%用在非解析上,不算高。理论上说上述比例应接近100%。

3.6.1 Buffer Nowait %

该指标指的是可立即访问SGA 中所有数据而不用等待的次数的比例,该指标应接近100%

如发现该指标过低,则检查awr报告中 Buffer Wait Statistics 部分来查看哪种类型的块导致等待。

3.6.2 Redo NoWait %

该指标指的是redo条目(redo-entries)在redo log中可立即生成而不用等待的次数与全部redo entries的比例。redo entry对应的是每一个DML语句。

计算公式:100 x (1- (redo log space requests/redo entries)

  • redo log space requests 该请求会在数据库进程请求生成redo entry,而这时redo log空间满的情况下发生,这时数据库会被动的进行日志切换以使事务可以继续进行。
  • redo entries 会在每次redo entry 写入 redo log时增加

上述2个值可通过视图 v$sysstat 查看,注意它们的值是累积的。一般来说我们需要保持redo log space requests的值不增长。

1
select * from v$sysstat where name in ('redo log space requests', 'redo entries');

如果该参数过低:

  1. 如redo log切换十分频繁(约15分钟切换一次),则需要增加online redo log大小。
  2. 如果redo log切换不频繁,则说明可能是磁盘IO性能太慢,需将online redo log放置到高性能磁盘中。

3.6.3 Buffer Hit %

该指标指的是数据库请求的数据在buffer cache中直接命中的比例。该指标越高代表oracle在buffer cache直接找到需要的数据越多,从而不需要从磁盘进行读取。buffer cache(内存)中读取的速率是从磁盘读取速率的成百上千倍。

该参数在OLAP和DSS系统中不太重要,因为他们有大量的全表扫描或者并行操作。并行操作有时会跳过buffer cache 而使用PGA。该参数对于OLTP系统非常重要,需要保持在90%以上,因为其有大量连续的操作,从磁盘读取将大大影响系统性能。

如该指标过低可使用 data buffer cache advisory 查看合适建议并修改 db_cache_size 参数大小。

3.6.4 In-memory Sort %

该参数反应了内存内排序和磁盘排序之间的比例,计算公式为 :(DeltaMemorySorts / (DeltaDiskSorts + DeltaMemorySorts)) * 100 。disk sort的在temp表空间中进行,他的速度比内存排序慢成百上千倍。

该查询的值为累计值,计算时应取观察时间段的差值(从instance启动开始)。

1
select * from v$sysstat where name in ('sorts (memory)', 'sorts (disk)');

存储区域:

  • 专用服务器( dedicated )类型中,排序区域分配在PGA中。
  • 共享服务器(shared)类型中,排序区域在 large pool 中,由于是共用的无法手动指定各个session使用的大小。

如该指标过低,需增加sort area 的大小。in-memory sorts的大小被sort_area_size或者pga_aggregate_target控制。

3.6.5 Library Hit %

library cache hit ratio,指的是将要执行的SQL 语句或者PL/SQL 代码已经存在于shared pool中的library cache中并可复用。

查看:

  • 监控库缓冲命中率及重载率(9i及以上):

    1
    2
    3
    4
    5
    6
    select sum (pins) "Eexcutions",
    sum(pinhits) "Hits",
    round((( sum (pinhits) / sum (pins)) * 100),2 ) "PinHitRatio",
    sum(reloads) "Misses",
    round((( sum (pins) / (sum (pins) + sum(reloads))) * 100 ),2) "RelodHitRatio"
    from v$librarycache;
  • 查看库缓冲命中率(10g及以上):

    1
    select * from v$sysmetric where metric_name = 'Library Cache Hit Ratio';

如果该指标过低说明SQL过早的被挤出shared pool,可能是由于shared pool过小导致。需要和软解析(soft parse)率进行比较,如过两者都低,需检查解析问题,如绑定变量是否使用。

3.6.6 Soft Parse %

软解析指的是需要执行的SQL语句或PL/SQL程序可以在library cache中找到并重复使用。计算公式为:((DeltaParseCountTotal - DeltaParseCountHard) / DeltaParseCountTotal) * 100

可以通过 v$sysstat 查看到,注意该参数是累积的,计算时需计算时间段的差值:

1
select * from v$sysstat where name in ('parse count (hard)', 'parse count (total)');

如该指标过低(80%),需检查是否有绑定变量问题,并查看parse 的TOP SQL:

1
select * from v$sysmetric where metric_name = 'Library Cache Hit Ratio';

如果该指标很高,也不代表不能优化,也需要查看排在前列的语句是否需要优化

3.6.7 Execute to Parse %

该指标是SQL执行次数和解析次数的比值,计算公式为:round(100*(1-parse/exe),2)

从公式可以看出:

  • 当parse和execute相差不大时,比值趋近于0,说明每次执行都会进行解析
  • 当parse远小于execute使,比值接近1,说明解析一次可以执行多次,这是非常好的

可以通过 v$sysstat 查看到,注意该参数是累积的,计算时需时时间段的差值:

1
select * from v$sysstat where name in ('parse count (total)', 'execute count');

有人会建议设置cursor sharing = similar,会针对相似语句使用软解析,但这样是不可取的,可能会导致性能问题。

若该指标过低,往往是开发人员的程序造成的,如未使用绑定变量。可参考 官方

  • You should do it in a single SQL statement if at all possible.
  • If you cannot do it in a single SQL Statement, then do it in PL/SQL.
  • If you cannot do it in PL/SQL, try a Java Stored Procedure.
  • If you cannot do it in Java, do it in a C external procedure.
  • If you cannot do it in a C external routine, you might want to seriously think about why it is you need to do it…

3.6.8 Latch Hit %

该指标指的是latch不需要等待即可获取的比例。计算公式为:SELECT (1 - (Sum(misses) / Sum(gets))) * 100 FROM v$latch;

可以从 v$latch 视图获取相关信息:

  • GETS:以 willing-to-wait 模式请求latch的次数
  • MISSES:以 willing-to-wait 模式请求latch但是需要等待的次数
  • SLEEPS:以 willing-to-wait 模式请求latch需要等待并且超时的次数
  • IMMEDIATE_GETS:以no-wait模式请求latch的次数
  • IMMEDIATE_MISSES:以no-wait模式请求latch且失败(miss)的次数
  • SPIN_GETS:以willing-to-wait模式请求latch需要等待,但是在spin中获得的次数

latch是Oracle的一种轻量级的锁,用于保护共享内存,如确保一个数据块同一时间只能被一个session访问等等

  • Cache Buffer Chains
  • Redo Copy Latch
  • …..

latch获取有2种方式

  1. willing-to-wait:大部分latch采用如下模式,若第一次未取得latch时采用等待的方法。

  2. no-wait:少部分latch采用这种模式,当第一次获取不到该latch时就不进行等待,直接进入sleep状态。

如此指标低于90%则说明latch等待严重,可查看awr报告的等待事件部分。

如上图表明library cache存在冲突。

3.6.9 Parse CPU to Parse Elapsd %

该指标指的是解析过程中CPU时间占的比重。由于解析需要CPU进行操作,如在解析过程中有什么东西阻止进程访问CPU,则会导致该比例过小。如该比例为100%说明解析过程中没有等待。该指标的计算公式为:(parse time cpu/parse time elapsed)*100

数值可从 v$sysstat 视图获取,注意该参数是累积的,计算时需时时间段的差值:

1
select * from v$sysstat where name in ('parse time cpu', 'parse time elapsed');

如此指标过低说明可能为shared pool 存在冲突,可能为shared pool过小或未使用绑定变量所致。

3.6.10 % Non-Parse CPU

该参数的意义就像是字面上的,表明的是用在非解析上面的CPU时间。该指标的计算公式为:(parse time cpu/CPU used by this session)*100

数值可从 v$sysstat 视图获取,注意该参数是累积的,计算时需时时间段的差值:

1
select * from v$sysstat where name in ('parse time cpu', 'CPU used by this session');

如此指标过低(95%)说明CPU时间用在解析上的时间过多,一般是由于SQL未复用导致,也就是未使用绑定变量。

3.7 Top 10 Foreground Events by Total Wait Time

这里是排名前十的前台等待事件:

  1. 首先看wait class栏位,如果是 User I/O , System I/O, Others这种的可以不用太担心,如发现Concurrency这类等待需要特别关心
  2. 其次看等待时间,wait avg=total wait time(总等待时间)/waits(等待次数),最主要看平均等待时间是否正常

3.7.1 db file sequential read

  • 物理读发生在一个用户需要的数据块不在SGA,从而将其从磁盘读取到SGA中。如果此时别的会话需要该数据块则必须等待这个过程结束,这时就产生了等待。
  • 顺序读是物理读的一种方式,这里的顺序指的是读取数据块到一个连续的内存区域,而且总是读取单个数据块(single-block read)。

如果该等待严重说明数据块存在严重的争用情况。

单个数据块读(single-block read)是由SQL语句引起的,用户发出或者递归调用,一般发生在以下情况:

  • 索引扫描
  • 表扫描(access by rowid)
  • 全表扫描(很少发生,例如刚好在extent边缘恰巧被分割成单块,或者已经在buffer cache中)

由于物理读是非常正常的,出现这个等待事件不意味着数据库出现性能问题。但是如果在AWR报告等待事件相关中看到其处于非常前的位置时就需要引起我们的注意了。

特别需要关注Avg Waits 参数,最好小于10ms,这里可采用如下方法进行解决:

  • 将数据文件放在高速磁盘中,提高读取性能,避免热块。
  • 将数据文件放在LUN(即一些存储设备)中,可确保数据块分散在足够多的磁盘中。

在优化磁盘的同时,我们还需要注意应用程序的SQL语句问题,因为一般这种等待都是由SQL语句造成的,我们需要找出找出相应的SQL语句。可能是索引使用不当导致,这时我们可以定位到具体的表或索引,通过执行计划判断索引是否合理,是否需要走全表扫描等等方式来进行优化。

如下是一些常用的诊断方式,通过如下方式定位到具体的会话,在通过sql_id或hash_value找出具体的语句用于优化:

  1. 查看当前正在等待的会话:

    我们可以查看 v$session_wait 视图的TIME_WAITED栏位来定位当前哪个会话等待sequential read过长时间(实时)。

    1
    select * from v$session_Wait where event = 'db file sequential read'
    • P1代表File ID,可通过dba_data_File视图的FILE_ID字段看出是哪个数据文件。
    • P2代表 First block,即该块在数据文件上开始的位置。
    • P3代表块数,由于sequential read为单块读,则该值始终为1。

    我们可以通过P1,P2参数得出对象的名称和类型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    select
    segment_name,
    segment_type
    from
    dba_extents
    where
    file_id = 128
    and
    3277531 between
    (block_id and block_id + blocks - 1);
  2. 查看从实例启动以来等待的会话:

    使用 v$session_event 视图来定位哪个会话等待sequential read过长时间(非实时)。也可使用 v$system_event 视图查看系统整体的等待事件。

    1
    2
    3
    4
    5
    SELECT sid, total_waits, time_waited
    FROM v$session_event
    WHERE event='db file sequential read'
    and total_waits>0
    ORDER BY 3 desc,2

    注意由于SID是可以复用的,这样查出来的有可能有问题。比如查看SID为617的会话对应的语句也有可能是上个SQL语句导致的sequential read等待,这点需要注意。

  • 查看高物理读的数据文件:可以通过awr报告中的 Tablespace IO StatsFile IO Stats 区域来定位最多IO操作的表空间和数据文件,如果可以请将其放置在高速的磁盘中(SSD)。

  • 查看高物理读的SQL语句:同样可以查看v$sql中高物理读的语句以及awr报告中的 SQL ordered by Reads 区域

3.7.2 db file scattered read

离散读是物理读的一种方式,这里的离散指的是读取数据块到一块离散(不连续)的内存区域,而且一般读取多个数据块(multi-block read),可能为单个数据库。每次读取的块数由 DB_FILE_MULTIBLOCK_READ_COUNT 参数控制,这点不同于sequential read。

下图为各种读取方式的比较:

多数据块读(multi-block read)是由SQL语句引起的,用户发出或者递归调用,一般发生在以下情况:

  • 全表扫描( full table scans )
  • 索引快速全扫描( index fast full scans)

在优化磁盘的同时,我们还需要注意应用程序的SQL语句问题,因为一般这种等待都是SQL语句造成的,我们需要找出相应的SQL语句:

  1. 通过执行计划进行优化判断全表扫描或者索引全扫描是否合理,是否使用了合适的驱动表,以需要达到减少物理读和逻辑读的目的。
  2. 执行计划中 HASH JOINSORT MERGE 动作(operation)会导致scattered read。
  3. 可增加 DB_FILE_MULTIBLOCK_READ_COUNT 参数的值来减少IO次数。
  4. 调整 HASH_AREA_SIZEOPTIMIZER_INDEX_COST_ADJ 参数的值也可用来优化scattered read。
  5. 保证统计信息的及时性。

与db file sequential read的区别在event值为 db file scattered read

3.7.3 log file sync

当用户提交(commit)语句时,一个进程会建立一个redo记录并把它拷贝至SGA中的log buffer中,然后这个进程会通知LGWR进程再将log buffer中的内容写入日志文件(redo file)中,同时清空log buffer的内容,最后返回完成消息,这就完成了一次commit操作。

commit动作在LGWR进程没有返回完成消息前是不会完成的,我们把LGWR将log buffer中的内容写入日志文件(redo file)以及返回完成消息的这段时间标记为log file sync等待事件,它有个1s的超时时间。这个等待事件往往伴随着log file parallel write等待事件。

log buffer大小:

1
SQL> show parameter log_buffer

这里需要注意的是LGWR写log buffer内容至日志文件有多种情况:

  • 每三秒钟
  • 每一次commit
  • 当其1/3满的时候
  • 当其达到1M的时候

查看LGWR进程等待情况(整体):通过上面的讲解我们知道log file sync事件和LWGR进程相关,我们可以查询

1
2
3
4
select sid, event, time_waited, time_waited_micro
from v$session_event
where sid in (select sid from v$session where program like '%LGWR%')
order by 3

可以看到LGWR进程主要的等待有哪些,哪些等待比较严重:

  1. rdbms ipc message表示LGWR正在等待写redo log,表示其处于空闲状体,我们不必理会。
  2. log file single/parallel write即我们今天所说的LGWR写redo文件。

查询当前LGWR进程状态(实时):

1
2
3
4
select a.*
from v$Session_wait a, v$session b
where a.sid = b.sid
and b.program like '%LGWR%';

如发现为log file write等说明目前LGWR进程正在繁忙。如等待事件为log file parallel write 则其参数意义如下

  • P1:需要写入的redo log的数量,即日志文件组的成员数量。
  • P2:需要写入每个redo log 成员的redo block数。
  • P3:写入完成需要进行的I/O请求次数。

如果log file sync等待事件占有过多的CPU时间,我们就需要注意了:

  1. 低速的磁盘可能会导致LGWR进程写文件较慢从而导致log file sync等待,我们可以简单的通过avg waits来判断,如超过15ms则说明磁盘可能是瓶颈,需要放到高速的磁盘,另外加日志组中成员文件放在不同的磁盘中。
  2. 服务器CPU内存资源不足会导致进程相应缓慢,同样会增加log file sync等待,所以在调优时首先保证系统资源充足。
  3. 数据库锁及latch也会影响log file sync等待。
  4. 过大的log buffer大小,log buffer过大可能导致刷新过于次数过低,从而导致单次刷新过慢。
  5. 过多的commit操作,通过上面我们知道每次commit操作都会导致LGWR写操作,如commit过多则该等待则会明显的上升。

3.7.4 log file parallel write

为了冗余考虑,redo log组一般都会有多个成员,log file parallel write中的parallel指的是并行的写入多个redo log成员文件。log file parallel write指的是LGWR进程并行的将log buffer中的内容写入redo log,在全部写入到所有redo log前的等待计入log file parallel write 等待事件。

查看redo log文件情况:

1
2
3
SELECT thread#, group#, members, bytes / 1024 / 1024 byte_mb, status
FROM v$log
order by thread#, group#;

log file sync(LFS)和log file parallel write(LFPW)对比:

通过上面的定义我们知道LFS和LFPW都是等待LGWR进程完成I/O操作。

  • LFS是用户进程等待LGWR进程完成I/O操作。
  • LFPW是LGWR进程本身等待其I/O操作完成。

例如有五个用户进程同时commit,每个完成耗时都是10ms。则LFS次数增加五次,LFS的wait time增加50ms。而LFPW次数增加一次,LFPW的wait time增加10ms。注意LGWR 进行写日志动作原因有很多,用户commit只是其中一个。

  • 每三秒钟
  • 每一次commit/rollback
  • 当其 1/3满的时候,这个由_LOG_IO_SIZE参数控制
  • 当其达到1M的时候

减少日志组中成员的数量可减少I/O此时从而减少log file parallel write等待。

查看日志切换频率:直接将如下代码执行,PLSQL请使用command界面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
column h0 format 999
column h1 format 999
column h2 format 999
column h3 format 999
column h4 format 999
column h5 format 999
column h6 format 999
column h7 format 999
column h8 format 999
column h9 format 999
column h10 format 999
column h11 format 999
column h12 format 999
column h13 format 999
column h14 format 999
column h15 format 999
column h16 format 999
column h17 format 999
column h18 format 999
column h19 format 999
column h20 format 999
column h21 format 999
column h22 format 999
column h23 format 999
column avg format 999.99
column day format a6

SELECT TRUNC (first_time) "Date", TO_CHAR (first_time, 'Dy') "Day", COUNT (1) "Total",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '00', 1, 0)) h0,
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '01', 1, 0)) "h1",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '02', 1, 0)) "h2",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '03', 1, 0)) "h3",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '04', 1, 0)) "h4",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '05', 1, 0)) "h5",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '06', 1, 0)) "h6",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '07', 1, 0)) "h7",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '08', 1, 0)) "h8",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '09', 1, 0)) "h9",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '10', 1, 0)) "h10",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '11', 1, 0)) "h11",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '12', 1, 0)) "h12",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '13', 1, 0)) "h13",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '14', 1, 0)) "h14",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '15', 1, 0)) "h15",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '16', 1, 0)) "h16",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '17', 1, 0)) "h17",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '18', 1, 0)) "h18",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '19', 1, 0)) "h19",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '20', 1, 0)) "h20",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '21', 1, 0)) "h21",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '22', 1, 0)) "h22",
SUM (DECODE (TO_CHAR (first_time, 'hh24'), '23', 1, 0)) "h23", ROUND (COUNT (1) / 24, 2) "Avg"
FROM gv$log_history
WHERE first_time >= trunc(SYSDATE) - 30
and thread# = inst_id
GROUP BY TRUNC (first_time), TO_CHAR (first_time, 'Dy')
ORDER BY 1 DESC;

3.7.5 log buffer space

log buffer space这个等待事件一般来说很少发生,一旦等待比较严重往往说明是系统的设置问题。Oracle的一些DML操作(insert,update,insert)会产生redo条目,并存储在log buffer中,当发生以下情况时LGWR进程会把log buffer中的信息写入redo log,之后清空log buffer。当redo条目的产生速度快于LGWR清理的速度就会发生redo log space requests等待事件。

  1. 每三秒钟
  2. 每一次commit/rollback
  3. 当其 1/3满的时候,这个由_LOG_IO_SIZE参数控制
  4. 当其达到1M的时候

log buffer的大小由参数log_buffer参数决定。默认值为512k或者128k*CPU数量,一般来说这个默认值是够用的。如果系统DML操作很多且这个等待事件比较严重时可以考虑增加log buffer参数的大小。修改该参数需要重启数据库。

log buffer过大也会有问题。上面说到当log bufffer达到1/3满时LGWR进程会清空log buffer。如log buffer为10m,则意味着在没有commit/rollbak的情况下,需要等到3m才会切换,这样会导致LGWR写入redo log缓慢,从而导致log file sync等待。所以我们在调优log buffer space时不应该增加其他等待事件,需要取得一个平衡。

如何调优:

  1. IO性能不好会导致LGWR进程清空log buffer过慢从而导致log buffer space等待,这时需要将redo log放在高速的磁盘(SSD)或裸设备上。
  2. 减少应用的commit活动,或者使用nologging选项,仅更新表中需要更新的栏位。
  3. 物化视图更新使用fast代替complete模式。
  4. 查看 log file switch 是否频繁。

这个等待事件发生在会话在等待从远程数据库获取信息,该信息是通过dblink进行传输的,oracle把该等待事件归类于network类。

  • 查询实时的等待:

    1
    select * from v$session_wait where event= 'SQL*Net message from dblink'
    • P1代表driver id。

    • P2代表通过dblink传输的字节数。

  • 查询非实时的等待:

    1
    2
    3
    4
    select *
    from v$session_event
    where event like '%SQL*Net message from dblink%'
    order by time_waited desc

    注意这里的信息是从实例起来的汇总,同时由于SID是可以复用的,所以查看出来的SID并不代表上次的语句是这个等待。

当我们的SQL语句通过dblink访问远程数据库时,需要先将远程数据传输到本地再进行处理,在完成这个动作之前该会话处于SQL*Net message from dblink等待。该等待主要发生在如下几种情形:

  1. 数据库中有大量的物化视图需要定时同步远程数据库至本地。
  2. 数据库中有大量SQL语句需要通过dblink从远程获取数据。

如何调优:

  1. 针对物化视图我们首先需要减少不必要的物化视图数量,同时采用增量更新的方式,对于DML操作频繁的主表我们需要提高刷新频率。
  2. 针对SQL语句中有大量dblink的语句我们需要尽量减少dblink的访问。
  3. 如果不能减少可以通过在源库建立view的方式使其在源库执行。
  4. 也可以使用DRIVING_SITE hint的方式,手动指定oracle让其在源库执行。

这个等待事件发生在会话在等待一个远程数据库一个确认信息,确认其发送的数据远程数据库是否收到,该数据通过dblink发送。一般是由于目标服务器无法及时接受信息,Oracle将该等待事件列为Network类。

  • 查询实时的等待:

    1
    select * from v$session_wait where event = 'SQL*Net message to dblink'
    • P1代表driver id。
    • P2代表通过dblink传输的字节数。
  • 查询非实时的等待:

    1
    2
    3
    4
    select *
    from v$session_event
    where event like '%SQL*Net message to dblink%'
    order by time_waited desc

    注意这里的信息是从实例起来的汇总,同时由于SID是可以复用的,所以查看出来的SID并不代表上次的语句是这个等待

当我们的SQL语句通过dblink访问远程数据库时,需要先将远程数据传输到本地再进行处理,这时远端数据库会发送数据至本地,此时远端数据库如不能及时接受消息,会话处于SQL*Net message to dblink等待。该等待主要发生在如下几种情形:

  1. 数据库中有大量的物化视图需要定时同步远程数据库至本地。
  2. 数据库中有大量SQL语句需要通过dblink从远程获取数据。

如何调优:

  1. 针对物化视图我们首先需要减少不必要的物化视图数量,同时采用增量更新的方式,对于DML操作频繁的主表我们需要提高刷新频率。
  2. 针对SQL语句中有大量dblink的语句我们需要尽量减少dblink的访问。
  3. 如果不能减少可以通过在源库建立view的方式使其在源库执行。
  4. 也可以使用DRIVING_SITE hint的方式,手动指定oracle让其在源库执行。

3.7.8 SQL*Net message from client

这个等待事件发生在会话在完成请求后等待后续client发送命令,查询 v$session 可以看到处于这种等待事件的session状态为非活动。Oracle将该等待事件列为Idle类,对于此类等待我们无须理会。

通过如下语句查询实时的等待事件:

1
2
3
4
select *
from v$session_wait
where event = 'SQL*Net message from client'
order by sid

3.7.9 SQL*Net message to client

这个等待事件发生在会话发送数据到客户端时客户端无法及时接受时发生,Oracle将该等待事件列为Network类。

通过如下语句查询实时的等待事件

1
2
3
4
select *
from v$session_wait
where event = 'SQL*Net message to client'
order by sid

查询非实时的等待事件:

1
2
3
4
select *
from v$session_event
where event ='SQL*Net message to client'
order by time_waited desc

该等待一般是由网络问题导致。

3.7.10 cursor:mutex X

Cursor正在被解析并尝试以独占的方式获取时产生的等待事件。实质就是一些会话长期持有互斥锁,在latch/mutex发生争用,意味着解析会面临压力,解析SQL需要更长的时间。可能导致该事件的原因有:

  • 频繁硬解析。
  • High Version Count。
    • 绑定变量不匹配。如带绑定变量的 sql 由于 ACS 特性(自适应游标)导致。
    • 统计信息导致。
    • 绑定变量字段类型不一致。
    • 触发此问题也有可能是Oracle BUG导致,如多个PDB字符集不一致BUG25054064,以及High Waits On cursor: mutex X After Upgrading The Database to 12c, 18c & 19c (Doc ID 2625815.1)等。
  • Cursor失效。
  • Oracle BUG。
1
2
3
4
select * from DBA_HIST_ACTIVE_SESS_HISTORY where event LIKE '%mutex%';
select * from v$session_wait where event LIKE '%mutex%';
-- 查看SQL对应子游标个数,为Y的字段表示为何不能共享
select * from v$sql_shared_cursor where address = '00000000AEC7BB48';

可以查看故障时段做的hang分析日志,分析导致会话blocking的操作,以及如产生大量子Cursor的原因。

Bug 28794230 - 12.2 Cursor Mutex X Due To Sql Not Shared Because Of BIND_EQUIV_FAILURE (Doc ID 28794230.8)

该BUG修复版本:

  • 20.1.0
  • 19.10.0.0.210119 (Jan 2021) Database Release Update (DB RU)
  • 19.9.0.0.201020 (Oct 2020) Database Release Update(DB RU)
  • 18.13.0.0.210119 (JAN 2021) Database Release Update (DB RU)
  • 12.1.0.2.210119 (JAN 2021) Database Proactive Bundle Patch
  • 12.2.0.1.191015 (Oct 2019) Bundle Patch for Windows Platforms

可以查看AWR报告中的Mutex Sleep Summary统计项,并于正常的进行对比。

3.7.11 cursor:mutex S

会话在共享模式下请求一个mutex,此时另外一个会话在同一个Cursor对象上以独占模式持有它时导致的事件。

场景:

  • Change the reference count (“in flux”)= “new guy is interested / spinning”
  • Parent examination
  • When finding a cursor to execute, the parent must be examined. The examination of the parent is performed using the mutex, cursor: mutex S.
  • When the parent cursor has many child cursors involved, this waits will come as the server process has to traverse the entire list of child cursors under the parent to find a match.
  • Mutex is in the parent cursor.

3.8 Wait Events Statistics

3.8.1 Time Model Statistics

该视图说明的是各过程所占的资源比例:

我们注意到所有 % of DB Time 总和大于100%,因为这是一个累计的比例,下面DB CPU相关的过程包含在DB CPU中。我们需要注意的是一些异常的高占用情况,如hard parse elapsed time (硬解析时间)占用时间过长等。

  • Statistic Name:表示状态的名称。
  • Time (s):表示在awr报告时间内持续的时间。
  • % of DB Time:表示和DB Time相比其占用的比例。DB Time=DB CPU+Non-Idle Wait Time

Oracle进程(服务器,前台,影子等)的运行需要消耗CPU时间,我们把这些时间成为DB CPU ,注意后台进程的消耗不包括在DB Time中。如果一个进程不消耗CPU资源,它就会处于等待状态。等待包含空闲等待和非空闲等待,非空闲等待(顺序读,离散读,log sync,锁,闩等)所消耗的时间我们称为 Non-Idle Wait Time

  • sql execute elapsed time:表示执行SQL语句语句所用的时间,102%说明大部分DB Time都在执行SQL语句,这是非常好的,说明DB Time没有浪费在其他动作上,如解析。
  • DB CPU:如上面所说表示消耗CPU的时间。
  • parse time elapsed:表示解析所占用的时间。
  • hard parse elapsed time:表示硬解析所占用的时间。
  • DB time = DB CPU+Non-Idle Wait Time。
  • background elapsed time:表示后台进程持续的时间。
  • background cpu time:表示后台进程的CPU时间。

如何计算Non-Idle Wait Time?

Non-Idle Wait Time=DB Time-DB CPU 通过上面公式我们可以计算非空闲等待时间的时间.回到上图,Non-Idle Wait Time=18877-11432=7445。

80/20原则:这个原则告诉我们80%的等待是由20%的事件造成的,我们需要集中精力解决排行前几的事件

3.8.2 Operating System Statistics

该视图是操作系统层面的性能指标

该部分说明的是OS层面的一些状态信息,如CPU,IO

  • CPU使用率=BUSY_TIME/(BUSY_TIME+IDLE_TIME)=20%。
  • BUSY_TIME=SYS_TIME+USER_TIME。

这里需要注意%iowait,他代表CPU在等待io操作完成,这个可能是io过慢或者io操作过多导致。

3.8.3 Wait Class

这部分是根据等待的类型来排序等待事件。

3.8.4 Wait Events

这部分以具体的等待事件名称来进行排序,让我们可以清晰的知道是什么等待事件占的比例高。

3.8.5 Background Wait Events

这部分是以后台进程的等待事件来进行排序的,让我们知道后台等待事件哪些占用的比例高。

3.8.6 Service Statistics

这部分是根据服务名称来所消耗的DB Time进行排序的。SYS$USERS 指的是用户连接是没有制定服务名称时默认的服务名。

3.8.7 Service Wait Class Stats

这部分是将上一部分的DB Time细分后展现。

3.9 SQL Statistics

接下来是最重要的一块,能帮助我们定位占用相关资源的TOP SQL语句:

3.9.1 SQL ordered by Elapsed Time

上图为根据持续时间排序的SQL语句,所有栏位可根据字面上意思得出意义

  • 如executions过多可能会引起CPU占用率高
  • 如executions低,而elapsed time很高,则需要优化该SQL,降低执行时间

需要注意的是execution如果为0不代表未执行,代表在awr报告的持续范围内该语句未执行完成。

3.9.2 SQL ordered by CPU Time

这部分是按SQL语句消耗的CPU时间来排序的。

3.9.3 SQL ordered by Gets

该部分是按SQL语句的逻辑读来排序的。这里需要注意的是执行次数非常多的语句,可能会导致操作系统CPU使用率飙升。

3.9.4 SQL ordered by Reads

这部分是按SQL语句的物理读来排序的。

3.9.5 SQL ordered by Executions

这部分是按SQL语句的执行次数来进行排序的。这里需要注意的是执行次数非常多的语句,可能会导致操作系统CPU使用率飙升。

3.9.6 SQL ordered by Parse Calls

这部分是按SQL语句的解析次数进行排序的.

  • Parse Calls/Executions > 1 :说明每次执行需要多次解析。
  • Parse Calls/Executions < 1 :说明一次解析可供多次执行使用。

越接近1说明解析没有被复用。

3.9.7 SQL ordered by Sharable Memory

该部分按SQL语句使用的共享内存排序。

3.9.8 SQL ordered by Version Count

该部分按照SQL 语句的version count数量进行排序。version count 多说明相同语句在内存中shared pool没有被复用,需要查看具体原因。

3.9.9 Complete List of SQL Text

这里列出了上面提到的所有SQL语句的全部语句。


参考:

🔗 《Oracle awr 报告全解析

🔗 《Oracle-绑定变量binding variable解读