首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

4.2.4  共享池

共享池是SGA中最重要的内存段之一,特别是对于性能和可扩缩性来说。共享池如果太小,会严重影响性能,甚至导致系统看上去好像中止了一样。如果共享池太大,也会有同样的效果。共享池使用不当会导致灾难性的后果。

那么,到底什么是共享池?共享池就是Oracle缓存一些“程序”数据的地方。在解析一个查询时,解析得到的表示(representation)就缓存在那里。在完成解析整个查询的任务之前, Oracle会搜索共享池,看看这个工作是否已经完成。你运行的PL/SQL代码就在共享池中缓存,所以下一次运行时,Oracle不会再次从磁盘重新读取。PL/SQL代码不仅在这里缓存,还会在这里共享。如果有1 000个会话都在执行同样的代码,那么只会加载这个代码的一个副本,并由所有会话共享。Oracle把系统参数存储在共享池中。数据字典缓存(关于数据库对象的已缓存信息)也存储在这里。简单地讲,就像是厨房的水池一样,什么东西都往共享池里放。

共享池的特点是有大量小的内存块(chunk),一般为4 KB或更小。要记住,4 KB并不是一个硬性限制,可能有的内存分配会超过这个大小,但是一般来讲,我们的目标是使用小块的内存来避免碎片问题。如果分配的内存块大小显著不同(有的很小,有的却相当大),就可能出现碎片问题。共享池中的内存根据LRU(最近最少使用)的原则来管理。在这方面,它类似于缓冲区缓存,如果你不用某个对象,它就会丢掉。为此提供了一个包,名叫DBMS_SHARED_POOL,这个包可用于改变这种行为,强制性地“钉住”共享池中的对象。可以使用这个过程在数据库启动时加载频繁使用的过程和包,并使它们不至于老化。不过,通常如果过一段时间共享池中的一段内存没有得到重用,它就会老化。甚至PL/SQL代码(可能相当大)也以一种分页机制来管理,这样当你执行一个非常大的包中的代码时,只有所需的代码会加载到共享池的小块中。如果你很长时间都没有用它,而且共享池已经填满,需要为其他对象留出空间,它就会老化。

如果你真的想破坏Oracle的共享池,最容易的办法是不使用绑定变量。在第1章已经看到,如果不使用绑定变量,可能会让系统陷于瘫痪,这有两个原因:

q  系统要花大量CPU时间解析查询。

q  系统使用大量资源来管理共享池中的对象,因为从来不重用查询。

如果提交到Oracle的每个查询都是具有硬编码值的惟一查询,则共享池的概念就一点用都没有。设计共享池是为了反复使用查询计划。如果每个查询都是全新的,都是以前从来没有见过的查询,那么缓存只会增加开销。共享池反而会损害性能。为了解决这个问题,很多人都会用一种看似合理的常用技术,也就是向共享池增加更多的空间,但是这种做法一般只会使问题变得比以前更糟糕。由于共享池不可避免地会再次填满,比起原来较小的共享池来说,开销甚至更大,原因很简单,与管理一个较小的满共享池相比,管理一个大的满共享池需要做更多的工作。

对于这个问题,真正的解决方案只有一个,这就是使用共享SQL,也就是重用查询。在前面(第1章),我们简要介绍了参数CURSOR_SHARING,在这方面,游标共享可以作为一种短期的解决方案。不过,真正要解决这个问题,首当其冲地还是要使用可重用的SQL。即使在最大的系统上,我发现一般也最多有10 00020 000条不同的SQL语句。大多数系统只执行数百个不同的查询。

下面是一个真实的示例,从这个例子可以看出,如果共享池使用不当后果会有多严重。我曾参与过这样一个系统,它的标准操作过程是每天晚上关闭数据库,清空SGA,再重启。之所以这样做只是因为,系统白天运行时有问题,会完全占用CPU,所以如果数据库运行的时间超过一天,性能就开始严重下降。他们原来在一个1.1 GBSGA中使用了1 GB的共享池。确实如此:0.1 GB由块缓冲区缓存和其他元素专用,另外1 GB则完全用于缓存不同的查询,但这些查询从来都不会再次执行。必须冷启动的原因是,如果让系统运行一天以上的时间,就会用光共享池中的空闲内存。此时,结构老化的开销就太大了(特别是对于一个如此大的结构),系统会为此疲于奔命,而性能也会大幅恶化(不过原来的性能也好不到哪里去,因为他们管理的是一个1 GB的共享池)。另外,使用这个系统的人一直想向机器增加越来越多的CPU,因为硬解析SQL太耗费CPU。通过对应用进行修正,让它使用绑定变量,不仅消除了物理的硬件需求(现在的CPU能力比他们实际需要的已经高出几倍),而且对各个池的内存分配也反过来了。现在不是使用1 GB的共享池,而只为共享池分配了不到100 MB的空间,即使是经过数周连续地运行,也不会用光共享池的内存。

关于共享池和参数SHARED_POOL_SIZE还有一点要说明。在Oracle9i及以前的版本中,查询的结果与SHARED_POOL_SIZE参数之间没有直接的关系,查询结果是:

SHARED_POOL_SIZE参数是:

如果实在要谈谈它们的关系,只能说SUM(BYTES) FROM V$SGASTAT总是大于SHARED_ POOL_SIZE。共享池还保存了另外的许多结构,它们不在相应参数的作用域内。SHARED_POOL_SIZE通常占了共享池(SUM(BYTES)报告的结果)中最大的一部分,但这不是共享池中惟一的一部分。例如,参数CONTROL_FILES就为共享池“混合”部分做出了贡献,每个文件有264字节。遗憾的是,V$SGASTAT中的“共享池”与参数SHARED_POOL_SIZE的命名让人很容易混淆,这个参数对共享池大小贡献最大,但是它并不是惟一有贡献的参数。

不过,在Oracle 10g 及以上版本中,应该能看到二者之间存在一对一的对应关系,假设你使用手动的SGA内存管理(也就是说,自己设置SHARED_POOL_SIZE参数):

如果你从Oracle9i或之前的版本转向10g,这是一个相当重要的改变。在 Oracle10g中,SHARED_POOL_SIZE参数控制了共享池的大小,而在Oracle9i及之前的版本中,它只是共享池中贡献最大的部分。你可能想查看9i(或之前版本)中实际的共享池大小(根据V$SGASTAT),并使用这个数字来设置Oracle 10g(及以上版本)中的SHARED_POOL_SIZE参数。用于增加共享池大小的多种其他组件现在期望由你来分配内存。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论