Oracle Write List and Database Writer

Write lists通常也叫做dirty lists或LRU-W lists,是由整个dirty buffer headers组成。每个dirty bufferheader也存放在CBC结构中。Oracle有一个工作集的概念,它由LRU latch,LRU chain与write list组成。每个数据库写进程与一个或多个工作集关联。在实例启动时,Oracle将决定工作集的数据量与数据库写进程的数量(db_writer_processes,它的缺省值为1),然后设置它们的关联。当一个数据库写进程执行操作时,它将从它的写列表中获取信息。服务器进程与数据库写进程从它们瓣LRU chains中移动不被频繁访问的dirty buffers到与它们相关的写列表中。

数据库写进程的运转
多块写比单块写更有效,因此数据库写进程在执行写操作之前会构建一个dirty列表。多年以来,Oracle已经修改了实例参数与算法来控制最小的dirty列表的批量大小。事实上,Oracle使用了一种自调整的算法来计算dirty buffers的突发事件。当数据库写进程正在进行多块写操作时,它将出现db file parallel write等待事件。v$session_wait视图中的参数用来提供正在被写到磁盘的数据块数。数据库写进程的职责是将dirty buffers写入磁盘。执行下面的查询:

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       155

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       185

SQL> select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       121
SQL>  select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       172

SQL>  select count(*) from v$bh where dirty='Y';

  COUNT(*)
----------
       173

从上面的查询结果可以看到dirty buffer数量是呈循环性的增长与减少。循环的结果是当数据库写进程将dirty buffers写入磁盘时,这些dirty buffers将再一次变为free buffers。这种循环是正常的,也是想要看到的。如果dirty buffers的计数一直在增长,那么你就知道数据库写进程处理能力不足。对于大型Oracle系统来说通常有上千个dirty buffers存在。

我们都知道数据库写进程每3秒会被唤醒一次。通过跟踪来查看在Oracle 11g中是不是也是这样。注意休眠时间是大概3秒钟并且系统调用为semtimeodop。而当一个服务器进程在获取latch期间休眠时,因为它执行的是select系统调用。select不允许这具进程被唤醒,但信号量调用可以。这是很重要的区别,因为数据库写进程由于各种原因需要被唤醒,比如检查点操作或free buffer waits等待事件。

[root@db1 ~]# ps -eaf |grep dbw
oracle    49087      1  0  2018 ?        03:20:23 ora_dbw0_RLZY1
oracle    49089      1  0  2018 ?        03:19:18 ora_dbw1_RLZY1

[root@db1 ~]# strace -rp 49089
......
0.000298 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000482 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000336 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......
0.000608 semtimedop(3407933, {{25, -1, 0}}, 1, {3, 0}) = -1 EAGAIN (Resource temporarily unavailable)
......

数据库写进程相关竞争的识别与解决
有各种与数据库写进程相关的等待事件。对争用情况进行分类的一种方法是理解数据库写进程是否有“push-to-disk”问题或“pull-from-write-list”问题。大多数的问题是push问题,也就是写磁盘的问题。但也有一种很常见的pull问题稍后再进行说明。与数据库写进程push-to-disk问题相关的所有等待事件都是以db file开始。与其它IO等待事件一样,在IO调用之前和之后,会执行gettimeofday调用,并且区别就是我们通过Oracle等待事件接口所看到的。这里有两个常见的数据库写进程push-to-disk等待事件:
.db file parallel write
目前最常见的数据库写进程等待事件,一个parallel写也可以简化为多块写。这是数据库写进程从写列表中获取数据并将dirty块批量写入磁盘的结果。希望这个等待事件的等待时间小于5ms,但每个单位有它自己的预算与服务要求。写操作的时间小于5ms说明写缓存工作的很好。

.db file single write
它不应该是top等待事件。当所有数据库文件头块在写入检查点操作的末尾可能会出现。这是通过一次执行多个单块写来完成的。

从需求和能力方面来查看IO问题。当存在一个IO问题时,需求已经超过能务了。只有当锁或阻塞类型出现时,比如free buffer waits事件才会出现例外。当看到数据库文件写操作出现问题时,除了锁或阻塞的原因之外,知道IO请求已经超过了IO子系统的能力。使用复杂IO管理将增加不同系统文件与数据库文件存储在相同磁盘上的机会。

IO问题可能变得非常情绪化。供应商参与进来并开始保护自己的地盘。为了帮助解决问题,从应用程序角度来看,将查找生成dirty buffers的SQL语句。将会找到一个或多个更新,插入与删除操作

从Oracle角度来分析,可以考虑任何可能增加Oracle IO写效率的参数。例如研究可以增加数据库写进程批量写大小的方法。修改数据库写进程的批量大小与数据库版本有关,例如_db_block_write_batch与_db_writer_max_writes参数。也可以考虑增加参数_db_writer_max_scan_pct(缺省值是40,例如40%)或_db_writer_max_scan_cnt,在触发数据库写进程开始执行写操作之前它们用来判断一个服务器进程将扫描多少LRU buffer headers。增加这些参数将提供更多时间来构建写列表,因此造成每个数据库写IO请求将写入更多数据块。这将有效的增加每秒写入磁盘的数据块。测试显示通过将db_writer_max_scan_pct从5增加到95,数据库写进程操作系统写调用将减少9%且db file parallel write等待减少3%,当事务活动增加14%时,每秒的块改变将增加19%。仅仅通过改变这个参数,当工作量增加时IO活动减少了。

另一种可能是遇到服务器进程不触发数据库写进程执行写操作,因此会构建写队列。当一个服务器进程,在搜索一个free buffer时,偶然发现一个不被频繁访问的dirty buffer后将其移动到相关的写列表,并且也会检查写列表是否足够长可以执行写操作了。如果写列表已经足够长了,服务器进程将触发数据库写进程执行写操作。因此这是一个有效的选择,为了允许构建写队列来导致大批量写操作,可以增加_db_large_dirty_queue(在某些系统中缺省值是25)参数。但创建太在的写队列需要小心。当dirty buffers正被写入磁盘时,它们不能被改变。任何需要改变正被写入磁盘的buffer都必须等待。相关的等待事件为write complete waits。在top等待事件中write complete waits不是很常见,但如果修改了写列队长度就可能会出现。

最后,从Oracle角度来看,增加buffer cache可以让数据库写进程减轻在短时间内强烈的数据块改变所带来的压力。一个大的buffer cache允许cache来填充dirty buffers,在不强制数据库执行写操作时,有更多的时间来创建大的且更有效的批量写。如果真的想给数据库写进程施加压力,创建一个小的buffer cache并执行一些DML操作,你将会看到数据库写进程疯狂地试图删除脏缓存区中的小缓存。

从操作系统角度来看,这应该存在IO瓶颈。这是很罕见的,如果IO写响应时间小于5ms而写缓存工作的很好,那么总的等待时间将足够大,因此db file parallel write等待事件将被推到top等待事件中。当出现这种情况时,关注IO子系统不会有什么实质性的效果。在这种情况下,关注减小应用程序IO写活动与增加Oracle写效率。这也意味着要在数据库活动高峰期来创造性的减小写操作。经验丰富的DBA已经看到了在正常业务期间存在写密集的IO活动,比如RMAN,血份与文件传输。减少非Oracle IO活动能有效地增加IO子系统的能力。

Oracle Least Recently Used Chains

LRU Chains(or LRU lists)有它们相关的算法在过去已经修改过多次。尽管算法已经修改过,但LRU chain的功能仍然相同:为了帮助被频繁访问的buffer内置在cache中和帮助服务器进程快速地找到可被替换的buffers。任何时候单个列表都要努力地完成这两个任务,这将可能出现一些妥协。LRU chain也不例外,正如你将要发现的一样,Oracle当前的LRU算法实现的非常好,支持buffer caches超过100G的大小来满足电信与政府系统的高事务处理的要求。

在Oracle 6中,只有单个LRU chain被单个LRU chain latch保护着。在大型的OLTP系统中,DBA将与LRU chainlatch竞争进行斗争。但从Oracle 7开始,Oracle通过将单个LRU chain分割成多个小的LRU chains,每个都有一个相关的LRU chain latch来缓解这种问题。每个cache buffer存放在CBC结构中并存放在一个LRU chain或一个写列表(也叫脏列表)中。buffers不会同时存放在一个写列表与一个LRU列表中。LRU chains要比CBCs长太多。

脏buffers存放在一个LRU chain中不是问题。事实上,如果脏buffers不能存放在一个LRU chain上将会影响性能。LRU chains的一目标就是将被频繁访问的buffers保留在cache中,并且许多脏buffers也会被频繁地访问。当在数据库检查点期间,每个脏buffer将被写入磁盘并再次变为free buffer。

隐含参数_db_block_lru_latches显示实例正在使用的的LRU chains的数量。与CBCs一样,每个LRU chain latch控制着一组LRU chains的序列化。
SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
2 from x$ksppi x, x$ksppcv y
3 where x.inst_id=USERENV(‘Instance’)
4 and y.inst_id=USERENV(‘Instance’)
5 and x.indx=y.indx
6 and x.ksppinm like ‘%&par%’;
Enter value for par: _db_block_lru_latches
old 6: and x.ksppinm like ‘%&par%’
new 6: and x.ksppinm like ‘%_db_block_lru_latches%’

NAME VALUE DESCRIB
———————- —— ——————————–
_db_block_lru_latches 640 number of lru latches

LRU Chain随着时间的推移而变化
当前的LRU chain算法被叫做touch-count算法,它使用计算频率方案在每个buffer header上设置一个数字。但是Oracle花了很多年才实现这个算法。理解Oracle的LRU算法的发展更能了解LRU chains是如何工作的,它的缺点是什么以及如何确保它们按需执行。

当LRU chains出现性能问题时,大量的LRU chain latch竞争将会出现。从Oracle算法角度来说,latch问题通常会造成服务器进程在搜索一个free buffer时持有一个lRU chain latch的时间太长。这里存在许多相互关联的原因,其解决方案也是一样。

Standard LRU Algorithm(标准LRU算法)
不管Oracle的LRU算法如何,每个Oracle LRU chain有一个最近最少使用(LRU)端,也有一个最近频繁使用(MRU)端。笼统地说,被频繁访问的buffer header将存放在靠近MRU端,并且不被频繁访问的buffer将存放在靠近LRU端。

标准LRU算法是非常简单的。当一个buffer被放入cache中或被访问时(查询或DML操作),buffer将被存放在会话相关的LRU chain(每个会话与一个LRU chain相关)的MRU端。这种想法是一个被频繁访问的buffer将被重复touched并且会被重复移动到LRU chain中的MRU端。buffer移动到LRU chain的MRU端通常叫做buffer promotion。如果一个buffer不被频繁访问,那么其它的buffer将被promoted或插入到LRU chain中,不被频繁访问的buffer将被移动到LRU chain的LRU端。

靠近每个LRU chain的LRU端可能潜伏着一个服务器进程用来查找一个可用的不被频繁访问的buffer好让刚刚从磁盘中读取来的块替换掉它。假设LRU chain是8个buffer header那么长,全表扫描会扫描8个数据块,并且每个数据块将读入Oracle cache中并且buffer headers会被放入LRU chain中。当标准LRU算法使用时,只有一个LRU chain,因此整个LRU链将被全表扫描所访问的数据块所替换。随着时间的推移包含被频繁访问的buffer已经被替换了。用户肯定会注意到性能的变化,并且IO子系统也将受到打击。当数据库大小继续增长的时候,Oracle显然不得不进行改进,所以修改了LRU算法。

Modified LRU Algorithm(修改后的LRU算法)
Oracle著名的LRU算法修改是在Oracle 6中。它是一次重大成就并且Oracle开发者确实应该对他们的高级buffer cache算法感到自豪。在这之后,它确实解决了标准LRU算法的关键问题。

修改后的LRU算法与标准LRU算法仅有的区别是对LRU chain的LRU端的几个buffer创建了一个窗口(用来存放被频繁访问的buffers)。这个窗口的大小只有几个buffers(例如,4个)并且可以通过隐含参数_small_table_threshold来进行修改。这可以确保不管对多大的表进行全表扫描都将不会对cache产生什么影响。

Oracle修改后的LRU算法对一些buffer headers创建了一个窗口,当所有全表扫描(FTS)的buffer headers被读入到buffer cache时会经过这个窗口。这确保了放在LRU chain中的MRU端的被频繁访问的buffers不会被替换掉。

与其它所有算法一样,修改的LRU算法也有限制,但这么多年来这些限制没有造成问题。然而,一旦客户开始使用Oracle来开发大型数据仓库应用程序时,两个显著的问题会出现:
.大型数据仓库有大量的索引,并且当大量索引使用范围扫描时,成千上万的索引叶子块必须被读入cache中。这个问题直到Oracle 8i,如果索引叶子块不在buffer cache中,Oracle将产生一个单块IO请求(db file sequential read)将数据块放入buffer cache。令人吃惊的是因为这不是一个多块IO请求,索引buffer被插入到LRU chain的MRU端,这破坏了开发良好的cache,现在完全存放着索引叶子块buffers。

.当数据块被请求时(基于索引叶子块),它们也会从IO子系统中(db file sequential read)被请求一次,因此再一次这些数据块被放入到LRU chain中的MRU端。当Oracle系统大小增加时,Oracle的buffer cache减少了使用性。

Oracle’s Touch-Count Algorithm
在Oracle 8.1.5中Oracle引入了一种完全修改好的LRU chain算法已经完全消除了所有LRU chain latch竞争问题。关于这种修改没有任何文档记录。发现算法改变是因为看到了新的隐含参数_db_percent_hot_default 和_db_aging_cool_count。当有新的参数出现或有旧的参数丢弃时,算法肯定有被修改。Oracle确实实现了计算机科学领域中通常所说的计数频率方案。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_percent_hot_default','_db_aging_cool_count');

NAME                          VALUE      DESCRIB
----------------------------- ---------- ------------------------------------------------
_db_percent_hot_default       50         Percent of default buffer pool considered hot
_db_aging_cool_count          1          Touch count set when buffer cooled

正如你所期待的,通用方法就是每次触及buffer header时递增计数器。更频繁访问的buffer headers将有更高的触及计数并且确实访问更频繁,因此buffer将被保留在buffer cache。Oracle’s touch-count算法判断buffer header是否被频繁访问是基于buffer header被触及的次数来确定的。注意FTS(全表扫描)窗口的概念将不再需要并且已经被删除了。touch-count算法有三个关键点:midpoint-insertion,touch count incrementation与buffer promotion

Midpoint Insertion
与修改后的LRU算法最根本的背离是midpoint insertion。每个LRU chain被分成hot区与cold区。当一个buffer从磁盘被读入且找到了一个free buffer,这个buffer与buffer header将替换之前的buffer与buffer header的内容然后这个buffer header被移动到LRU chain的midpoint。单块读,多块读,快速完全索引扫描或全表扫描都没有差别。buffer header不会被插入到LRU chain的MRU端,而是LRU chain的midpoint。这确保了不会因为单个对象的大量数据块被读入到buffer cache中而使用LRU chain被破坏掉。

缺省情况下,hot区与cold区各占一半。midpoint确实在中间。然而这个可以通过隐含参数_db_percent_hot_default来配置。

当其它buffer headers被插入到midpoint或被promoted(提升)时,原有的buffer headers自然地将从LRU chain的hot区移动到cold区。在一个buffer header被插入后,只有一种方式可以保留在cache很长时间就是被不断重复地promoted。

因为窗口方案用于修改的LRU算法中而不再被使用,隐含参数_small_table_threshold因此被丢弃。然而在Oracle11g中,它又再次被使用,但是用于不同的目的。从Oracle 11g开始,_small_table_threshold参数是服务器进程开始执行直接路径读的阈值。直接路径读可以提高性能因为数据块从磁盘直接读取到服务器进程的PGA内存中而不用放入buffer cache。然而,这是更自私的读取操作并且可能实际上降低性能,因为其它的服务器进程不能从IO操作中获利。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm like '%&par%';
Enter value for par: _small_table_threshold
old   6: and x.ksppinm like '%&par%'
new   6: and x.ksppinm like '%_small_table_threshold%'

NAME                           VALUE                           DESCRIB
------------------------------ ------------------------------  -----------------------------------------------------
_small_table_threshold         60283                           lower threshold level of table size for direct reads

假设你是一个服务器进程必须要查询一行存放在特定数据块中的记录。基于这个SQL语句与数据字典,你知道数据块的文件号与块号。如果只关心查询速度,因此希望这个数据块已经存放在buffer cache中了。为了检查数据块是否存放在buffer cache中,需要得到buffer’s buffer cache内存地址,它存放在它的buffer header中。

为了找到buffer header,必须访问CBC结构。哈希文件号与块号,它将指向一个哈希桶。基于这个哈希桶,可以查找相关的CBC latch与持有它。在几次spin后,你可能可以获得latch,因此开始你的序列化CBC搜索。第一个buffer header如果不是你想要的,并且不幸地是在这个CBC中没有第二个buffer header,因此知道buffer当前没有放入buffer cache。

释放CBC latch并执行调用给操作系统,要求访问你需要的数据块。当你正等待时,你将被告知db file sequential read等待事件。最终从操作系统接收到这个数据块并在PGA中持有它。因为没有使用直接路径读,在你或其它服务器进程访问buffer之前,buffer必须被合理地插入到buffer cache并更新所有合理结构。

你将需要一个free buffer用来在buffer cache中存放刚读取的数据块,因此你将移到LRU chain的LRU端。但在你开始扫描LRU chain之前,你必须持有并获得相关的LRU chain latch。之后当休眠时通过spinning与posting等待事件latch:cache buffers lru chains来消耗CPU,最终获得latch。从LRU chain的LRU端开始,你查看buffer header是否它是一个不被频繁访问的free buffer,得到的回答是它是不被频繁访问的buffer。那么你现在就可开始buffer替换操作。你立即pin(固定)住这个buffer header。从buffer header中,可以获得数据块对应buffer在buffer cache中的内存地址,使用刚被读取的且仍在你PGA内存中的块来替换这个free buffer,执行任何要求buffer header所要进行的修改。你维护这个LRU chain并移动buffer header到LRU chain’s midpoint,释放LRU chian latch,并unpin这个buffer header。现在任何服务器或后台进程包括你可以访问这个buffer,这将都是在一瞬间就能完成。

Touch Count Incrementation
这个概念是一个buffer header每被touch一次,它的touch count将会增加。事实上并不是这样。缺省情况下,一个buffer header的touch count只有每3秒才会增加一次。这可以用来确保buffer活动时间超过几秒才算做被频繁访问

当一个buffer被插入到buffer cache中时,它的touch count被设置为0.然而,如果buffer在短期内被重复地touch,那么touch将不会进行计数。

Oracle也允许touch count被遗漏。这将没有latch被调用(这是消除latch竞争最好的方法),并且Oracle不会pin住buffer header。不使用序列化控制,两个服务器进程可以递增与更新buffer header’s的touch count到相同的值。

假设服务器进程S100在时间T0点得到的buffer header的touch count是13,并且开始递增为14。但服务器进程S200现在在时间T1点询问这个buffer header的touch count,并且因为服务器进程S100还没有完成touch count的递增操作,所以buffer header的touch count现在仍然显示为13。服务器进程S200现在开始将touch count从13递增到14。在时间T2点,服务器进程S100将buffer header的touch count修改为14,并且在时间T3点,服务器进程S200也将buffer header的touch count修改为14。这是不是touch count递增被遗漏了?没有结构被损坏,并且touch count确实已经被递增了,但不是递增两次。如果一个buffer确实被频繁地访问,它将再次被touch。通过这种模糊实现节省的是CPU的消耗与内核代码运行量。

Buffer Promotion
没有说当一个buffer被touch后,它将会被promoted到LRU chain的MRU端。这是因为buffer header的touching与buffer header的promotion现在是两个分开的操作。当一个buffer被考虑进行promotion时,也会考虑替换它。而服务器进程与数据库写进程都可以promote buffer header,但只有一个服务器进程将替换这个buffer并且与它相关的buffer header作为一个物理读取数据块的结果。数据库写进程执行替换没有意义,因为它没有替换的内容。

在一个服务器进程从磁盘读取一个数据块之后,它必须要找到一个不被频繁访问的free buffer来存放刚被读取的数据块。服务器进程要获得适当的LRU latch,然后从LRU chain的LRU端开始扫描buffer headers。记住buffer headers存放在LRU chain中,不是buffers中。如果服务器进程遇到了一个free buffer header,那么它检查它是否被频繁访问。如果被频繁访问,服务器进程将promote这个buffer header,然后继续扫描。如果这个free buffer header不被频繁访问,服务器进程将使用从磁盘读取到的数据块来替换这个buffer,并更新buffer header,移动buffer header到LRU chain的midpoint。注意这里不需要更新CBC结构,因为buffer没有被移动,只有LRU chain上的buffer header被移动。如果服务器进程遇到一个dirty buffer header,那么检查是否是一个被频繁访问的dirty buffer header。如果dirty buffer header被频繁访问,它将promote这个buffer header并继续扫描。如果dirty buffer header不被频繁访问,服务器进程将移动这个buffer header到写列表中。如果服务器进程遇到一个被pin 住的buffer header,那将继续扫描。pin住的buffer被禁止使用。

promotion操作只要达到最低值2(_db_aging_hot_criteria)就会中断。因此当一个服务器进程或数据库写进程在询问“每个buffer的touch count数是多少?”时,它实际是问“buffer的touch count是否大于或等于_db_aging_hot_criteria?”。如果每隔几秒一个buffer就会被touch,那么它应该被保留在cache中。如果不是,它将被快速替换掉。

当一个被频繁访问的buffer被promoted时,它的生命周期将变得更困难。promotion操作的一部分是touch count被设置为0(_db_aging_stay_count)。除非buffer是一个segment header或一个consistent read(CR) buffer,否则会出现这种情况。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_stay_count');

NAME                      VALUE        DESCRIB
------------------------- ------------ --------------------------------------------------------------
_db_aging_stay_count      0            Touch count set when buffer moved to head of replacement list

数据库写进程也可能promote被频繁访问的buffer headers。当一个数据库写进程处于休眠状态,它将每3秒钟被唤醒一次。每个数据库写进程都有一个属于它的写列表(dirty列表)并且它也与一个或多个LRU chain相关联。当一个LRU chain的数据库写进程被唤醒,它将检查它的写列表来查看写列表的长度是否足够执行一个IO写操作。如果数据库写进程决定构建一个写列表,它将扫描它的LRU chain来查找不被频繁访问的dirty buffer。非常像服务器进程查找free buffer那样,数据库写进程也将获得相关的LRU chain lath,从LRU chain的LRU端开始并检查buffer header是否为dirty且不被频繁访问。如果一个不被频繁访问的dirty buffer被找到,数据库写进程将会这个buffer header从LRU chain移动到它的写列表中(记住,这个buffer header仍然存放在CBC结构中,因此它能被其它进程找到)。如果写列表的长度仍然不足够执行一次IO写操作,那么数据库写进程将继续扫描它的LRU chain,查找更多的不被频繁访问的dirty buffer headers。

Hot Region to Cold Region Movement
一个buffer header的生命周期在LRU chain是从midpoint(正中间)开始的。因为其它buffer headers将被替换并且被插入到正中间,随着buffers被promoted,一个buffer header自然地将迁移到LRU chain的LRU端。promote一个buffer header的唯一方法就是buffer header标识为被频繁访问。当一个buffer跨过正中间(midpoint)时另一个显著事件会出现,那就是从hot region移动到cold region。

当一个buffer进入到cold region中时,它的touch count会被重设置为缺省值1(_db_aging_cool_count)。这有冷却hot buffer的效果,任何希望保留在cache中的buffer都不想出现这种情况。增加这个参数值将人为增加buffer值从而增加了buffer移动的可能性。因此缺省情况下,当一个buffer header进行到cold region时,它必须至少被touched一次来使其匹配promotion操作的条件(_db_aging_hot_criteria)。

SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_cool_count');

NAME                      VALUE     DESCRIB
------------------------- --------- ------------------------------------
_db_aging_cool_count      1         Touch count set when buffer cooled


SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm in('_db_aging_hot_criteria');

NAME                       VALUE      DESCRIB
-------------------------- ---------- ---------------------------------------------------------------
_db_aging_hot_criteria     2          Touch count which sends a buffer to head of replacement list

Touch Count Changes
可能会疑问为什么当一个buffer header被promoted和当它进入到cold region时Oracle要重新设置touch count。要理解这一点关键要理解中间点(midpoint)。中间点(midpoint)缺省情况下将每个LRU chain平分为hot与cold
region(_db_percent_hot_default=50),它可以被设置为0到100之间的任何数值。如果LRU chain变成一个100%的hot region,那么唯一的touch count重置将发生在buffer被promoted时。当Oracle释放出创建任何数量buffer pools的能力时,在每个pool中维护中间点(midpoint)的能力将允许高度优化和特定的LRU活动。尽管双重设置可能最初看起来比较愚蠢,但它确实有其真正的目的并为将来奠定了基础。

SQL> select '00 : '||count(*) x from x$bh where tch=0
  2  union
  3  select '01 : '||count(*) x from x$bh where tch=1
  4  union
  5  select '02 : '||count(*) x from x$bh where tch=2
  6  union
  7  select '03 : '||count(*) x from x$bh where tch=3
  8  union
  9  select '04 : '||count(*) x from x$bh where tch=4
 10  union
 11  select '05 : '||count(*) x from x$bh where tch=5
 12  union
 13  select '06 : '||count(*) x from x$bh where tch=6
 14  union
 15  select '07 : '||count(*) x from x$bh where tch=7
 16  union
 17  select '08 : '||count(*) x from x$bh where tch=8
 18  union
 19  select '09 : '||count(*) x from x$bh where tch=9
 20  union
 21  select '10 : '||count(*) x from x$bh where tch=10
 22  union
 23  select '11 : '||count(*) x from x$bh where tch=11
 24  union
 25  select '12 : '||count(*) x from x$bh where tch=12
 26  union
 27  select '13 : '||count(*) x from x$bh where tch=13
 28  union
 29  select '14 : '||count(*) x from x$bh where tch=14
 30  union
 31  select '15 : '||count(*) x from x$bh where tch=15
 32  union
 33  select '16 : '||count(*) x from x$bh where tch=16
 34  /

X
---------------------------------------------
00 : 1879125
01 : 697463
02 : 254482
03 : 227324
04 : 161410
05 : 141651
06 : 91699
07 : 70599
08 : 55605
09 : 25551
10 : 17181
11 : 29833
12 : 19978
13 : 13324
14 : 29006
15 : 9998
16 : 9649

17 rows selected

touch count被重新设置有重要影响。首先,这意味着touch count不会飙升到无穷大。touch count重新设置也意味着最被频繁访问的buffer headers将不需要有最高的touch counts。如果你注意到一个特定的buffer有一个较低的touch count,那么你可能捕获了一个被频繁访问的buffer,只是它可能刚刚被promoted或进入到LRU chain的cold region。事实上,最高touch count的buffer headers将存放在LRU chain的LRU端附近。

LRU Chain Contention Identification and Resolution
Oracle的LRU touch-count算法,与缺省的实例参数设置进行组合来使用微不足道的竞争来启用高性能LRU chain活动。当touch-count算法遇到压力时,这是IO和CPU活动的独特组合。

LRU chain latches命名为cache buffers lru chain。哈希chain latches被命名为cache buffer chains。命名很接近并且可能导致相当大的混乱。只要记住LRU chain latches的名字中lru就不会混乱。在Oracle 10g之前的版本中,等待事件被简化成latch free,为了判断特定的latch,需要使用v$session_wait视图中的p2列与v$latch中的latch#进行关联来进行查询。对于Oracle 10g及以后的版本,等待事件标识为latch:cache buffers lru chain。

如果不需要执行物理读来从磁盘读取数据,那么就不会存在LRU chain latch竞争,因为就不需要查找free buffer或者插入一个buffer header到一个LRU chain中。数据库写进程查找不被频繁访问的dirty buffers不会对LRU chain结构造成压力从而导致LRU chain latch的竞争。然而,任何时候一个服务器进程从磁盘读取数据块,它必须要找到一个free buffer,这将请求LRU chain活动(除了直接路径读)。如果IO读区花了10ms,那么你可能看到的是db file scattered read与db file sequential read等待事件而不是LRU chain latch竞争。但如果IO子系统返回数据块的时间少于5ms,那么压力就转移到CPU子系统了,并且这时LRU chain的活动将开始承受压力。

LRU chain latch竞争可能的结果是获取latch的问题,持有latch大长时间或者两个同时出现。如果操作系统的CPU受限,获得latch可能花费很长时间,因为没有足够的CPU周期。一旦latch被获得且LRU chain相关的内核代码被运行,如果CPU周期供应不足或者不被频繁访问的free buffers有限,LRU chain latch可能被持有很长时间足够造成严重的竞争。

因此,首先,必须要有强烈的物理读取活动。第二,IO子系统响应时间非常快,将大部分的等待时间从读取等待事件传递到LRU chain latch等待事件。这种竞争提供了许多可供组合使用解决方法:
.优化物理IO SQL语句
如果没有物理IO存在就不会有大量的LRU chain latch竞争。因此,从应用程序角度来说产,查找主要活动为执行物理块读取也就是物理IO活动的SQL语句。尽你所能地减少SQL语句的物理IO消耗。这意味着执行经典的SQL优化操作,包括使用索引,以及在性能关键时期减少顶级物理IO SQL语句的执行速度。

.增加CPU处理能力
与CBC latch竞争一样或任何其它latch竞争一样,如果有更多的CPU资源可以使用,内存管理将会花费更少的时间。这意味着latch持有时间与latch获取时间(spinning与sleeping)将被减少。增加CPU处理能力也意味着在竞争高峰期间寻找创建性方法来减秒CPU消耗。

.增加LRU latch数量
通过增加latches可以增加LRU的并发,这意味着增加隐含参数_db_block_lru_latches的值。如果有很多G的buffer cache增加latches可能是特别有效的。

.使用多个buffer pools
一种创造性策略来减少主LRU chain压力的方法就是实现keep与recycle pools。所有的buffer pools都可以增加LRUchain latches的数量。它们也使用touch-count算未能,并且有类似的touch count实例参数,比如_db_percent_hot_keep

.调用touch count实例参数
有几个可用touch count参数。但要注意,这些参数的值都很小,比如1和2。因引,即使参数从1修改为2都是相当大的改变可能导致意想不到的后果。只有在测试后将调整touch count参数作为最后的手段。

_db_percent_hot_default参数,它的缺省值为50。它表示在hot region的buffer headers的百分比。如果想要更多的buffer header存放在hot region,可以增加这个参数。减小这个参数将会给予buffer headers在遇到一个服务器进程或数据库写进程之前更多的时间来被touched。

_db_aging_touch_time参数,它的缺省值为3它是唯一能增加一个buffer header的touch count(x$bh.tch)时间窗口的方法。增加这个参数将减小突然爆发以buffer为中心活动的影响,同时会冒着贬值频繁被访问buffer的风险。

_db_aging_hot_criteria参数,它的缺省值为2。一个buffer header的touch count阈值必须满足或被超过才能被promoted(提升)。如果想一个buffer被promoted更困难,可以增加这个参数值。那么只有真正hot buffers才会被保留在cache中。

_db_aging_stay_count参数,它的缺省值为0。当一个buffer header被promoted时touch count被重设置后的值。一致性读与段头块除外。

_db_aging_cool_count参数,它的缺省值为1。当一个buffer header从hot region进入cold region时touch count被重设置后的值。减小这个参数值将使buffer header被promoted变得更困难。

_db_aging_freeze_cr参数,它的缺省值为false。使一致性读取的 buffers总是为cold状态,因此它们容易被替换。

Oracle Cache Buffer Chains

一个Oracle Buffer是一个Oracle段对象缓存块。一个Oracle buffer一开始时包含与Oracle块中相同的信息。一个buffer的内容依赖于段类型以及它是滞是一个段头块。buffer有许多种状态通过v$bh的state列来表示,它们可能被归纳成在种模式:free(可用),dirty(脏)与pinned(固定)。

Free Buffers
当一个buffer与磁盘上的数据块匹配时它的状态就是free。一个free buffer可以看作是一个镜像buffer,因为它镜像了磁盘上的数据块。下面的查询简单的显示了如何判断buffer cache中free buffers的数量。一个free buffer可能确实是空的(例如,在实例重启之后),但它将最有可能包含真实的块信息,比如行记录。一个free buffer可以被替换而不会产生任何损坏,因为有一个副本存储在磁盘上。当然,如果一个事务提交,那么至少被修改的buffer必须被记录到联机重做日志文件中。

SQL> select count(*) from v$bh where status='free';

  COUNT(*)
----------
        24

一个free buffer可能不是被频繁的访问。也许一个查询需要访问单行数据因此需要将数据块放入buffer cache中,而这个buffer之后再也没有被访问过。而另一方面,一个free buffer也可以是被频繁访问的。例如,如是一个特定的数据块被重复地查询,它将被频繁的访问,但它的状态仍然是free状态,因为buffer没有被改变过。如果你对freebuffer的定义简单又清晰,那么许多Oracle的算法将也变得清晰,这将使理解,检测与解决竞争更容易。

Dirty Buffers
当一个buffer它不能与磁盘上的相关块进行匹配时它的状态就是dirty。对一个buffer进行的任何改变都会使用它的状态变为dirty,因为buffer将不再与磁盘上的块相匹配。当内存中的改变还没有被写入磁盘而要对其进行覆盖时,dirty块是不能被替换的。一旦数据库写进程将一个dirty buffer写入磁盘,那么buffer将与磁盘上的块再一次匹配那么这个buffer的状态将变为free。

一个dirty buffer可能也不被频繁访问。假设一行记录被修改但其它进程不需要访问这个buffer。因为行记录被改变这个块确实是dirty的,但它不被频繁访问。当然,也有被频繁访问的dirty buffers。简单地重复更新一行记录将确保它的buffer的状态为dirty又被频繁的访问。

下面的查询显示dirty buffers的状态可能是xcur或write。将在cache buffer chains中详细介绍current与consistent模式的buffers。xcur状态意味着一个进程已经改变了一个current模式的buffer的状态为这种状态,并且进程可能现在更新buffer中的行记录,虽然行记录现在仍然受制于其它条件,比如行级锁。排他模式不会阻止多个用户改变相同buffer中的多行记录,它简单表示当current模式的buffer可以被改变。在RAC环境中这是至关重要的,可能有多个共享current模式buffers(scur),但在整个RAC数据库中每个块只有一个排他current模式buffer存在。

SQL> select status, count(*) from v$bh where dirty='Y' group by status;

STATUS       COUNT(*)
---------- ----------
xcur            20792
scur              919
pi               2567

Pinned Buffers
当一个buffer被pinned时,它不能被替换。另一种看待pinning的方式是对buffer的一种非官方锁。因为一个buffer不是一种关系结构,标准的锁机制不能应用。Pinning一个特定的buffer,latches或mutexes可以控制访问整组buffers。Pinning可以与latch与lock一起连用来确保适当的序列化,保护与并行控制被实现。

假设一个服务器进程将要读取一个buffer中的一行记录。当你仍然在访问这一行记录时,有人使用其它的buffer替换了你正在访问的buffer这是极端粗鲁的。这就像你正在读一本书时,有一个人说”让我看看”,并从你手中抢走一样。许多进程可以pin相同的buffer(读取相同的块),但是只有一个进程能pinned这个buffer,它不能被替换。当一个free buffer的行记录正被查询时,它的状态从free变为pinned再次回到free。当free buffer中的行记录被修改后,它的buffer状态将从free变为pinned,再变为dirty。

Oracle没有通过v$bh视图来显示pinned buffers,但任何被touched的buffer也就是被pinned了。当一个buffer正被移动到写列表中并且正更新touch计数时Oracle将也会pin这个buffer。

Buffer Headers的作用
当buffers内置在buffer cache中并且buffers确实已经被改变了,列表管理实际作用于buffer headers,而不是实际的buffers。一个buffer header是一个优化过的内存结构它包含关于一个buffer和与它相关的块信息,但不包含块数据比如行记录。

为什么对于buffer cache没有视图v$bc?,这是因为一个buffer与一个块的元数据被存储在buffer header,并且它的元数据对于我们的性能分析是需要的。因此视图被命名为v$bh,对于buffer header有三个关键的列表或链:
.Cache buffers chains(CBCs)被用来快速判断一个Oracle块是否内置在buffer cache中。

.最近最少使用(LRU)列被用来在cache中保留住被频繁访问的buffers并找到free buffers。

.写列表包含不久将被写入磁盘的dirty buffers。

重要的是理解buffer headers的这三个列表而不是实际的buffers。单个buffer header总是内置在一个CBC中和一个LRU链或一个写列表中。

三个列表的维护是在buffer header级别,不是buffer级别,更不是在数据块级别。我们许多人被教导当buffer内置在buffer cache中时,buffers它们本身是被链接的。这是不正确。每个buffer都与一个buffer header相关联,并且在各种列表中操作的是buffer header。

Cache Buffer Chains
简而言之,CBCs被用来回答“这个buffer是否在buffer cache中,如果在,它在哪里”这本质上是一个搜索类型的问题。很多类型的搜索算法可能被用来获得答案:二叉树,B+树,B*树,顺序搜索,哈希算法,或一些算法组合。Oracle选择使用一种哈希算法,紧接着使用快速顺序搜索。

哈希算法
哈希算法可以非常快速,因为整个结构通常被存储在内存中并且要求一个单独的数学计算,同时存在一些内存访问来回答搜索问题。哈希结构有许多变化,但所有的哈希结构都是由一个哈希函数,哈希桶与哈希链组成的。

哈希函数
哈希函数接收输入并使用定义的范围来产生一个输出。输入被叫作一个哈希值。x mod 10函数可以简单地被用来确保不管输入的正数哈希值,它的输出总是在0到9之间。哈希值输入11,输出将是1。一个好的哈希函数将会产生均匀分布的输出。当Oracle将要搜索一个buffer时,基于数据块的文件号与块号的组合(它也叫数据块地址DBA)来生成一个哈希值。因此哈希函数本质上是对buffer的数据块文件号和块号进行哈希运算。这是一种非常方便并可以快速哈希运算的情况。

哈希桶
哈希输入值将被哈希到桶,每个输出值代表了一个单独的桶。在许多哈希情况下,可能输入的哈希值的数量超过桶数。对于Oracle来说,可能的哈希输出值就是Oracle数据块的数量。但在任何情况下,哈希输入值的数量将与buffercache中的buffers的数量相等。

当有两个哈希值被哈希到相同的桶时,这叫作碰撞。碰撞对于哈希来说是很常见的。碰撞可以通过增加哈希桶的数量来最小化,但可能对于高性能程序来说是一种灾难,比如Oracle数据库。例如,假设x mod 10的哈希函数有1000哈希输入值,这将肯定会出现碰撞。为了避免碰撞,哈希算法输出完全均匀的输出将需要1000个哈希桶。使用一种极好的哈希算法与大量的哈希桶两种方法减少碰撞。如果哈希算法不变,那么可以增加哈希桶的数量。

哈希链
每个哈希桶都有一个相关联的哈希链。当一个搜索的对象被哈希到一个桶时,这个桶的链被顺序搜索来查找对象。如果对象在哈希链中没有找到,我们知道对象不在整个哈希结构中。如果哈希链很短,顺序搜索将很快完成。如是对象不在cache中,链长度最好为零。

Oracle的CBC结构是一种复杂的内存结构,并且Oracle必须要维持序列化控制。所以它使用了一种序列化结构:latch或mutex。

如何破坏CBC的性能
要学习如何解决性能问题的最好方法就是知道如何模拟问题,有三种典型的方法来降低CBC的性能:
.当减少latches的数量时,剩余latches的并发将会增加
.如果减少CBCs的数量,平均每个CBC的长度将会增加,剩余chains的并发与CBC的扫描时间也会增加
.如果buffer克隆变得激烈,那么频繁访问的chain将变得很长,会增加并发与CBC的扫描时间

减少latches来限制并发
使用单个latch,序列化将被保证,但是并发性将受到严重的限制。当另一个进程请求latch时而它被其它进程所持有时就会产生竞争。在这个例子中,简单地增加一个latch可以解决这个问题。如果存在上百成千个进程需要访问CBCs,那么可以看到存在严重的并发性能限制问题。幸运地是缺省情况下Oracle创建了上百个CBC latches。

Oracle知道它的哈希函数不完美并且将会产生碰撞。一种减少碰撞的方法是有大量的CBCs。但你第一反应会觉得更多的CBCs将会消耗更多的内存,但事实不是这样的。每个buffer header必须内置在一个CBC链上,与CBC链的数量及长度无关。当使用更多的CBC链时,而buffer headers的数量不变时,平均CBC链的长度会减小。因此,对于每个CBC链虽然有一些额外的内存消耗,但真正的内存消耗者是buffer headers的数量,不仅仅是CBC链的数量。

许多年以前规则定义latches的数量不应该超过CPU核数的两倍。很明显Oracle已经修改了规则,CBC latches只是Oracle数据库中许多latches中的一种。

Oracle可能处理多个CBC latches,有人会认为对于每个CBC将有一个latch,但Oracle认为这是不必要的且一个latch可以管理上百个CBC链。

如果CBC链比buffers多,这意味着有一些CBC链将不会关联buffer header,这将有效的使CBC链的长度变为零。

[oracle@jytest2 ~]$ sqlplus / as sysdba

SQL*Plus: Release 12.2.0.1.0 Production on Thu Mar 21 10:28:02 2019

Copyright (c) 1982, 2016, Oracle.  All rights reserved.


Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL> col param format a50 heading "Instance Param and Value" word_wrapped
SQL> col description format a20 heading "Description" word_wrapped
SQL> col dflt format a5 heading "Dflt?" word_wrapped
SQL> select rpad(i.ksppinm, 35) || ' = ' || v.ksppstvl param,
  2  i.ksppdesc description,
  3  v.ksppstdf dflt
  4  from x$ksppi i,
  5  x$ksppcv v
  6  where v.indx = i.indx
  7  and v.inst_id = i.inst_id
  8  and i.ksppinm in
  9  ('db_block_buffers','_db_block_buffers','db_block_size',
 10  '_db_block_hash_buckets','_db_block_hash_latches'
 11  )
 12  order by i.ksppinm
 13  /

Instance Param and Value                           Description          Dflt?
-------------------------------------------------- -------------------- -----
_db_block_buffers                   = 97136        Number of database   TRUE
                                                   blocks cached in
                                                   memory: hidden
                                                   parameter

_db_block_hash_buckets              = 262144       Number of database   TRUE
                                                   block hash buckets

_db_block_hash_latches              = 8192         Number of database   TRUE
                                                   block hash latches

db_block_buffers                    = 0            Number of database   TRUE
                                                   blocks cached in
                                                   memory

db_block_size                       = 8192         Size of database     FALSE
                                                   block in bytes

引起CBC latch竞争的最好和最简单的方法之一就是创建一个大的buffer cache来缓存更多的块,然后将CBC latches的数量减少到一个。Oracle从10g开始就不允许CBC latches的数量小于1024,但是即使有1024个CBC latches和足够的逻辑IO能力,也能经常看到CBC latch竞争。

通过减少CBC的数量来增加CBC的扫描时间
如果CBCs很长,那么扫描它的时间将会引起显著的竞争。另外其它进程获得CBC latch的时间也会显著增强。一种很明显的方式是增加平均每个CBC的长度来减少CBC的数量,这可以通过减少哈希桶的数量来完成。简单地将实例参数_db_block_hash_buckets减少到50,确保你查询的块内置在buffer cache中,那么会很快得到CBC latch竞争。因为Oracle至少要确保64个哈希桶来忽略你的设置,但这仍然会有大量的竞争。

在现实中,一种解决CBC latch竞争的方法是增加哈希桶的数量,这将减少平均每个CBC的长度。如果一个特定的CBC很长且被频繁文章,那么这个解决方案将不能提高性能。此外Oracle创建了大量的CBC,因此增加哈希桶的数量不像增加CBC一样能显著的提高性能,但它有一种有效的方法应该值得考虑。

使用克隆Buffers来增加CBC的扫描时间
虽然长CBC的问题很少见,但如果出现了,那么情况是很严重的。理解这是如何发生的不仅仅可以帮助你解决这个问题还能更深入的理解CBCs,latch,undo与读一致性。它涉及RAC系统。

长CBC代表了一个非常有挑战性的问题。首先,哈希结构是很快速的因为几乎没有扫描,因此长CBC会迅速降低使用哈希算法的好处。第二,一个扫描进程必须处理一个CBC latch,不是随便一个CBC latch,这个CBC latch保护特定的CBC。一个长CBC意味着CBC latch将被持有更长时间并且当扫描列表将使用更多的CPU。另外,因为CBC latch被持有的时间更长,这将增加另外的进程竞争latch的可能性。当竞争latch的进程在spinning与在sleeping时发布等待事件时都是要消耗CPU的。但问题远不止如此。

正常情况下,Oracle的哈希算法使用的CBC的数量是buffers的两倍还多,因此CBC的长度很短。长CBC出现的唯一方式是多个buffers被哈希到相同的CBC上。通常这不是一个问题,但也可能出现。为了解析这种情况,先了解块克隆与哈希。当一个Oracle块被cached后,只有单个当前模式buffer能被修改。如果buffer中的一行需要被修改,单个当前模式buffer必须是可用的。当前模式buffers有时也叫CU buffers。在RAC系统中,如果需要的当前模式buffer内置在另一个实例中,那它必须被发送到你使用的这个实例中然后才可以修改buffer。

假设一个服务器进程在时间T100正运行一个查询。这个进程访问数据字典并知道它将必须访问一个特定块,因此它将被哈希到合适的CBC,获取合适的CBC latch,扫描CBC,并找到当前模式buffer的buffer header。然而在检查buffer header时,发现当前模式buffer在时间T200被修改过,是在服务器进程开始执行查询之后。这意味着在查询执行后需要的行记录已经被修改过了。 Oracle的缺省读一致性模式要求被返回的信息与查询开始执行时的一致。因此Oracle必须采取操作来确保被返回的信息对于时间T100来说是正确的。

Oracle现在要么找到一个buffer的副本,要么构建一个当前模式buffer的副本,因此这个buffer代表了时间T100所处处的情况。一个buffer副本通常叫做buffer克隆。克隆一个buffer是一种相对昂贵的处理。首先,必须找到一个free buffer,然后buffer header必须被合适的连接到CBC结构与LRU链结构。

理解潜在的重大性能影响的关键是理解被克隆的buffer的buffer header将内置在CBC结构中的什么位置。因为被克隆的buffer是一个合法的buffer,它在buffer cache中占据了空间,能被共享且必须被定位。这意味着它必须被合适的内置在CBC结构中。被克隆的buffer的文件号与块号与它的当前模式buffer的相同,这意味着它必须被哈希到相同的CBC。因此,如果一个buffer有50个克隆副本,与它相关的CBC将至少有50个buffer header那么长,并且如果与其它buffer出现碰撞可能更长。Oracle对此无能为力,因为哈算法是基于文件号与块号的。

不仅free buffer搜索算法有利于替换克隆的buffer,但Oracle试图限制每个buffer的克隆数量。Oracle想要每个buffer的克隆数量不超过隐含参数_db_block_max_cr_dba,它的缺省值为6。然而如果克隆变得很激烈,一个buffer的克隆副本很容易超过6个。

SQL> col name for a30
SQL> col value for a20
SQL> col describ for a50
SQL> select x.ksppinm NAME,y.ksppstvl value,x.ksppdesc describ
  2  from x$ksppi x, x$ksppcv y
  3  where x.inst_id=USERENV('Instance')
  4  and y.inst_id=USERENV('Instance')
  5  and x.indx=y.indx
  6  and x.ksppinm like '%&par%';
Enter value for par: _db_block_max_cr_dba

NAME                           VALUE                DESCRIB
------------------------------ -------------------- --------------------------------------------------
_db_block_max_cr_dba           6                    Maximum Allowed Number of CR buffers per dba

1 row selected.

有许多克隆的buffer不一定意味着有性能问题。如果真的出现性能问题,CBC latch竞争问题将非常明显。如果出现这种情况并发现克隆buffer的问题,那么考虑以下可能的补救措施:
.修复应用程序
这通常是必须要做的。这是非常痛苦的,需要开会,如果应用程序开发者参与将会非常专业化,并且通常要求应用程序以某些方式被修改来减少单个克隆buffer被频繁的访问。

.移动行记录
如果幸运的话,可能存在多行记录使得buffer被频繁访问。如果可能散这些行,因此多个buffer现在不再被频繁的访问。当修改传统的pct_free与pct_used存储参数是一种选择时,为了增加控制,可以考虑设置一个块可以存储的最大记录数。意外地是这不仅仅是简单地执行类似于alter table all_status minimizer records_per_block 5语句

.平衡工作负载
如果能控制工作负载强度,在克隆活动高峰期间,考虑减少与buffer克隆活动相关的工作负载。虽然这不是一个令人兴奋的解决方案,工作负载平衡也能对性能产生积极影响。

CBC竞争识别与解决方案
一些解决方案可以帮助你解决CBC竞争的问题。在尝试解决CBC latch问题之前,确保它们存在。

SQL> @swpctx
Remember: This report must be run twice so both the initial and
final values are available. If no output, press ENTER twice.

DB/Inst: RLZY/RLZY1                                               25-Mar 11:24am
Report:   swpctx.sql           OSM by OraPub, Inc.                Page         1
            System Event CHANGE (17 sec interval) Activity By PERCENT

                                       Time Waited  % Time    Avg Time     Wait
Wait Event Display Name                      (sec)  Waited Waited (ms) Count(k)
-------------------------------------- ----------- ------- ----------- --------
latch: cache buffers chains                 10.610   96.28        15.7        1
control file parallel write                  0.160    1.45         7.6        0
log file parallel write                      0.030    0.27        15.0        0
log file sync                                0.000    0.00         0.0        0

如果数据库系统是Oracle 10g之前的版本,那么top wait event将会是latch free,就需要确认latch问题是CBClatch。对于Oracle 10g及以后的版本,wait event将是latch: cache buffers chains。在大多数情况下,CPU子系统将被大量利用并且负担过重。以下是可能的CBC latch解决方案:
.优化逻辑IO SQL语句
当回答“buffer是否在buffer cache”中时CBC结构将变得紧张起来,期待的答案总是“Yes”,如果答案为“No”,将会看到顺序读或分散读等待事件。因此从应用程序角度来看,查找执行活动主要是buffer gets也就是逻辑IO的SQL尽你所能地减少逻辑IO消耗。这是典型的SQL优化,包括索引,以及在性能问题出现时减少执行速率。

.增加CPU处理能力
在大多数情况下,CPU子系统将被过多利用并且可能是操作系统瓶颈。latch的获得与相关的内存管理可能消耗过多的CPU资源。做任何可以减少CPU消耗与能增加CPU能力的事。查找在高峰期间没有执行或正在执行的进程。考虑增加或者使用更快的CPU。如果正在运行在虚拟环境中,考虑确保Oracle系统已经增加CPU资源。然而,请注意除非应用程序工作负载已经显著增加,增加的CPU处理能力通常将被快速地消耗掉。真正的解决方案可能是其它的方案。增加CPU能力可能是一个快速解决方案,但它可能不能真正地解决问题。

.检查buffer克隆问题
无论何进遇到CBC latch竞争问题,都需要检查是否存在buffer克隆的问题。这是很少见的情况,但如果遇到了,那么解决方案与其它解决方案是非常不同的。

.增加CBC latch数量
这通常会带来一些安慰,但不是真正的优化逻辑IO SQL。隐含参数_db_block_hash_latches控制着CBC latch的数量

.增加CBC buckets
它很难对性能产生影响,因为Oracle缺省情况下,创建了大量的buckets。除非之前减少了CBC buckets的数量,增加这个参数的大小将会显著地影响性能。

In-Memory Column Store

使用In-Memory Column Store
从Oracle 12.1.0.2开始可以使用In-Memory Column Store。In-Memory Column Store对于SGA是一个可选部分用来存储表,表分区与其它数据库对象副本。在In-Memory Column Store中,数据是以列式被加载而不是像SGA中其它部分的数据是以行式被加载,并且数据被优化用于快速扫描。

In-Memory Column Store在SGA中是一个新的静态内存池。在In-Memory Column Store中所存储的数据不是使用传统行格式而是使用列式。每个列作为单独的结构进行存储。In-Memory Column Store不会替代buffer cache,是对buffer cache的一种补充,因此数据在内存中可以以行与列的形式进行存储。为了启用In-Memory Column Store,必须将inmemory_size参数设置为非零值。

可以在以下级别启用In-Memory Column Store
.列
.表
.物化视图
.表空间
.分区

如果在表空间级别启用In-Memory Column Store,那么所有存储在该表空间中的所有表与物化视图将抽默认启用In-Memory Column Store。可以将一个数据库对象的所有列或者将一个数据库对象的部分列加载到In-Memory Column Store中。类似地,对于分区表或物化视图,可以将所有分区或部分分区加载到In-Memory Column Store中。

在In-Memory Column Store中存储数据库对象可以显然提高对数据库对象执行以下类型操作的性能:
.查询扫描大量数据并且使用=,< ,>与in操作来进行过滤
.查询从表中或者从有大量列的物化视图中选择少量列,比如从有100列的表中选择5列
.查询对小表与大表进行关联
.查询将聚集数据

通常情况下,创建多列索引可以提高分析与报告查询的性能。这些索引可能影响DML语句的性能。当数据库对象被加载到In-Memory Column Store中,用于分析或报告查询的索引可以被减少或消除而不会影响查询的性能。消除这些索引可以提高事务和数据加载操作的性能。

可以通过对以下语句增加inmemory子句的方式来对数据库对象启用In-Memory Column Store:
.create table
.alter table
.create tablespace
.alter tablespace
.create materialized view
.alter materialized view

为了判断那个数据库对象被加载到In-Memory Column Store中,可以查询v$in_segments视图:

SQL> alter table jy inmemory;
Table altered

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION   FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION   FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

SQL> select count(*) from jy;
  COUNT(*)
----------
     72736

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION  FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY LOW

In-Memory Column Store不能对以下操作类型提高性能:
.有复杂谓词的查询
.选择大量列的查询
.返回大量数据行的查询
.使用大表联接的查询

SYS用户的对象并且存储在system或sysaux表空间中,那么这种对象不能被加载到In-Memory Column Store中。

In-Memory Column Store压缩方法
在In-Memory Column Store中,数据是可以被压缩的,并且SQL查询可以直接对压缩数据进行查询。In-Memory Column Store压缩方法有:
no memcompress 数据不会被压缩

memcompress for dml 这种方法只对DML操作的数据进行优化与压缩

memcompress for query low 这种方法将提供最佳的查询性能。这种压缩方法比memcompress for dml所压缩的数据要多但比
memcompress for query high压缩方法所压缩的数据要少。当在create或alter语句中指定inmemory子句但没指定压缩方法时或者当指定memcompress for query而没指定low或high时所使用的缺省值

memcompress for query high 这种方法会提供杰出的查询性能。这种压缩方法所压缩的数据量要比memcompress for query low方法多,但比memcompress for capacity low方法所压缩的数据量少

memcompress for capacity low 这种方法会提供很好的查询性能。这种压缩方法所压缩的数据量要比memcompress for query high方法所压缩的数据量多,但比memcompress for capacity high方法所压缩的数据量少。当指定memcompress for capacity而没有指定low或high时所使用的缺省值

memcompress for capacity high 这种方法提供的查询性能一般,但它的所压缩的数据量最多

SQL> alter table jy inmemory;
Table altered

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION
  2  FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION
  2  FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

SQL> select count(*) from jy;
  COUNT(*)
----------
     72736

SQL> 
SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION
  2  FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY LOW

关键字memcompres必须要出现在inmemory后面

SQL> alter table jy  inmemory memcompress for query high;
Table altered

SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

SQL> select count(*) from jy;
  COUNT(*)
----------
     72736


SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY HIGH

In-Memory Column Store Data Population Options
当对数据库对象雇用In-Memory Column Store时,可以让数据库来控制数据库对象何时被加载到In-Memory Column Store中,或者可以指定一个优先级别来决定加载队列中数据库对象的优先级。Oracle SQL包含一个inmemory priority子句来对于加载队列提供更多的控制。例如,它可以在加载其它数据库对象的数据之前将更重要或不重要的数据库对象的数据进行加载。

在In-Memory Column Store中对于加载数据库对象提供了以下优先级:
priority none Oracle数据库控制何时将数据库对象的数据加载到In-Memory Column Store中。对这个数据库对象进行扫描会触发将这个对象加载到In-Memory Column Store中。当priority在inmemory子句中没有指定时这是它的缺省级别。例如当对表jy修改In-Memory Column Store的压缩方法后,立即查询这个对象是否被加载到In-Memory Column Store中会发现并没有加载

SQL> alter table jy  inmemory memcompress for query low;
Table altered

SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------

因为没有指定加载数据库对象的优先级,所以需要执行查询进行扫描来触发将其加载到In-Memory Column Store中

SQL> select count(*) from jy;
  COUNT(*)
----------
     72736

SQL> SELECT OWNER, SEGMENT_NAME, INMEMORY_PRIORITY, INMEMORY_COMPRESSION FROM V$IM_SEGMENTS;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY LOW

现在可以看到表jy已经被加载到In-Memory Column Store中了,可以看到inmemory_priority为none

priority low 在优先级为none的数据库对象之前将优先级为low的对象加载到In-Memory Column Store中,但在优先级为medium,high或critical的数据库对象被加载到In-Memory Column Store中之后才会将优先级为low的对象加载到In-Memory Column Store中

priority medium 在优先级为none或low的数据库对象之前将优先级为medium的对象加载到In-Memory Column Store中,但在优先级为high或critical的数据库对象被加载到In-Memory Column Store中之后才会将优先级为medium的对象加载到In-Memory ColumnStore中。

priority high 在优先级为none,low或medium的数据库对象之前将优先级为high的对象加载到In-Memory Column Store中,但在优先级为critical的数据库对象被加载到In-Memory Column Store中之后才会将优先级为medium的对象加载到In-Memory ColumnStore中。

priority critical 在优先级为none,low,medium或high的数据库对象之前将优先级为critical的对象加载到In-Memory Column Store中

当多个数据库对象设置了优先级而不为none时,Oracle数据库将对象的所有数据基于它们的优先级别进行排队来将它们加载到In-Memory Column Store中。优先为critical的对象将会最先被加载,接下来就是优先级为high的对象会被加载,依此类推。如果在In-Memory Column Store中没有足够的空间,那么额外要被加载的对象直到有足够空间之前是不会被加载的。当数据库被重启时,优先级不为none的对象会在数据库启动时被加载到In-Memory Column Store中。对于优先级不为none的数据库对象来说,alter table或alter materialized view DDL语句在DDL语句被记录在In-Memory Column Store之前不会返回执行结果。

下面的例子,创建表t1,在没有设置inmemory属性时,表t1是没有被加载到In-Memory Column Store中的

SQL> create table t1 as select * from jy;
Table created

SQL> select owner, segment_name, inmemory_priority, inmemory_compression from v$im_segments;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY LOW

修改表的inmemory属性,并且设置了加载优先级为high,数据压缩为memcompress for query high,在修改完成后,表t1就已经被加载到In-Memory Column Store中了

SQL> alter table t1 inmemory priority high memcompress for query high;
Table altered


SQL> select owner, segment_name, inmemory_priority, inmemory_compression from v$im_segments;
OWNER                                                                            SEGMENT_NAME                                                                     INMEMORY_PRIORITY INMEMORY_COMPRESSION
-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- ----------------- --------------------
JY                                                                               JY                                                                               NONE              FOR QUERY LOW
JY                                                                               T1                                                                               HIGH              FOR QUERY HIGH

优先级的设置必须应用给整个表或一个表分区。对表中的不同列集合指定不同的In-Memory Column Store加载优先级别是不允许的。如果一个段在磁盘上的大小为64KB或者更小,那么这个对象不会被加载到In-Memory Column Store中。因此对于一些较小的对象虽然启用了In-Memory Column Store,但可能并不会被加载到In-Memory Column Store中。

In-Memory Column Store相关的初始化参数
inmemory_size:这个参数设置实例中的In-Memory Column Store的大小,它的缺省值为0,这意味着没有雇用In-Memory Column Store。为了雇用In-Memory Column Store这个参数必须设置为非0值。如果这个参数设置为非0值,那么它的最小值为100M。

在多租户环境中,在root容器中设置这个参数是对整个CDB生效的。这个参数也可以对每个PDB进行设置来限制每个PDB中的In-Memory Column Store的大小。PDB的In-Memory Column Store的总大小可以小于,等于或大于CDB的值。然而CDB所设置的In-Memory Column Store的大小是整个CDB中,包含root与所有PDB的In-Memory Column Store可以使用的最大内存,这意味着PDB可以CDB中所有可以使用的In-Memory Column Store的内存。

inmemory_force:这个参数可以对表与物化视图启用In-Memory Column Store或者对表与物化视图禁用In-Memory Column Store,设置这个参数为default,这是它的缺省值,它将允许通过单个对象的inmemory或no inmemory属性来决定是否将对象加载到In-Memory Column Store中。将这个参数设置为off来指定所有表与物化视图将禁用In-Memory Column Store.

inmemory_clause_default:这个参数能让你对新表与物化视图指定一个缺省的In-Memory Column Store子句。不设置这个参数或者设置为一个空字符串来指定对于新表与物化视图不存在缺省的In-Memory Column Store子句。将这个参数设置为no inmemory与它的缺省值(空字符串)有相同的作用。将这个参数设置为有效的inmemory子句来指定它为所有新表与物化视图的In-Memory Column Store的缺省值。这个子句可以包含In-Memory Column Store压缩方法与数据加载选项。如果子句以inmemory开头,那么所有新表与物化视图,包含那些没有inmemory子句的对象,将会被加载到In-Memory Column Store中。如果子句忽略了inmemory,那么它只会对在创建时指定了inmemory子句的新表与物化视图雇用In-Memory Column Store。

inmemory_query:这个参数指定是否允许in-memory查询。将这个参数设置为enable,它是缺省值,允许查询所访问的对象被加载到In-Memory Column Store中,将这个参数设置为disable来禁止将查询所访问的对象加载到In-Memory Column Store中。

inmemory_max_populate_servers:这个参数指定执行In-Memory Column Store加载操作的后台加载服务器进程的最大数量,因此这些服务器进程不能超过系统所能承受的负载。基于系统中的CPU内核数来设置一个合理值。

inmemory_trickle_repopulate_servers_percent:这个参数用来限制用于In-Memory Column Store重新加载的后台加载服务器进程的最大数量,trickle repopulation被设计只占加载服务器进程总数量的很小百分比。这个参数的值为
inmemory_max_populate_servers参数值的一个百分比。例如,如果这个参数设置为10,并且inmemory_max_populate_servers被设置为10,那么将会有一个cpu内核用于tricle repopulation操作。

optimizer_inmemory_aware:这个参数用来启用或禁用优化器成本模型来增强In-Memory Column Store。将这个参数设置为false,将造成优化器在优化SQL语句时忽略有in-memory属性的表。

对数据库启用In-Memory Column Store
在表,表空间或物化视图被启用In-Memory Column Store之前,必须对数据库启用In-Memory Column Store。对数据库启用In-Memory Column Store需要执行以下步骤:
1.确保数据库的兼容性参数设置为12.1.0或更高版本。
2.将inmemory_size参数设置为非零值。当对spfile参数文件设置这个参数时,可以使用alter system语句,并且必须指定scope=spfile,它的最小值为100M。
3.重启数据库,为了在SGA中初始化In-Memory Column Store必须要重启数据库。
4.可选操作,可以执行下面的语句来检查当前为In-Memory Column Store所分配的内存大小。

SQL> show parameter inmemory_size

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
inmemory_size                        big integer 1G

对表启用与禁用In-Memory Column Store
通过对create table或alter table语句指定inmemory子句来对表启用In-Memory Column Store。通过对create table或alter table语句指定no inmemory子句对表禁用In-Memory Column Store。

为了对表启用与禁用In-Memory Column Store,需要执行以下步骤:
1.确保数据库已经启用了In-Memory Column Store
2.使用有合适权限的用户连接到数据库并创建或修改表
3.执行create table或alter table并指定inmemory或no inmmeory子句

创建表并启用In-Memory Column Store
下面的例子将创建一个名叫test_inmem的表并启用In-Memory Column Store

SQL> create table test_inmem(id number(5) primary key,test_col varchar2(15)) inmemory;

Table created.

这个例子对inmemory子句使用缺省值。因此将使用memcompress for query与priority none

对表启用In-Memory Column Store
下面的例子将对表oe.product_information表启用In-Memory Column Store:

SQL> alter table oe.product_information inmemory;

Table altered.

这个例子对inmemory子句使用缺省值。因此将使用memcompress for query与priority none

对表启用In-Memory Column Store并且指定压缩方法为for capacity low
下面的例子对表oe.product_information启用In-Memory Column Store并且指定压缩方法为for capacity low:

SQL> alter table oe.product_information inmemory memcompress for capacity low;

Table altered.

这个例子对priority子句使用缺省值,因此优先级为priority none。

对表启用In-Memory Column Store并且指定数据加载优先级为high
下面的例子对表oe.product_information启用In-Memory Column Store并且指定数据加载优先级为priority high

SQL> alter table oe.product_information inmemory priority high;

Table altered.

这个例子对memcompress子句使用缺省值,因此压缩方法为memcompress for query

对表启用In-Memory Column Store并且指定压缩方法为for capacity high,数据加载优先级为low
下面的例子对表oe.product_information启用In-Memory Column Store并且指定压缩方法为for capacity high,数据加载优先级为priority low

SQL> alter table oe.product_information inmemory  memcompress for capacity high priority low;

Table altered.

对表中的列启用In-Memory Column Store
下面的例子对表oe.product_information表中的一些列启用In-Memory Column Store而剩余的列不雇用。并且对不同的列子集启用不同的In-Memory Column Store压缩方法。

SQL> alter table oe.product_information
  2  inmemory memcompress for query (product_id, product_name, category_id, supplier_id, min_price)
  3  inmemory memcompress for capacity high (product_description, warranty_period, product_status, list_price)
  4  no inmemory (weight_class, catalog_url);

Table altered.

在这个例子中定义如下
以product_id开始至min_price结束的字段列表启用了In-Memory Column Store并且指定压缩方法为memcompress for query.

以product_description开始至list_price结束的字段列表启用了In-Memory Column Store并且指定压缩方法为memcompress for
capacity high

字段weight_class与catalog_url没有启用In-Memory Column Store。

在这个例子中对于priority子句使用了缺省值。因此将使用priority none。可以查询v$in_column_level视图,注意优先级的设置
必须应用给整个表或表分区。对不同字段列表指定不同的优先级是不允许的。

对表禁用In-Memory Column Store
为了对表禁用In-Memory Column Store,只需使用no inmemory子句。下面将对表oe.product_information禁用In-Memory Column Store:

SQL> alter table oe.product_information no inmemory;

Table altered.

对表空间启用与禁用In-Memory Column Store
在使用create tablespace语句来创建表空间时可以通过指定inmemory子句来让表空间启用In-Memory Column Store。也可以使用包含inmemory子句的alter tablespace语句来修改表空间让其启用In-Memory Column Store。

通过执行包含no inmemory子句的create tablespace或alter tablespace语句来对表空间禁用In-Memory Column Store。

当表空间启用In-Memory Column Store之后,存储在该表空间中的所有表与物化视图会默认启用In-Memory Column Store。对于表,物化视图与表空间inmemory子句是相同的。当对表空间启用In-Memory Column Store时在inmemory子句之前,并且当对表空间禁用In-Memory Column Store时在no inmemory子句之前需要使用default存储子句。

当表空间启用In-Memory Column Store后,存储在表空间中的单个表与物化视图可以有不同的in-memory设置,并且单个对象的设置会覆盖表空间级别的设置。例如,如果表空间对于数据加载优先级设置为priority low,但在表空间中的表的数据加载优先级设置为priority high,那么表将使用priority high。

为了对表空间启用或禁用In-Memory Column Store,执行以下操作:
1.确保对数据库启用了In-Memory Column Store。
2.使用有合适权限的用户连接到数据库来创建或修改表空间。
3.执行有inmemory或no inmemory子句的create tablespace或alter tablespace语句

下面的例子将创建一个表空间tbs1并对它启用In-Memory Column Store

SQL> create tablespace tbs1 datafile 'tbs1.dbf' size 40m online default inmemory;

Tablespace created.

这个例子对inmemory子句使用了缺省值,因此压缩方法为memcompress for query,数据加载优先级为priority none。

下面的例子将修改表空间tbs1让其启用In-Memory Column Store并且指定数据压缩方法为memcompress for capacity high,数据加载优先级为priority low

SQL> alter tablespace tbs1 default inmemory memcompress for capacity high priority low;

Tablespace altered.

对物化视图启用与禁用In-Memory Column Store
通过执行包含inmemory子句的create materialized view或alter materialized view语句来对物化视图启用In-Memory Column Store。通过执行包含no inmemory子句的ceate materialized view或alter materialized view语句来对物化视图禁用In-Memory Column Store。

为了对物化视图雇用与禁用In-Memory Column Store,需要执行以下操作:
1.确保对数据库启用了In-Memory Column Store。
2.使用有合适权限的用户连接到数据库来创建或修改物化视图。
3.执行包含inmemory或no inmemory子句的create materialized view或alter materialized语句

下面的例子创建一个名叫oe.prod_info_mv的物化视图并且启用In-Memory Column Store:

SQL> create materialized view oe.prod_info_mv inmemory as select * from oe.product_information;

Materialized view created.

这个例子对inmemory子句使用缺省值。因此将使用memcompress for query与priority none

下面的例子将让物化视图oe.prod_info_mv启用In-Memory Column Store并且指定数据加载优先级为priority high

SQL> alter materialized view oe.prod_info_mv inmemory priority high;

Materialized view altered.

Data Pump与In-Memory Column Store
在impdp命令中通过使用transform=inmemory:y选项来导入启用了In-Memory Column Store的数据库对象。使用这个选项,Data Pump将对所有对象保留In-Memory Column Store子句。当transform=inmemory:n时,Data Pump将对对象删除In-Memory Column Store子句。

也可以使用transform=inmemory_clause:string选项来覆盖在dump文件中数据库对象所设置的In-Memory Column Store子句。例如可以使用这个选项来修改被导入对象的In-Memory Column Store的数据压缩方法。

rman存储脚本

存储脚本是命令文件的一种替方法用来管理频繁使用的一系列RMAN命令。存储脚本相比命令文件来说它的优点就是对于任何连接到目标数据库和恢复目录的RMAN客户端都能够使用它。而命令文件只有能够访问它的RMAN客户端能够使用。

存储脚本可以是全局或者局部脚本。一个局存储脚本与创建该脚本时RMAN所连接的目标数据库相关,只能当连接到该目标数据库时才能执行存储脚本。一个全局的存储脚本能在恢复目录中所注册的所有数据库中执行。注意执行存储脚本,即使是全局存储脚本,也必须连接到恢复目录和目标实例才能执行。

创建存储脚本
确保RMAN连接到正确的目标数据库和恢复目录,然后执行create script命令来创建局部存储脚本。例如:

create script full_backup
{
backup as backupset database plus archivelog;
delete obsolete;
}


RMAN> create script full_backup
2> {
3> backup as backupset database plus archivelog;
4> delete obsolete;
5> }

created script full_backup

执行创建命令如果没有输出错误信息,那么该存储脚本full_backup就创建成功并存储在恢复目录中。

下面创建一个全局存储脚本,如下所示:

create global script global_full_backup
{
backup as backupset database plus archivelog;
delete obsolete;
}


RMAN> create global script global_full_backup
2> {
3> backup as backupset database plus archivelog;
4> delete obsolete;
5> }

created global script global_full_backup

还可以提供了comment来描述全局存储脚本:

create global script global_full_backup
comment 'use only with archivelog mode database'
{
backup as backupset database plus archivelog;
delete obsolete;
}

RMAN> create global script global_full_backup
2> comment 'use only with archivelog mode database'
3> {
4> backup as backupset database plus archivelog;
5> delete obsolete;
6> }

created global script global_full_backup

还可以读取文本文件来创建局部或全局存储脚本:

RMAN> create script test_script from file '/u01/app/oracle/test_script_file.txt';

script commands will be loaded from file /u01/app/oracle/test_script_file.txt
created script test_script

RMAN> create global script global_test_script from file '/u01/app/oracle/test_script_file.txt';

script commands will be loaded from file /u01/app/oracle/test_script_file.txt
created global script global_test_script

文本文件内宾3必须以{开始,再接RUN块中的一系列有效的命令,最后以}结束。

执行存储脚本
为了执行存储脚本,连接到目标数据库和恢复目录,使用execute script命令,execute script命令要求使用RUN块:

RMAN> run
2> {
3> execute script full_backup;
4> }

executing script: full_backup


Starting backup at 18-MAR-15
current log archived
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=145 devtype=DISK
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=1 recid=9 stamp=871547115
input archive log thread=1 sequence=2 recid=10 stamp=872692777
input archive log thread=1 sequence=3 recid=11 stamp=873390010
input archive log thread=1 sequence=4 recid=12 stamp=873477689
input archive log thread=1 sequence=5 recid=13 stamp=873557205
input archive log thread=1 sequence=6 recid=14 stamp=873628154
input archive log thread=1 sequence=7 recid=15 stamp=873881476
input archive log thread=1 sequence=8 recid=16 stamp=873969596
input archive log thread=1 sequence=9 recid=17 stamp=874234871
input archive log thread=1 sequence=10 recid=18 stamp=874315436
input archive log thread=1 sequence=11 recid=19 stamp=874486364
input archive log thread=1 sequence=12 recid=20 stamp=874574023
input archive log thread=1 sequence=13 recid=21 stamp=874579545
input archive log thread=1 sequence=14 recid=22 stamp=874657043
input archive log thread=1 sequence=15 recid=23 stamp=874663015
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpvd26_.bkp tag=TAG20150318T095657 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:27
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=44 recid=6 stamp=870948358
input archive log thread=1 sequence=45 recid=7 stamp=870948359
input archive log thread=1 sequence=46 recid=8 stamp=870948359
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpw6cl_.bkp tag=TAG20150318T095657 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backupset
channel ORA_DISK_1: specifying datafile(s) in backupset
input datafile fno=00001 name=/u01/app/oracle/oradata/jy/system01.dbf
input datafile fno=00002 name=/u01/app/oracle/oradata/jy/undotbs01.dbf
input datafile fno=00003 name=/u01/app/oracle/oradata/jy/sysaux01.dbf
input datafile fno=00005 name=/u01/app/oracle/oradata/jy/example01.dbf
input datafile fno=00004 name=/u01/app/oracle/oradata/jy/users01.dbf
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T095727_bjkpw9xz_.bkp tag=TAG20150318T095727 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:15
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=16 recid=24 stamp=874663125
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095845_bjkpyqdc_.bkp tag=TAG20150318T095845 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting Control File and SPFILE Autobackup at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663128_bjkpytxr_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 18-MAR-15

RMAN retention policy will be applied to the command
RMAN retention policy is set to redundancy 1
using channel ORA_DISK_1
Deleting the following obsolete backups and copies:
Type                 Key    Completion Time    Filename/Handle
-------------------- ------ ------------------ --------------------
Archive Log          3769   06-FEB-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_44_bf88d537_.arc
Archive Log          3770   06-FEB-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_45_bf88d73o_.arc
Archive Log          3771   06-FEB-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_46_bf88d7cv_.arc
Archive Log          3772   13-FEB-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_13/o1_mf_1_1_bftk375y_.arc
Archive Log          3773   26-FEB-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_26/o1_mf_1_2_bgxhx2ww_.arc
Archive Log          3774   03-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_03/o1_mf_1_3_bhbvorcp_.arc
Archive Log          3775   04-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_04/o1_mf_1_4_bhfk9mdr_.arc
Archive Log          3776   05-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_05/o1_mf_1_5_bhhyxwnp_.arc
Archive Log          3777   06-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_06/o1_mf_1_6_bhl47mgr_.arc
Archive Log          3778   09-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_09/o1_mf_1_7_bhsvmzby_.arc
Archive Log          3779   10-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_10/o1_mf_1_8_bhwkos6m_.arc
Archive Log          3780   13-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_13/o1_mf_1_9_bj4nqm2j_.arc
Archive Log          3781   14-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_14/o1_mf_1_10_bj73f7y4_.arc
Archive Log          3782   16-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_16/o1_mf_1_11_bjdbbrvm_.arc
Archive Log          3783   17-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_17/o1_mf_1_12_bjgzxz92_.arc
Archive Log          3784   17-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_17/o1_mf_1_13_bjh5bqyo_.arc
Archive Log          3785   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_14_bjkk0h4p_.arc
Backup Set           3786   06-FEB-15
  Backup Piece       3788   06-FEB-15          /u01/app/oracle/flash_recovery_area/JY/autobackup/2015_02_06/o1_mf_s_870945982_bf861zo6_.bkp
Datafile Copy        3794   03-MAR-15          /u01/app/oracle/users01.dbf
Backup Set           3787   06-FEB-15
  Backup Piece       3789   06-FEB-15          /u01/app/oracle/flash_recovery_area/JY/autobackup/2015_02_06/o1_mf_s_870948381_bf88dyom_.bkp
Archive Log          3806   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_15_bjkpv6py_.arc
Backup Set           3813   18-MAR-15
  Backup Piece       3817   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpvd26_.bkp
Backup Set           3814   18-MAR-15
  Backup Piece       3818   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpw6cl_.bkp

Do you really want to delete the above objects (enter YES or NO)? YES
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_44_bf88d537_.arc recid=6 stamp=870948358
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_45_bf88d73o_.arc recid=7 stamp=870948359
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_06/o1_mf_1_46_bf88d7cv_.arc recid=8 stamp=870948359
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_13/o1_mf_1_1_bftk375y_.arc recid=9 stamp=871547115
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_02_26/o1_mf_1_2_bgxhx2ww_.arc recid=10 stamp=872692777
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_03/o1_mf_1_3_bhbvorcp_.arc recid=11 stamp=873390010
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_04/o1_mf_1_4_bhfk9mdr_.arc recid=12 stamp=873477689
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_05/o1_mf_1_5_bhhyxwnp_.arc recid=13 stamp=873557205
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_06/o1_mf_1_6_bhl47mgr_.arc recid=14 stamp=873628154
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_09/o1_mf_1_7_bhsvmzby_.arc recid=15 stamp=873881476
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_10/o1_mf_1_8_bhwkos6m_.arc recid=16 stamp=873969596
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_13/o1_mf_1_9_bj4nqm2j_.arc recid=17 stamp=874234871
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_14/o1_mf_1_10_bj73f7y4_.arc recid=18 stamp=874315436
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_16/o1_mf_1_11_bjdbbrvm_.arc recid=19 stamp=874486364
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_17/o1_mf_1_12_bjgzxz92_.arc recid=20 stamp=874574023
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_17/o1_mf_1_13_bjh5bqyo_.arc recid=21 stamp=874579545
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_14_bjkk0h4p_.arc recid=22 stamp=874657043
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_02_06/o1_mf_s_870945982_bf861zo6_.bkp recid=4 stamp=870948272
deleted datafile copy
datafile copy filename=/u01/app/oracle/users01.dbf recid=2 stamp=873392067
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_02_06/o1_mf_s_870948381_bf88dyom_.bkp recid=5 stamp=870948382
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_15_bjkpv6py_.arc recid=23 stamp=874663015
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpvd26_.bkp recid=6 stamp=874663020
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095657_bjkpw6cl_.bkp recid=7 stamp=874663046
Deleted 23 objects

上面的命令将调用指定名称的局部脚本full_script。如果没有找到局部脚本,但存在相同名称的全局脚本,RMAN将执行同名的全局存储脚本。如果存在同名的局部和全局脚本可以使用execute global script来控制只执行全局脚本。在恢复目录中没有名叫global_full_backup的局部脚本存在,那么执行下面两个命令的结果是一样的:

RMAN> run
2> {
3> execute global script global_full_backup;
4> }

executing global script: global_full_backup


Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=16 recid=24 stamp=874663125
input archive log thread=1 sequence=17 recid=25 stamp=874663571
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100613_bjkqdppq_.bkp tag=TAG20150318T100613 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backupset
channel ORA_DISK_1: specifying datafile(s) in backupset
input datafile fno=00001 name=/u01/app/oracle/oradata/jy/system01.dbf
input datafile fno=00002 name=/u01/app/oracle/oradata/jy/undotbs01.dbf
input datafile fno=00003 name=/u01/app/oracle/oradata/jy/sysaux01.dbf
input datafile fno=00005 name=/u01/app/oracle/oradata/jy/example01.dbf
input datafile fno=00004 name=/u01/app/oracle/oradata/jy/users01.dbf
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T100615_bjkqds89_.bkp tag=TAG20150318T100615 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:15
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=18 recid=26 stamp=874663651
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100732_bjkqh6cv_.bkp tag=TAG20150318T100732 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting Control File and SPFILE Autobackup at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663655_bjkqh9db_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 18-MAR-15

RMAN retention policy will be applied to the command
RMAN retention policy is set to redundancy 1
using channel ORA_DISK_1
Deleting the following obsolete backups and copies:
Type                 Key    Completion Time    Filename/Handle
-------------------- ------ ------------------ --------------------
Archive Log          3845   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_16_bjkpynv3_.arc
Backup Set           3846   18-MAR-15
  Backup Piece       3850   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T095727_bjkpw9xz_.bkp
Backup Set           3864   18-MAR-15
  Backup Piece       3870   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095845_bjkpyqdc_.bkp
Archive Log          3913   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_17_bjkqdmto_.arc
Backup Set           3880   18-MAR-15
  Backup Piece       3882   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663128_bjkpytxr_.bkp
Backup Set           3925   18-MAR-15
  Backup Piece       3928   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100613_bjkqdppq_.bkp

Do you really want to delete the above objects (enter YES or NO)? yes
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_16_bjkpynv3_.arc recid=24 stamp=874663125
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T095727_bjkpw9xz_.bkp recid=8 stamp=874663049
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T095845_bjkpyqdc_.bkp recid=9 stamp=874663127
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_17_bjkqdmto_.arc recid=25 stamp=874663571
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663128_bjkpytxr_.bkp recid=10 stamp=874663130
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100613_bjkqdppq_.bkp recid=11 stamp=874663574
Deleted 6 objects



RMAN> run
2> {
3> execute script global_full_backup;
4> }

executing global script: global_full_backup


Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=18 recid=26 stamp=874663651
input archive log thread=1 sequence=19 recid=27 stamp=874664008
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T101329_bjkqtbtn_.bkp tag=TAG20150318T101329 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backupset
channel ORA_DISK_1: specifying datafile(s) in backupset
input datafile fno=00001 name=/u01/app/oracle/oradata/jy/system01.dbf
input datafile fno=00002 name=/u01/app/oracle/oradata/jy/undotbs01.dbf
input datafile fno=00003 name=/u01/app/oracle/oradata/jy/sysaux01.dbf
input datafile fno=00005 name=/u01/app/oracle/oradata/jy/example01.dbf
input datafile fno=00004 name=/u01/app/oracle/oradata/jy/users01.dbf
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T101331_bjkqtdoh_.bkp tag=TAG20150318T101331 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:05
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=20 recid=28 stamp=874664078
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T101439_bjkqwjl4_.bkp tag=TAG20150318T101439 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting Control File and SPFILE Autobackup at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874664082_bjkqwmhg_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 18-MAR-15

RMAN retention policy will be applied to the command
RMAN retention policy is set to redundancy 1
using channel ORA_DISK_1
Deleting the following obsolete backups and copies:
Type                 Key    Completion Time    Filename/Handle
-------------------- ------ ------------------ --------------------
Archive Log          3944   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_18_bjkqh3ov_.arc
Backup Set           3945   18-MAR-15
  Backup Piece       3948   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T100615_bjkqds89_.bkp
Backup Set           3967   18-MAR-15
  Backup Piece       3973   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100732_bjkqh6cv_.bkp
Archive Log          4036   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_19_bjkqt8p5_.arc
Backup Set           3988   18-MAR-15
  Backup Piece       3990   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663655_bjkqh9db_.bkp
Backup Set           4053   18-MAR-15
  Backup Piece       4056   18-MAR-15          /u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T101329_bjkqtbtn_.bkp

Do you really want to delete the above objects (enter YES or NO)? YES
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_18_bjkqh3ov_.arc recid=26 stamp=874663651
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_nnndf_TAG20150318T100615_bjkqds89_.bkp recid=12 stamp=874663576
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T100732_bjkqh6cv_.bkp recid=13 stamp=874663654
deleted archive log
archive log filename=/u01/app/oracle/flash_recovery_area/JY/archivelog/2015_03_18/o1_mf_1_19_bjkqt8p5_.arc recid=27 stamp=874664008
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/autobackup/2015_03_18/o1_mf_s_874663655_bjkqh9db_.bkp recid=14 stamp=874663657
deleted backup piece
backup piece handle=/u01/app/oracle/flash_recovery_area/JY/backupset/2015_03_18/o1_mf_annnn_TAG20150318T101329_bjkqtbtn_.bkp recid=15 stamp=874664010
Deleted 6 objects

在执行全局脚本只影响RMAN所连接到的目标数据库;为了跨多个数据库执行全局脚本,必须单独连接到每一个数据库并执行全局存储脚本。

在执行脚本时将会使用配置的自动通道来执行脚本。如果需要覆盖配置的自动通道可以在脚本中使用allocate channel命令来手动分配通道。注意因为使用的是RUN块,如果脚本中的一个RMAN命令失败,脚本中的后续命令将不会执行。

查看存储脚本内容
print script命令用来查看存储脚本或将脚本内容写入到新文件。使用RMAN连接到目标数据库和恢复目录。执行print script:

RMAN> print script full_backup;

printing stored script: full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

显示全局脚本内容:
RMAN> print global script global_full_backup;

printing stored global script: global_full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

为将脚本内容写入到新文件中,执行以下命令:

RMAN> print script full_backup to file '/u01/app/oracle/full_backup.txt';

script full_backup written to file /u01/app/oracle/full_backup.txt

将全局脚本内容写入文件:

RMAN> print global script global_full_backup to file '/u01/app/oracle/global_full_backup.txt';

global script global_full_backup written to file /u01/app/oracle/global_full_backup.txt

查看恢复目录中的存储脚本:list script names
使用list script names命令来显示恢复目录中的存储脚本名。这个命令会显示所有存储脚本名。

RMAN> list script names;

List of Stored Scripts in Recovery Catalog


    Scripts of Target Database JY

       Script Name
       Description
       -----------------------------------------------------------------------
       full_backup


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup
       use only with archivelog mode database

只显示全局存储脚本名:

RMAN> list global script names;

List of Stored Scripts in Recovery Catalog


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup
       use only with archivelog mode database

为了查看当前恢复目录中所有存储脚本的名称,包括在恢复目录中所有已经注册的目标数据库的全局和局部脚本,执行以下命令:

RMAN> list all script names;

List of Stored Scripts in Recovery Catalog


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup
       use only with archivelog mode database


    Scripts of Target Database JY

       Script Name
       Description
       -----------------------------------------------------------------------
       full_backup


    Scripts of Target Database TEST

       Script Name
       Description
       -----------------------------------------------------------------------
       test_full_backup

可以看到上面显示了两个目标数据库(jy,test)中的所有存储脚本

更新存储脚本
为了更新存储脚本,连接到目标数据库和恢复目录并使用replace script命令来执行更新。如果脚本不存在,那么RMAN将会创建一个新存储脚本。

下面的命令用来更新脚本full_backup的内容:
先来查看恢复目录中所有存储脚本名来检查是否存在full_backup脚本

RMAN> list all script names;

List of Stored Scripts in Recovery Catalog


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup
       use only with archivelog mode database


    Scripts of Target Database JY

       Script Name
       Description
       -----------------------------------------------------------------------
       full_backup


    Scripts of Target Database TEST

       Script Name
       Description
       -----------------------------------------------------------------------
       test_full_backup

查看脚本full_backup的内容

RMAN> print script full_backup;

printing stored script: full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

更新脚本full_backup
RMAN> replace script full_backup
2> {
3> backup as backupset database plus archivelog;
4> }

replaced script full_backup

检查脚本full_backup是否已经被更新

RMAN> print script full_backup;

printing stored script: full_backup
 {backup as backupset database plus archivelog;
}

从脚本内容可知full_backup内容确实已经被更新.

下面来更新一个不存在的脚本full_backup_bak

RMAN> replace script full_backup_update
2> {
3> backup as backupset database plus archivelog;
4> }

replaced script full_backup_update

从下面的信息中可以看到当更新一个不存在的脚本,RMAN确实会创建这个脚本

RMAN> list all script names;

List of Stored Scripts in Recovery Catalog


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup
       use only with archivelog mode database


    Scripts of Target Database JY

       Script Name
       Description
       -----------------------------------------------------------------------
       full_backup

       full_backup_update


    Scripts of Target Database TEST

       Script Name
       Description
       -----------------------------------------------------------------------
       test_full_backup



RMAN> print script full_backup_update;

printing stored script: full_backup_update
 {backup as backupset database plus archivelog;
}

全局存储脚本可以使用replace global script命令来进行更新:
查看更新前global_full_backup脚本的内容

RMAN> print script global_full_backup;

printing stored global script: global_full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

更新全局脚本global_full_backup

RMAN> replace global script global_full_backup
2> {
3> backup as backupset database plus archivelog;
4> }

replaced global script global_full_backup

显示更新后全局脚本global_full_backup的内容

RMAN> print global script global_full_backup;

printing stored global script: global_full_backup
 {backup as backupset database plus archivelog;
}

与create script命令一样,可以使用文件来更新局部或全局存储脚本。

更新前查看全局脚本global_full_backup的内容

RMAN> print global script global_full_backup;

printing stored global script: global_full_backup
 {backup as backupset database plus archivelog;
}

使用global_full_backup.txt文件来更新全局脚本global_full_backup

RMAN> replace global script global_full_backup from file '/u01/app/oracle/global_full_backup.txt';

script commands will be loaded from file /u01/app/oracle/global_full_backup.txt
replaced global script global_full_backup

查看更新后的脚本内容:

RMAN> print global script global_full_backup;

printing stored global script: global_full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

更新前查看局部脚本full_backup的内容

RMAN> print script full_backup;

printing stored script: full_backup
 {backup as backupset database plus archivelog;
}

使用full_backup.txt文件来更新局部脚本full_backup

RMAN> replace script full_backup from file '/u01/app/oracle/full_backup.txt';

script commands will be loaded from file /u01/app/oracle/full_backup.txt
replaced script full_backup

查看更新后的脚本内容:

RMAN> print script full_backup;

printing stored script: full_backup
 {backup as backupset database plus archivelog;
delete obsolete;
}

删除存储脚本
为了从恢复目录中删除存储脚本,连接恢复目录和目标数据库后执行delete script命令:

RMAN> list script names;

List of Stored Scripts in Recovery Catalog


    Scripts of Target Database JY

       Script Name
       Description
       -----------------------------------------------------------------------
       full_backup

       full_backup_update


    Global Scripts


       Script Name
       Description
       -----------------------------------------------------------------------
       global_full_backup

为了从恢复目录中删除局部存储脚本full_backup

RMAN> delete script 'full_backup';

deleted script: full_backup

为了删除全局存储脚本global_full_backup,执行delete global script:

RMAN> delete global script 'global_full_backup';

deleted global script: global_full_backup

如果没有给delete script命令指定global参数,并且目标数据库中没有指定名称的存储脚本存在,RMAN将使用指定名称来查找全局存储脚本如果找到就将其删除。因此为了删除全局存储脚本也可以执行以下的命令:

RMAN> delete script 'global_full_backup';

deleted script: global_full_backup

对于delete script ‘global_full_backup’命令,RMAN会在连接到的目标数据库中查找脚本’global_full_backup’,如果没有找到,将会搜索名为’global_full_backup’的全局脚本,如果找到就将其删除。

启动RMAN客户端并执行存储脚本
为了运行RMAN客户端并执行存储在恢复目录中的存储脚本,可以使用script参数来启动RMAN客户端:

[oracle@oracle11g ~]$ rman target sys/zzh_2046@test catalog rman/rman@jy script 'global_full_backup'

Recovery Manager: Release 10.2.0.5.0 - Production on Wed Mar 18 11:22:04 2015

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

connected to target database: TEST (DBID=2168949517)
connected to recovery catalog database

executing global script: global_full_backup


Starting backup at 18-MAR-15
current log archived
allocated channel: ORA_DISK_1
channel ORA_DISK_1: sid=141 devtype=DISK
skipping archive log file /u02/1_49_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_50_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_51_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_52_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_53_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_54_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_55_870806981.dbf; already backed up 1 time(s)
skipping archive log file /u02/1_56_870806981.dbf; already backed up 1 time(s)
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=57 recid=64 stamp=874668125
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u02/ora_test874668127_671 tag=TAG20150318T112206 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
using channel ORA_DISK_1
channel ORA_DISK_1: starting full datafile backupset
channel ORA_DISK_1: specifying datafile(s) in backupset
input datafile fno=00001 name=/u01/app/oracle/oradata/test/system01.dbf
input datafile fno=00003 name=/u01/app/oracle/oradata/test/sysaux01.dbf
input datafile fno=00005 name=/u01/app/oracle/oradata/test/example01.dbf
input datafile fno=00006 name=/u01/app/oracle/oradata/test/tspitr01.dbf
input datafile fno=00002 name=/u01/app/oracle/oradata/test/undotbs01.dbf
input datafile fno=00004 name=/u01/app/oracle/oradata/test/users01.dbf
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u02/ora_test874668130_681 tag=TAG20150318T112209 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:01:15
Finished backup at 18-MAR-15

Starting backup at 18-MAR-15
current log archived
using channel ORA_DISK_1
channel ORA_DISK_1: starting archive log backupset
channel ORA_DISK_1: specifying archive log(s) in backup set
input archive log thread=1 sequence=58 recid=65 stamp=874668205
channel ORA_DISK_1: starting piece 1 at 18-MAR-15
channel ORA_DISK_1: finished piece 1 at 18-MAR-15
piece handle=/u02/ora_test874668206_691 tag=TAG20150318T112326 comment=NONE
channel ORA_DISK_1: backup set complete, elapsed time: 00:00:02
Finished backup at 18-MAR-15

Starting Control File and SPFILE Autobackup at 18-MAR-15
piece handle=/u01/app/oracle/flash_recovery_area/TEST/autobackup/2015_03_18/o1_mf_s_874668209_bjkvxl3b_.bkp comment=NONE
Finished Control File and SPFILE Autobackup at 18-MAR-15

Recovery Manager complete.

在启动RMAN客户端时必须要连接到恢复目录(存储脚本存储在恢复目录中)和目标数据库。

存储脚本命名限制
RMAN是如何解决脚本名称,尤其是在局部和全局脚本共享相同名称时存在的一些问题:
.RMAN允许但一般不要求将存储脚本名使用引号括起来。然而,存储脚本名以数字或RMAN的保留关键开头就需要将存储脚本名称使用引号括起来。对于存储脚本名称要考虑避免使用A-Z之外的字符或RMAN的保留关键字作为名称的开始字符。
.当在命令行中使用script参数来启动RMAN客户端,如果存在相同名称的局部和全局存储脚本,那么RMAN将总是执行局部脚本。
.对于execute script,delete script和print script命令,如果脚本名称作为一个参数被传递并且在连接到的目标数据库中没有该相同名称的脚本存在,RMAN将执行,删除或显示相同名称的全局脚本。例如,如果在恢复目录中存在一个名叫global_full_backup的全局存储脚本,但在目标数据库中没有名叫global_full_backup的局部存储脚本,下面的命令将会删除全局脚本:
DELETE SCRIPT global_full_backup;

Proudly powered by WordPress | Indrajeet by Sus Hill.