|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
×
如果想要成为更出色的数据库管理员并为终端客户群提供更好的服务,那么了解Oracle数据库内部的结构非常重要。Oracle数据库的库缓存就是数据库管理员需要深入了解的一个内部结构,通过深入学习,能够帮助您消除由应用软件用户引发的一些非常烦人的拒绝服务请求事件。 Oracle的库缓存不过是内存的一个区域,是共享池里的三个组分之一。库缓存由共享SQL工作区、PL/SQL包和过程、不同的锁和句柄组成。每当有应用程序要执行SQL或PL/SQL语句(统称代码)时,这些代码必须先暂存在Oracle的库缓存中。当应用程序运行和参考代码时,Oracle会先搜索库缓存看该代码是否已经存在内存中。如果代码已经写入内存里,Oracle就可以重新使用该已存代码(也称为软解析)。如果内存里找不到该代码,Oracle就必须将代码载入到内存中(也称为硬解析或库缓存不命中)。用来检查正在请求的代码是否确实和库缓存中已有的代码相匹配的标准很多,不过这些内容超出了本文的涵盖范围,有兴趣的读者可以自行查阅相关信息。只是要记住一点,系统给一个已配置的库缓存工作区分配了一定的内存量,也就是说在该区只能容纳与相同大小的代码量,当内存耗尽时,会自动从内存中删除掉一些不常用的旧代码,以便腾出一定空间来装载应用程序所需要的代码。这并不一定是件坏事,但我们必须要了解自己的库缓存容量大小,以及出现了多少次缓存不命中事件。如果硬解析出现的次数太多,我们可能需要增加分配给库缓存的内存容量。
为了检测和获悉自您最近一次启动Oracle数据库之后库缓存目前的性能情况,您可以使用下面的SQL语句进行查询。如果你正好遇上了性能问题,你应该会想看看该查询返回的相关结果信息。不管怎样,返回结果的每一行都代表了在库缓存中存放着的某种特定类型的代码(名字空间),以及这些代码的执行状况。
查询代码:
以下是引用片段:
SQL> select namespace,
pins,
pinhits,
reloads,
invalidations,
pinhitratio * 100 pinhitratio
from v$librarycache;
|
返回结果:
以下是引用片段:
NAMESPACE PINS PINHITS RELOADS INVALIDATIONS PINHITRATIO
--------------- ---------- ---------- ---------- ------------- -----------
SQL AREA 9510054 9475115 1920 1731 99.6326099
TABLE/PROCEDURE 2173571 2158422 2525 0 99.3030363
BODY 977001 976796 16 0 99.9790174
TRIGGER 28566 28491 11 0 99.7374501
INDEX 9735 8359 5 0 85.865434
CLUSTER 2294 2281 2 0 99.4333043
OBJECT 0 0 0 0 100
PIPE 0 0 0 0 100
JAVA SOURCE 0 0 0 0 100
JAVA RESOURCE 0 0 0 0 100
JAVA DATA 493 489 0 0 99.188641 |
要知道返回的这些结果代表了什么信息,首先看某特定名字空间的执行次数(即PINS列),然后再看尝试执行不在库缓存里的代码的次数(即RELOADS)。还有一项重要的统计指标就是某特定名字空间发生INVALIDATIONS的次数。INVALIDATIONS是指那些由于某种原因(特别是通过DDL操作)变成无效而请求解析的代码。所有这些指标都统一归纳为命中率(PINHITRATIO)。所以,在上面的例子中,可以看出该库缓存似乎运行得很好,应用程序也能够非常有效的重新使用代码。
您还可以通过以下的SQL语句获得库缓存的整体性能状况。从结果我们还是可以看到该库缓存运行的相当不错:
以下是引用片段:
select sum(pins) pins,
sum(pinhits) pinhits,
sum(reloads) reloads,
sum(invalidations) invalidations,
100-(sum(pinhits)/sum(pins)) *100 reparsing
from v$librarycache; |
返回结果:
PINS PINHITS RELOADS INVALIDATIONS REPARSING
---------- ---------- ---------- ------------- ----------
12703182 12651415 4479 1731 .407512071
当某个应用程序需要调用代码时,也许您会认为只要简单地把代码放到库缓存里就行了,不过说是起来简单,但实际做到这一点却又是另一码事了。在程序使用代码的时候,必须遵守一些内部的锁定机制。这些都是为了确保所执行的查询和代码确实有效,并涉及有效的对象。简而言之,这些锁以及后续的等待事件包括以下几个方面:
库缓存加载锁(library cache load lock)
库缓存加载锁,照字面的意思就是为某个数据库对象加上锁以便使该对象能顺利加载到库缓存里。此类锁都是以EXCLUSIVE模式获得,这样其他人就不能在同一时间加载相同的对象。如果在您的会话试图获取该锁的时候,而锁已经被其他人获取了,那么您就要等到他们把对象加载到库缓存的过程进行完毕为止。
库缓存锁(library cache lock)
一旦数据库对象加载到了库缓存,库缓存锁就会控制不同客户端之间对共享对象的并发访问。数据库对象必须要有这个锁才能实现串行访问,并准许单个应用程序使用对象。锁持续的事件取决于持有锁的应用程序或用户正在做什么。此外,获得此类锁还可以很容易地在库缓存中定位某个对象。
库缓存pin (livrary cache pin)
库缓存pin负责管理库缓存内的并发访问。要加载一个对象堆到内存里需要获得一个库缓存pin。此外,如果想要修改或检查某个对象,也需要库缓存pin。
Oracle为了将对象载入库缓存中,使用了锁和pin来访问或载入对象句柄和堆。锁是用来管理不同用户或应用程序进程之间的串行或并发访问。也就是说,如果有需要,一个进程可以锁定并防止其他进程访问某个对象。要在库缓存中定位一个对象也需要有锁。如果需要修改或检查某对象,则必须在将其句柄锁住后获取一个pin。如果对象不在内存里,那pin该对象就会使对象堆载入到内存中。如果代码的编译和解析一直都在进行,为了确保对象定义不在它们被使用的时候发生改变,必须要动用锁和pin的锁住机制。也就是说,对于任何要载入到库缓存的代码,首先要获取对该对象句柄的库缓存锁,如果获取失败,则发生库缓存锁等待;直到成功获取该锁,再继续获取库缓存pin,以便将对象堆pin入到库缓存。
为了确保对象和SQL代码正常有效地执行,并可供接下来其他的应用程序执行,对Oracle数据库库缓存的检测至关重要。如果总是发生一些等待事件,从而限制了对代码的并发访问,就会给库缓存带来压力,使其性能下降。在出现问题之前,运行上面提到的SQL查询语句,获取有关库缓存整体性能状况的相关信息。发生在库缓存中的大多数问题都是由应用程序或用户引起的,在接下来的篇幅里我们会讨论并解决这些问题:看看问题会出现在哪里,如何让其现身,以及解决这些问题办法。
如何解决库缓存给您制造的麻烦
库缓存中偶尔会为我们带来的麻烦通常来自于各种锁以及随之而来的由锁机制引发的以下等待事件:
库缓存加载锁:用户端碰到这种锁等待事件是因为有其他用户端对该对象使用了该锁,因此后来的用户端必须等待先前的用户端将锁释放出来为止。
库缓存锁:此类锁使某用户端能够享有对某对象的单独访问权,并阻止其他用户端的访问,例如在两个用户端想要同时编译某段相同的代码时。
库缓存pin:出现库缓存pin等待事件意味着有其他会话以不兼容模式锁定了该PIN。
不管库缓存中出现了哪种类型的等待事件,想要确定哪些会话在等待以及在等待的是什么资源,可以通过V$SESSION_WAIT视图查询进行诊断。例如,如果想要找出那些在等待“库缓存pin”的会话,可以执行以下的查询语句。对于一个库缓存pin来说,该查询的关键部分是P1RAW字段,该字段给出了阻塞特定会话的对象的句柄地址。对于其他类型的等待事件,您可以参考Oracle数据库的说明文档,找出对应于等待中的某对象或资源的P值。
以下是引用片段:
SELECT sid, event, p1raw
FROM sys.v_$session_wait
WHERE event = 'library cache pin'
AND state = 'WAITING'; |
然后我们可以执行以下的查询来找出正在等待哪些库缓存对象:
以下是引用片段:
SELECT kglnaown AS owner, kglnaobj as Object
FROM sys.x$kglob
WHERE kglhdadr='&P1RAW'; |
要找出那些正在等待某个对象的用户,可以使用DBA_WAITERS视图并执行以下查询。这是一个非常简单的查询,却可以很巧妙的找出阻塞的会话,也就是查找与上面从V$SESSION_WAIT查询中找出的会话相匹配的等待会话,然后看看返回的holding_session结果。我们还可以看到在被阻塞的会话之后还有多少其他会话在等待中。如果有很多等待会话,那你就需要迅速采取行动了。
以下是引用片段:
SELECT waiting_session, holding_session FROM dba_waiters; |
如果想要了解锁定中的会话正在执行什么SQL查询,可以使用以下的SQL语句进行查询:
以下是引用片段:
select sesion.sid,
sql_text
from v$sqlarea sqlarea, v$session sesion
where sesion.sql_hash_value = sqlarea.hash_value
and sesion.sql_address = sqlarea.address; |
有时候我们最好能够知道问题源于何处。给Oracle带来问题的可能并不是整个应用程序代码组,而实际上是某个特定的语句。我们可以通过追踪原始的SQL语句来查看可纠正此类问题。
现在我们已经确定了正在进行中的会话和被等待对象,以及引发问题的会话及其SQL。那么接下来要如何解决出现的问题呢?如果等待事件持续的时间过长,那么库缓存内部很可能发生了错误或故障。唯一的补救办法就是杀死持有该锁的所有进程。在Oracle数据库中要达到这个目的,可以使用alter system kill session命令。不过,这个命令是否有效还得看连接的类型。有时候需要用operating system kill命令或者关闭一系列应用程序来终止连接。我们需要检测库缓存中完全锁定状态下,哪一个方法对系统更行之有效。至少在不得不关闭系统和数据库之前,尝试一下强迫杀死进程的方法。
当我们使用库缓存时,只要记住它不过是在代码执行前,Oracle数据库必须将这些代码载入其中的内存区。将代码载入到库缓存的过程可能会受到限制,从而引起等待事件,使系统挂起。这时候我们要通过杀死会话、进程或修改代码的方法快速确定导致系统挂起的SQL进程,不过千万不要忘记了库缓存只是内存的事实,我们可能只是需要给引发问题的部分重新分配一些内存,使Oracle更有效地运行而已。 |
|