解析对全部性能影响的可变因素非常多。在某些情况下,可以简单地忽略它。在其他情况下,它是造成性能问题的主要原因。如果存在解析问题,这通常代表应用不能正确处理它。这是个主要问题,因为通常要改变应用的行为,你需要修改相应的代码。开发人员需要知道解析的影响以及如何在写代码时尽可能避免相关问题。
1.1
识别解析问题
当寻找解析问题时,很容易会遇到强迫性的混乱优化。发生这类问题的原因是,多个动态性能视图包含的计数器详细记录了软解析、硬解析和执行的次数。这些计数器和基于它们的比率一样,都是没用的,因为它们没有提供关于解析花费时间的信息。
请注意对于解析,这才是真正的问题,因为它们没有标准周期。实际上,根据SQL语句的复杂度和它引用的对象,解析的周期通常会相差几个数量级。简单地说,这些计数器只能告诉你数据库引擎是否完成少量或大量的解析,而没有关于是否存在问题的信息。因此,实际中它们只用来做趋势分析。
如果你遵循第一部分和第二部分提供的建议,那么应该清晰地知道,唯一有效识别解析问题的方法,就是衡量数据库引擎花费了多少时间来解析SQL语句。如果要查找单个会话或是整个系统的全部时间信息,可以查询提供时间模型统计信息的动态性能视图。这些视图包括v$sess_time_model、v$sys_time_model和在12.1多租户环境下的v$con_sys_time_model。
例如,下面查询的输出显示了一个会话花费了大量时间(将近59%)来解析SQL语句的信息:
WITH db_time AS
(select sid, value
FROM v$sess_time_model
WHERE sid = 137
AND stat_name = 'DB time')
select ses.stat_name AS statistic,
round(ses.value / 1E6, 3) AS seconds,
round(ses.value / nullif(tot.value, 0) * 1E2, 1) AS"%"
FROM v$sess_time_model ses, db_time tot
WHERE ses.sid = tot.sid
AND ses.stat_name 'DB time'
AND ses.value > 0
ORDER BY ses.value DESC;
类似于这个查询所提供的信息可以用来判断解析是否存在问题。不幸的是,通过动态性能视图提供的时间模型统计信息,并不能帮助找出是哪个SQL语句导致的问题!
如果要寻找的是证据而不是线索,那么只有两个信息来源可以使用:由SQL跟踪生成的输出,和来自v$active_session_history或dba_hist_active_sess_history的活动会话历史。实际上,在SQL语句级别上,这些是仅有的可以提供关于解析定时信息的来源。这就是为什么本章我会仅基于SQL跟踪和活动会话历史来讨论解析问题的识别。
注意如果想使用活动会话历史来分析解析问题,需要知道四个限制。第一,使用活动会话历史不仅需要企业版,还需要Diagnostics Pack选件。第二,仅在11.1及之后版本中,活动会话历史才提供用于分析解析问题(in_parse和in_hard_parse标识)的必要信息。第三,不能使用企业管理器来做分析。第四,既然SQL语句的文本无法直接通过活动会话历史获得(只能得到SQLID),那么获得的信息并不一定足够用来识别导致解析问题的SQL语句。
解析问题主要有两种。第一种与持续时间非常短的解析有关,称为快速解析(quick parse)。当然需要大量执行才会引起注意。第二种解析问题与持续时间很长的解析有关,称为长解析(long parses)。通常是在SQL语句相当复杂或查询优化器需要很长时间才能生成高效执行计划时才会出现。这种情况下与执行次数无关。
1.1.1 快速解析
接下来介绍如何定位快速解析导致的性能问题。针对11.2.0.3版本的数据库,执行ParsingTest1.java文件中的类来生成负载样例。同样在PL/SQL、C(OCI)、C#(ODP.NET)和PHP(PECLOCI8扩展)中也实现了同样的处理过程。鉴于在第3章中介绍过两个探查器,TKPROF和TVD$XTAT,我会针对这两个探查器的输出文件来讨论相同的例子。可以在ParsingTest1.zip文件中找到跟踪文件和输出文件。
1.1.1.1 使用TKPROF
正如第3章中建议的那样, TKPROF 使用以下选项执行:
tkprof sys=no
sort=prsela,exeela,fchela
要开始分析输出文件,最好先看一下最后几行。在本例里,需要重点注意的是处理持续了大约14秒,应用程序执行了10000个SQL语句,并且所有SQL语句都不相同(user
SQL statements与unique SQL
statements相等)。
1 session
in tracefile.
10000 user SQL statements
in trace file .
0 internal sql statements in trace file .
10000 SQL statements in trace file.
10000 unique
SQLstatements in trace file.
120060 lines in trace file.
14 elapsed seconds in
trace file.
接着,需要检查输出中的第一个SQL语句执行了多长时间。由于指定了sort选项,SQL语句可以根据其响应时间进行排序。有趣的是,第一个游标的响应时间要小于百分之一秒(0.00)。换句话说,所有SQL语句的执行都低于百分之一秒。实际上,平均一个执行持续了1.4毫秒(14/10000)。这很明显意味着是短时间内处理的大量SQL语句占用了大量的响应时间,而不是少量长时间运行的SQL语句。
call
count cpu elapsed disk
query current rows
------- ------ -------- ---------- ---------- ----------
---------- ----------
Parse
1 0.00 0.00 0 0 0 0
Execute
1 0.00 0.00 0 0 0 0
Fetch
1 0.00 0.00 0 2 0 0
------- ------ -------- ---------- ---------- ----------
---------- ----------
total
3 0.00 0.00 0 2 0 0
这种情况下,要想判断是不是解析的问题,就必须检查总计的部分。根据执行统计信息,解析时间大约占用整个执行时间的95%(5.7/6)。这明显证明了数据库引擎除了解析什么都没做。
call
count cpu elapsed disk
query current rows
------- ------ -------- ---------- ---------- ----------
---------- ----------
Parse
10000 5.54 5.70 0 0 0 0
Execute
10000 0.17 0.15 0 0 0 0
Fetch
10000 0.13 0.14 0
23051 0 3048
------- ------ -------- ---------- ---------- ----------
---------- ----------
total
30000 5.86 6.00 0
23051 0 3048
下面这行也显示这10000个解析都是硬解析。注意,即使高比例的硬解析通常并不是我们想要的,但这未必就有问题。但这证明了存在次优的部分。
Misses in library cache during parse: 10000
执行统计信息的问题是缺少大约57%(1-6.00/14)的响应时间。实际上,通过查看汇总等待事件的表,可以看到等待客户端用去了6.24秒。然而,仍然有大约2秒(14-6.00-6.24)下落不明。
Elapsed times include waiting on following
events:
Event waited on Times Max. Wait
Total Waited
---------------------------------------- Waited
---------- ------------
SQL*Net message to client 10000 0.00 0.02
SQL*Net message from client 10000 0.02 6.24
latch: shared pool 5 0.00 0.00
log
file sync
1 0.00 0.00
当你知道解析出了问题时,明智的做法是查看一下SQL语句。本例中,查看它们其中的一些即可(下面是排名前五位的SQL语句),很明显它们都非常类似。只有在WHERE子句中用到的文字不同。这是不用绑定变量的典型案例。
select
pad FROM t
WHERE val =0
select
pad FROM t
WHERE val = 2139
select
pad FROM t
WHERE val = 9035
select
pad FROM t
WHERE val = 8488
select
pad FROM t
WHERE val = 1
这种情况的问题是,TKPROF无法识别只有文字不同的SQL语句。实际上,即使当aggregate选项设置为yes(默认就是),也只有同样文本的SQL语句会集合在一起。实际中这会造成TKPROF很难对快速解析问题进行分析。但指定record选项可以使这个过程简单一些。这样的话,文件仅会包含生成的SQL语句。
tkprof sys=no sort=prsela,exeela,fchela record=
接着可以使用命令行工具如grep和wc来找出相似的SQL语句有多少条。例如,下面的命令返回的值是10000:
grep "select pad
FROM t WHERE
val =" | wc -l