1 闩锁
闩锁(Latch)的作用主要是保护数据的一致性。当很多会话用户请求同一个数据块时,要先获取闩锁才可以做动作。也就是说,当一个会话
用户要到数据库内找数据时,会先到数据库高速缓冲区中找,如果找不到表,就必须到数据文件中找。读出数据时,必须在数据库高速缓冲
区中获取足够的空闲空间,而要获取足够的空闲空间,会先去清理LRU List(把不常用的东西清出来);但要清理LRU List,必须先执行闩锁
操作,而对DB_BLOCK_ LRU_LATCHES参数的设置,表示决定使用几个闩锁。但基本上,闩锁的个数大于CPU的个数是没效益的
闩锁的个数是可以由DB_BLOCK_LRU_ATCHES参数设置的。
闩锁的最大值可以由以下两点决定:
CPU的个数×2。
DB Block Buffer/50。
以这两项中的最小的值来算。
2
DB_WRITER_PROCESSES
当数据库高速缓冲区需要读取或写回数据到数据文件中时,都需要通过DBWRn这个后台进程来协助处理,而DB_WRITER_PROCESSES主要就是设
置要由几个DBWRn来协助处理,最多可以达到10个。在此建议不要超过系统CPU的个数,如果设置的值超过了CPU的个数,那么超过的那些是
无法起作用的,
3 V$DB_CACHE_ADVICE
。当开启参数DB_CACHE_ADVICE后,经过一段时间,Oracle就会自动收集足够的相关统计数据,并预测出DB_CACHE_SIZE在不同大小情况下
的性能数据,而这些数据就是通过V$DB_CACHE_ADVICE视图来显示的,因此,可以根据这些数据对DB_CACHE_SIZE做相关的调整,以达到最佳
情况。
SELECT ADVICE_STATUS,
SIZE_FOR_ESTIMATE,
ESTD_PHYSICAL_READ_FACTOR,
ESTD_PHYSICAL_READS
FROM V$DB_CACHE_ADVICE
WHERE NAME = 'DEFAULT';
当ESTD_PHYSICAL_READ_FACTOR为1时,可以得出最好的cache分配内存。
4 V$BUFFER_POOL_STATISTICS
可以查询V$BUFFER_POOL_STATISTICS来查看每个数据库高速缓冲区的命中率,方式如下:
SQL> SELECT name,1 - (physical_reads / (db_block_gets + consistent_gets))
"HIT_RATIO" FROM sys.v$buffer_pool_statistics
WHERE db_block_gets + consistent_gets > 0;
对于SYS.V$BUFFER_POOL_STATISTICS,要先执行程序catclust.sql。
注意,原则上,获取命中率要在90%以上。
但回收率若是在90%以上,就代表这期间常用的数据是被放在回收池里的,而回收池里的数据又是经常被重复使用,所以,如果回收率是在
90%以上,就代表有问题,因为这代表90%以上的数据用完就丢了,应该将常用的数据放在保留池里。
5 共享池
共享池(Share Pool)主要是存放最近执行过的SQL语句、数据字典与PL/SQL的一些数据、表的定义和权限等,包含库缓存、数据字典高速
缓存、控制结构、可重用运行时内存等部分
1)库缓存
共享SQL区域:当执行一个SQL语句时,Oracle就会为每一句SQL打开一个游标(Cursor),并提供一个共享SQL区域和私有SQL区域空间用于
存放,当发现两个以上的会话在使用同一个SQL语句时,Oracle就会重新组织共享SQL区域,使这些会话用户能重复使用此区域的内容,从而
不浪费宝贵的资源,同时,还会在私有SQL区域里保存一份这个SQL语句的副本;反之,如果没有找到相同的SQL语句,就必须重新进行完整
的分析作业,并将分析树(Parse Tree)以及执行计划等存放在共享池内,以备下次使用。这时要注意的是,在Oracle于共享池中寻找相同的
SQL语句的过程中,如果使用的SQL语句有任何一个地方不相同,Oracle就会将此SQL语句视为在此共享池中找不到,所以需要重新进行分析
。因此,如果同一段SQL语句只是在条件的部分输入不同的值,Oracle也会认为SQL语句不同,为此,可使用绑定变量(Bind Variable),意
思是Oracle会将变动的值视为绑定变量,这样很容易在共享池里找到相同的SQL语句
当一条新的SQL语句被分析(Parse)时,Oracle从共享池中分配一块内存来存储共享SQL区域,而这块被分配的内存的大小与这条语句的复
杂性相关,如果共享池中的空间不够分配给共享SQL区域使用,Oracle就会启动LRU机制,将最近最少使用的内存块释放出来,直到有足够的
空间分配给新SQL语句的共享SQL区域;被释放出来的那块内存块内的SQL语句,如果再次被使用就必须重新分析,并重新分配共享SQL区域,
而这些动作都是比较消耗CPU的作业。这就是为何要使用绑定变量的原因了。因为如果没有使用绑定变量,当SQL语句中的变量的数值不同时
,Oracle就会将其视为一条新的语句,重复上面的分析、内存分配等动作,这都将大大消耗系统资源,降低Oracle系统性能。
2) 数据字典高速缓存
数据字典高速缓存是有关于数据库的参考信息、数据库的组织结构和数据库中的用户信息与权限等,而这些内容都是存储在表内,通常
以表和视图的形式进行呈现,就像经常使用到的V$xxx、DBA_xxx等系统的视图和表都是存储在数据字典(Data Dictionary)内。这里值得注
意的是,当SQL语句在分析的过程中时,也会去参考数据字典内的信息,而参考数据字典的动作所产生的SQL语句称为递归的SQL(Recursive
SQL),如果使用Statspack分析统计数据,此类的递归的SQL会被统计为Recursive Calls。
3)重要概念
基本上,共享池是依照修正过的LRU算法来判断其中的SQL语句与对象是否需要保留在共享池内。一般来说,当共享池需要为一个新的
SQL语句或对象分配内存时,会先确认是否有足够的空间,如果没有,就会将那些不经常使用的SQL语句和对象释放掉,也就是利用LRU机制
;但对于经常使用的SQL语句或对象,就会一直保留在共享池内。
除了LRU机制以外,当发生以下的情况时,Oracle也会将SQL语句从内存中释放出来:
当长时间没有被使用打开的游标(Open Cursor)时,SQL语句还是可能会从共享池中被释放出来,而此时如果还需要运行相关的SQL语句,
Oracle就会重新分析,并记录到共享SQL区域内。
当使用Analyze语句重新统计对象的信息时,所有与被分析对象相关的SQL语句都从共享池内被释放掉,这是因为重新分析对象,意味着原来
的执行计划可能不适用,所以必须将相关SQL语句都从共享池内释放掉,当再次使用到时,会重新分析并产生新的执行计划。
当对象的结构被修改过后,与该对象相关的所有共享SQL区域内都被注记为Invalid,当再次使用到相关的SQL语句时,就会再重新分析一次
。
Global Database Name被修改了,共享池内的所有信息都会被清空。
手动清空共享池的语句如下:
ALTER SYSTEM FLUSH SHARED_POOL;
当Oracle分析一个PL/SQL对象时,如过程、函数、触发器、包等,都需要从共享池中分配内存给这些对象。由于这些对象一般所占用的内存
空间都比较大,如包,因此分配的内存空间也相对较大,当Oracle经过长时间的运行后,共享池可能存在海量存储器碎片(Fragmentation)
,因而导致无法满足此类对象对于内存的需求。为了解决这类问题,Oracle专门从共享池内设置出一块共享的保留专用内存区域(Shared
Reserved Pool,简称保留区)来分配给这些具有内存需求的对象。这个保留区的默认值是共享池大小的5%,其大小一般建议是共享池的10%
,但绝对不要超过共享池大小的50%。除此之外,保留区的大小也可以通过参数SHARED_POOL_RESERVED_SIZE来调整,保留区的特点如下:
保留区是从共享池中分配,而不是直接从SGA中分配的,因此,保留区是共享池的一部分,用于存储大的对象。
在共享池中内存大于5 KB的对象就会被存放在共享池内的保留区,5 KB是默认值,而这个大小限制是可以通过隐藏参数
_SHARED_POOL_RESERVED_MIN_ ALLOC来设置的。除了在实例打开的过程中,所有小于这个数的对象永远都不会放到保留区,而大于这个值的
对象也永远不会存放到非保留区中。
当保留区有多余的内存空间时,对象也不会被放到共享池内的空间列表中,它有自己独立的保留空闭列表;相对的,当共享池空间不足时,
Oracle就利用LRU机制将不常用的对象释放,而不会影响到保留区内的对象。也就是说,保留区也是使用的LRU机制,但是在扫描时,其与共
享池是相互不影响的。
保留区内的信息可查询V$SHARED_POOL_RESERVED。关于V$SHARED_ POOL_RESERVED的详细内容,请参阅第2.3.5节。
前面提过Oracle在共享池中寻找相同的SQL语句的过程中,如果使用的SQL语句有任何一个地方不相同,Oracle就会视此SQL语句在此共享池
中找不到,所以需要重新进行分析,因此,若当同一段SQL语句只是在条件的部分输入了不同的值,Oracle也会将其视为不同的SQL语句,为
了适应这种情况,可使用绑定变量,但要如何判断SQL语句是否使用了绑定变量呢?可使用以下语句得知目前Hard Parse(硬分析)的次数与
比例,Hard Parse次数越少越好,所以比例值越接近于0越好。
select a.value as Total,b.value as "Hard Pase",
round(b.value / a.value, 2) as Ratio
from v$sysstat a, v$sysstat b
where a.name = 'parse count (total)' and b.name = 'parse count (hard)';
下面的语句查询哪些语句没使用变量绑定:
oracle 9/10:
select d.plan_hash_value plan_hash_value,
d.execntexecnt,
a.hash_valuehash_value,
a.sql_textsql_text
from v$sqltext a,
(select plan_hash_value, hash_value, execnt
from (select c.plan_hash_value,
b.hash_value,
c.execnt,
rank() over(partition by c.plan_hash_value order by
b.hash_value) as hashrank
from v$sql b,
(select count(*) as execnt, plan_hash_value
from v$sql
where plan_hash_value <> 0
group by plan_hash_value
having count(*) > 10
order by count(*) desc) c
where b.plan_hash_value = c.plan_hash_value
group by c.plan_hash_value, b.hash_value, c.execnt)
where hashrank <= 3) d
where a.hash_value = d.hash_value
order by d.execnt de SHARED_POOL_SIZEsc, a.hash_value, a.piece;
oracle 11g:
select d.plan_hash_value plan_hash_value,
d.execnt,
a.HASH_VALUE,
a.SQL_TEXT
from v$sqltext a,
(select plan_hash_value, hash_value, execnt
from (select c.plan_hash_value,
b.hash_value,
c.execnt,
rank() over(partition by c.plan_hash_value order by
b.hash_value) as hashrank
from v$sql b,
(select count(*) as execnt, plan_hash_value
from v$sql
where plan_hash_value <> 0
group by plan_hash_value
having count(*) > 10
order by count(*) desc) c
where b.plan_hash_value = c.plan_hash_value
group by c.plan_hash_value, b.hash_value, c.execnt)
where hashrank <= 3) d
where a.hash_value = d.hash_value
order by d.execnt desc, a.hash_value, a.piece;
6 共享池的重要参数
1) SHARED_POOL_SIZE
SHARED_POOL_SIZE指定了共享池的大小。在32位操作系统中,此参数的默认值是8 MB,而64位操作系统中的默认值是64 MB。
提示
Oracle 9i之前的版本和10g版本的共享池的算法是不相同的,差异如下:
Oracle 9i的算法是SHARED_POOL_SIZE的设置加上内部SGA消耗大小,因此,如果以SHOW PARAMETER SHARED_POOL_SIZE查询V$SGASTAT,并汇
总共享池的大小是不相同的。
Oracle 10g的算法是当设置了SHARED_POOL_SIZE的值,就将内部SGA消耗大小算在其中,因此,共享池真正使用的空间是SHARED_POOL_SIZE
的设置值减去内部SGA消耗大小。在10g中设置共享池的大小时必须加入这个因素,如果共享池的设置太小,在打开数据库时,将会有ORA-
0371的错误信息。
2)一般情况下,会发现保留区是很少被使用的,也就是说,5%~10%的保留区空间可能有些浪费。不过,建议经过长时间的观察再决定是否
需要调整保留区的大小。
V$SHARED_POOL_RESERVED的使用方式如下:
(1) 确认SHARED_POOL_RESERVED_SIZE的空间情况,查询语句如下:
SELECT ROUND(FREE_SPACE / 1024 / 1024, 1) "FREE SPACE MB",
REQUEST_MISSES,
REQUEST_FAILURES
FROM V$SHARED_POOL_RESERVED;
上结果的详细说明如下所示:
当REQUEST_FAILURES大于0时,增加SHARED_POOL_SIZE和SHARED_ POOL_RESERVED_SIZE的空间。
当REQUEST_MISS等于0,或是FREE_MEMORY大于等于SHARED_POOL_ RESERVED_SIZE的空间时,则增加SHARED_POOL_RESERVED_SIZE的空间。
(2) 确认SHARED_POOL_RESERVED_SIZE的命中率(Hit Ratio),查询语句如下:
SELECT (REQUEST_MISSES / (REQUESTS + 0.0001)) * 100 "REQUEST MISSES RATIO",
(REQUEST_FAILURES / (REQUESTS + 0.0001)) * 100 "REQUEST FAILURES RATIO"
FROM V$SHARED_POOL_RESERVED;
以上结果应该都要小于1%,如果大于1,应该加大SHARED_POOL_RESERVED_ SIZE
(3) V$SQL、V$SQLTEXT、V$SQLAREA与V$SQL_PLAN
V$SQL、V$SQLTEXT、V$SQLAREA与V$SQL_PLAN这4个视图都是查询在共享池中的SQL语句,而妥善的交互利用这些视图对于实例Tuning和
SQL Tuning有相当大的帮助。
V$SQL是共享池中已经分析过的SQL,其中列出了所有SQL的相关信息,但不包含GROUP BY语句,每一个SQL语句都是一笔数据,但此记录只保
留SQL语句的前1000个字符。
V$SQLAREA中记录了在共享池中分析过的每个SQL语句和准备运行的SQL语句的统计数据,和V$SQL一样只保留SQL的前1000个字符。
V$SQLTEXT视图会记录完整的SQL,但会将SQL分成不同笔的数据来存储。
V$SQL_PLAN视图包含了库缓存中所有游标所产生的执行计划,可结合V$SQLAREA获得库缓存中所有语句的查询计划。
V$SQL、V$SQLTEXT、V$SQLAREA与V$SQL_PLAN的运用如下所示:
利用V$SQL查询出使用频率最高的10句SQL:
select sql_text, executions
from (select sql_text,
executions,
rank() over(order by executions desc) exec_rank
from v$sql)
where exec_rank <= 10;
利用V$SQL查询出最耗费磁盘读的前10句SQL:
select disk_reads, sql_text
from (select sql_text,
disk_reads,
dense_rank() over(order by disk_reads desc) disk_reads_rank
from v$sql)
where disk_reads_rank <= 10;
利用V$SQL查询出最耗费内存的前10句逻辑读SQL语句:
select buffer_gets, sql_text
from (select sql_text,
buffer_gets,
dense_rank() over(order by buffer_gets desc) buffer_gets_rank
from v$sql)
where buffer_gets_rank <= 10;
利用V$SQLAREA和V$SQLTEXT查看消耗资源最多的SQL语句,步骤如下。
A SELECT hash_value, executions, buffer_gets, disk_reads, parse_calls
FROM V$SQLAREA
WHERE buffer_gets > 10000000 OR disk_reads > 1000000
ORDER BY buffer_gets + 100 * disk_reads DESC;
b 得到HASH_VALUE后,再利用V$SQLTEXT查询出实际的SQL语句。
select ADDRESS, HASH_VALUE, PIECE, SQL_TEXT
from v$sqltext
where hash_value = '2450129223'
order by PIECE;
利用V$SQLAREA查询出磁盘读次数超过100 000的查询语句,也就是最耗资源的SQL语句,语句如下:
select b.username "USER NAME",
a.disk_reads "DISK READ",
a.executions EXECUTIONS,
a.disk_reads / decode(a.executions, 0, 1, a.executions) "READ_EXEC%",
a.sql_text "SQL TEXT"
from v$sqlarea a, dba_users b
where a.parsing_user_id = b.user_id
and a.disk_reads > 100000
order by a.disk_reads desc;
利用V$SQL和V$SQL_PLAN查询出SQL语句的执行计划,步骤如下:
A 利用V$SQL查出查询的SQL语句的ADDRESS和HASH_VALUE,语句如下:
SELECT sql_text, address, hash_value FROM v$sql
WHERE sql_text like 'SELECT * FROM EMP %';
B 利用查出的ADDRESS和HASH_VALUE,配合V$SQL_PLAN相对应的字段,即可查询出此SQL语句的执行计划,语句如下;
SELECT operation, options, object_name, cost FROM v$sql_plan
WHERE address = '32557612' AND hash_value =2128902247;
利用V$SESSION的SQL_HASH_VALUE和SQL_ADDRESS字段,配合V$SQLTEXT的HASH_VALUE和ADDRESS,可查询出当前用户链接的SQL语句,步骤
如下
a 利用V$SESSION查询出SQL_HASH_VALUE和SQL_ADDRESS,语句如下
SELECT USERNAME,
TERMINAL, PROGRAM,
SQL_HASH_VALUE,
SQL_ADDRESS, TYPE
FROM V$SESSION
WHERE TYPE NOT LIKE 'BACKGROUND';
b 利用查出的SQL_HASH_VALUE和SQL_ADDRESS,再配合V$SQLTEXT相对应的字段即可,语句如下;
SELECT *
FROM V$SQLTEXT
WHERE HASH_VALUE = '889124229'
AND ADDRESS = '000000008EBE3A38'
ORDER BY PIECE;
(3) V$LIBRARYCACHE
V$LIBRARYCACHE的内容包含了关于库缓存的性能统计信息,对于共享池的性能优化有很大帮助
此表中必须特别注意PIN和GET的命中率或未命中率,当命中率小于99%或未命中率大于1%时,说明Hard Parse过多,可能需要加大共享池或是使用绑定变量等优化的动作。查询命中率的语句如下:
Select
namespace,
gets,
gethitratio*100 "GET%",
pins,
pinhitratio*100 "PIN%",
reloads,
invalidations
from
v$librarycache;