每一个Oracle的初学者在入门阶段都会接触到SGA/PGA的知识,如果是从10g开始学习那么会多或少会对ASMM有所了解,从使用的角度来说ASMM的出现极大地简化了Oracle内存初始化参数的设置,在ASMM的使用上高级DBA和初学者不会有太大的差别;很多人因此而认为ASMM极大程度地减少了数据库对于专业DBA的依赖:如果我们有一个足够智能的DB,那么为什么还要花费金钱雇佣DBA呢?这似乎是时下一种十分流行的想法。当然这种想法我个人是不能苟同的,ASMM一定程度上带来了便利,更大程度上它是一个黑盒,黑盒里面藏了很多秘密,这些秘密带来比手动管理更多的不确定性;在10g release 1和10.2的早期版本中ASMM扮演的角色有点像一个闯祸精,另一个让用户对ASMM很不待见的原因是ASMM直接拖慢了startup的速度。一个个人观点是ASMM也好AMM也罢,都要求产品数据库DBA掌握更多SGA/PGA相关的知识才能成功”驾驭”这些”有智力”的家伙,有点夸张的说这个时候的DBA很像一个chemist(需要和一大堆以1个或2个下划线开头的奇怪参数打交道)。
为了不辱使命我们真的有必要了解一下ASMM的基本知识,显然这并不是一件容易的事情……
Oracle的SGA基本内存组件从9i开始并没有非常大的变化,他们包括:
Buffer Cache 我们常说的数据库高速缓存,虽然我一直不明白要冠以高速之名
Default Pool 默认的缓冲池,大小由DB_CACHE_SIZE决定
Keep Pool 持久的缓冲池,大小由DB_KEEP_CACHE_SIZE决定
Non standard pool 非标准块标准池,大小由DB_nK_cache_size决定
Recycle pool 回收池,大小由db_recycle_cache_size决定
Shared Pool 共享池,大小由shared_pool_size决定
Library cache 俗称的库缓存
Row cache 行缓存,也叫字典缓存
Java Pool java池,大小由Java_pool_size决定
Large Pool 大池,大小由Large_pool_size决定
Fixed SGA 固定的SGA区域,包含了Oracle内部的数据结构,一般被存放在第一个granule中
在9i中尚未引入ASMM,唯一的选择是手动管理的SGA,有时候也叫做MSMM。在9i中除去buffer cache的大小可以手动修改外,其余组件都无法动态修改。因为缺乏一种动态管理的机制,所以在9i中如果有某个内存区域有急用,也无法从其他有空闲内存的组件中捐献一些来解燃眉之急。
手动管理SGA的缺点在于:
个别组件如shared pool、default buffer pool的大小存在最优值,但组件之间无法交换内存
在9i中就提供了多种内存建议(advisor),但都要求人工手动干预
无法适应工作负载存在变化的环境
往往会导致内存浪费,没有用到实处
若设置不当,引发著名的ORA-04031错误的可能性大大提高
ASMM的优势在于:
全自动的共享内存管理
无需再配置每一个内存组件大小参数
仅使用一个参数sga_target驱动
有效利用所有可用的内存,极大程度上减少内存浪费
对比MSMM其内存管理模式:
更加动态
更加灵活
并具备适应性
易于使用
一定程度上增强了性能,因为内存分配更为合理了
当某个组件急需更多内存时可以有效提供,因此可以一定程度避免ORA-04031的发生
ASMM主要可以囊括为三个部分:
1.由一个新参数sga_target驱动的管理模式2.内存交换的模型,包括了:内存组件(memory component),内存代理(memory broker)和内存交换机制(memory mechanism)3.内存建议(memory advisor)ASMM下一部分参数是自动设置的(Automatically set),这些参数包括:shared_pool_size、db_cache_size、java_pool_size、large_pool _size和streams_pool_size;而另外一些是需要手动设置的静态参数,包括了:db_keep_cache_size、db_recycle_cache_size、db_nk_cache_size、log_buffer以及固定SGA内存结构等,如果以上没有手动设置这些参数的话,除去log_buffer和fixed sga其他内存组件一般默认大小为零。
通过ASMM用户仅需要设置一个sga_target参数,其他参数都将由ASMM自行内部控制。但如果用户依旧设置了如db_cache_size、java_pool_size等参数,那么这些参数被认为是相关内存组件的最小限制,同时每个内存组件的大小也存在一个最大上限(内部的)。在实例启动阶段,Oracle会将必要的内存颗粒(granule,当SGA<1GB时granule大小为4M,当SGA>1GB时granule大小为16M)分配给内存组件,多余没有分配出去的全都分配给buffer cache,之后随着系统的不断活跃更多的内存颗粒(granule)将被分配给急需内存的组件。我们可以动态地修改sga_target参数,前提是所在的系统平台支持动态地共享内存(dism,主流平台都支持)。使用ASMM的一个必要条件是初始化参数statistics_level必须设置为typical或ALL,如果设置为BASIC那么MMON后台进程(Memory Monitor is a background process that gathers memory statistics (snapshots) stores this information in the AWR (automatic workload repository). MMON is also responsible for issuing alerts for metrics that exceed their thresholds)将无法有效分析内存的使用的统计信息从而驱动ASMM的自动调优,实际上我们不能同时设置sga_target为非零值和statistics_level为BASIC:
SQL> show parameter sgaNAME TYPE VALUE------------------------------------ ----------- ------------------------------lock_sga boolean FALSEpre_page_sga boolean FALSEsga_max_size big integer 2000Msga_target big integer 2000MSQL> show parameter sga_targetNAME TYPE VALUE------------------------------------ ----------- ------------------------------sga_target big integer 2000MSQL> alter system set statistics_level=BASIC;alter system set statistics_level=BASIC*ERROR at line 1:ORA-02097: parameter cannot be modified because specified value is invalidORA-00830: cannot set statistics_level to BASIC with auto-tune SGA enabled
如果使用了server parameter file即spfile的话,ASMM会在实例shutdown之前将当前实际的内存组件大小(Oracle认为这是最优的,但实际上可能不是)保存到spfile中,如果你使用strings命令打印过spfile的内容的可以发现一些以双下划线开头的参数,如:
G10R2.__db_cache_size=973078528G10R2.__java_pool_size=16777216G10R2.__large_pool_size=16777216G10R2.__shared_pool_size=1006632960G10R2.__streams_pool_size=67108864
这些在spfile保存的组件大小会在下次启动时被沿用,以达到将已经实践得出的”最佳值”记住的目的,这样下次就不用从头再”学习”了。
在ASMM的内存交换模型中存在三类组件
可调优组件(tunable):可调优组件是那些大小可以随之变化且若过小仅损害少量性能。buffer cache就是一个经典的例子,cache过小的情况下应用程序仍能正常运行,但带来的代价是更多的 IO。注意可调优组件同时存在一个下限。举例来说共享池由库缓存Library cache和其他一些 subheap子堆组成,那么共享池就存在一个下限要保证至少能放下某个一个时刻并行打开的游标(open cursors)以及负担其他共享池client的运行时内存分配需求。类似的buffer cache也存在一个下限,该下限至少要大于并行被pin住的buffer的总和(虽然这部分很小)
不可调组件(Un-tunable):不可调组件是那些存在最小下限的内存组件,这个最小下限足够让应用程序正常运行,超过这个上限并不会带来额外的性能收益。在这类组件中large pool是一个典型。
固定大小组件(Fixed Size):自动调优框架之外的组件,一般为固定大小。这些组件的大小仅在手动调整时改变。例如非标准块大小的高速缓冲池
内存交换模型中内存大小调整的申请(memory resize request)存在三种不同的模式,它们分别是:
立即内存申请(Immediate Request):这种申请模式一般出现在ASMM管理的某个自动调优组件在无法分配到连续可用内存块(chunk)时,为了避免出现OUT-OF-MEMORY(ORA-04031)的错误,系统尝试从其他候选组件中转移一个内存颗粒(granule)过来。需要注意的是当前可能没有可用的全空granule,这时就会出现不完整的转移,在此情形下系统会开始从候选组件中清理内存颗粒以满足内存申请,并将一个granule不完整地转移给急需内存的组件。
延迟内存申请(Deferred Request):这种申请一般出现在系统认为存在一种更为合理的内存分配时,考虑在组件之间转移一个或多个granule。用以判定最佳内存分配的依据是MMON进程所提供的统计信息delta.
手动内存申请(Manual Request):这种申请仅发生在用户使用alter system命令手动调整内存组件的大小时。在这种模式下仅允许使用空的内存颗粒参与大小调整。如果当时没有足够的空granule,那么针对组件grow操作会返回ORA-4033错误,而针对组件shrink操作会返回ORA-4034错误。
当ASMM被启用后,内存代理(Memory Broker)定期执行上图所示的活动。以上操作都处于延迟内存申请模式下(Deferred)。其目的是通过自动化的作业来调优自动调优组件(auto-tunable component)的大小以适应不断改变的工作负载,将内存分配到最需要它们的地方。MMON辅助进程会在后台不断将统计信息和内存建议定期地捕获到循环使用的内存中,并计算不同时期缓存信息之间的差值;MMON还会基于Memory Broker的策略分析这些差值并估算出长期和短期内的趋势。最后MMON基于以上分析生成一些内存组件的大小调整决议并将相应的申请发送到一个系统申请队列中(resize request system queue)。MMAN后台进程(Memory Manager is a background process that manages the dynamic resizing of SGA memory areas as the workload increases or decreases)会定期扫描系统申请队列并执行内存转移。
在10gR1中Shared Pool的shrink收缩操作存在一些缺陷,造成缺陷的原因是在该版本中Buffer Cache还没有能力共享使用一个granule,这是因为Buffer Cache的granule的尾部由granule header和Metadata(可能是buffer header或者RAC中的Lock Elements)拼接组成,在其尾部不容许存在空洞。另一个原因是当时的shared pool允许不同生命周期duration(以后会介绍)的chunk存放在同一个granule中,这造成共享池无法完全释放granule。到10gR2中通过对Buffer Cache Granule结构的修改允许在granule header和buffer及Metadata(buffer header或LE)存在缝隙,同时shared pool中不同duration的chunk将不在共享同一个granule,通过以上改进buffer cache与shared pool间的内存交换变得可行。此外在10gr2中streams pool也开始支持内存交换(实际根据不同的streams pool duration存在限制)
当某个组件扮演捐献者(Donor,下面的trace中会看到)角色时,它可能将一个不完整granule转移给buffer cache,那么在该granule被使用前需要完成以下步骤:
首先在该granule的尾部生成新的granule header
针对剩下的chunk判定在该granule中是否还有未使用的buffer header
如果有,那么将chunk中的内存转换为一个数据块。否则将之转换为一个metadata块
重复以2-4步骤,直到该granule被转换完
接着我们来了解一下内存转移的基本原理,当将buffer cache中的granule转移给shared pool时,将按照以下步骤:
MMAN后台进程找到一块属于buffer cache的合适granule
MMAN将看中的granule置入quiesce列表中(Moving 1 granule from inuse to quiesce list of DEFAULT buffer cache for an immediate req)
DBWR将负责写出置入quiesced列表中granule里面的脏buffer(dirty buffer)
MMAN将为shared pool调用消费回调函数(consume callback),granule中free的chunk都会被shared pool消费(consume)掉,并对共享池新的内存分配可用。从这里开始该granule变成一个shared granule共享内存颗粒,注意不要认为这个时候该granule的空间全部属于共享池了,实际上有部分pin住的buffer及其Metadata(上述的buffer header和LE)的空间仍被buffer cache占用着
最终该granule将完整的转移给shared pool,这时此granule不再是一个shared共享的
实际使用中ASMM受到众多隐藏参数的影响,其中比较主要的参数有:
_enabled_shared_pool_duration:该参数控制是否启用10g中特有的shared pool duration特性,当我们设置sga_target为0时该参数为false;同时在10.2.0.5前若cursor_space_for_time设置为true时该参数也为false,不过在10.2.0.5以后cursor_space_for_time参数被废弃
_memory_broker_shrink_heaps:若该参数设置为0那么Oracle不会去收缩shared pool或java pool,当该参数大于0,会在shrink request失败后等待该参数指定秒数时间后再次申请
_memory_management_tracing: 该参数控制针对MMON和MMAN后台进程包括内存建议(advisor)和内存代理(Memory Broker)决议的相关trace的级别;针对ORA-04031的诊断可以设置为36,设置为8启用针对启动期间组件大小的trace,设置为23启动针对Memory Broker decision的跟踪,设置为32将转储cache resize的细节;该参数的具体级别如下:
Level | Contents |
0x01 | Enables statistics tracing |
0x02 | Enables policy tracing |
0x04 | Enables transfer of granules tracing |
0x08 | Enables startup tracing |
0x10 | Enables tuning tracing |
0x20 | Enables cache tracing |
接下来我们通过设置_memory_management_tracing隐藏参数和DUMP_TRANSFER_OPS转储来实地了解一次完整的内存转移,和不完整的内存转移。以下演示的完整trace文件可以从这里下载、。
SQL> alter system set "_memory_management_tracing"=63;System alteredOperation make shared pool grow and buffer cache shrink!!!..............
以下为一个完整granule转移的过程,包括了对default buffer pool的resize操作:
AUTO SGA: Request 0xdc9c2628 after pre-processing, ret=0/* 这里的0xdc9c2628是前台进程的addr */AUTO SGA: IMMEDIATE, FG request 0xdc9c2628/* 这里可以看到前台进程的Immediate立即申请 */AUTO SGA: Receiver of memory is shared pool, size=16, state=3, flg=0/* 此次申请的收益人是shared pool,共享池,其大小为16个granule,处于grow状态 */AUTO SGA: Donor of memory is DEFAULT buffer cache, size=106, state=4, flg=0/* 此处的捐献者是Default buffer cache,高速缓存,其大小为106个granule,处于shrink状态 */AUTO SGA: Memory requested=3896, remaining=3896/* 这里immeidate request所要求的空间是3896 bytes */AUTO SGA: Memory received=0, minreq=3896, gransz=16777216/* 这里没有free的granule,所以received为0,gransz为granule的大小 */AUTO SGA: Request 0xdc9c2628 status is INACTIVE/* 因为没有空的内存颗粒,先将申请置于inactive状态 */AUTO SGA: Init bef rsz for request 0xdc9c2628/* 为相关申请初始化before-process大小调整 */AUTO SGA: Set rq dc9c2628 status to PENDING/* 将request置于pending状态 */AUTO SGA: 0xca000000 rem=3896, rcvd=16777216, 105, 16777216, 17/* 返回起始地址为0xca000000的16M大小granule */AUTO SGA: Returning 4 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 4, 1, aAUTO SGA: Resize done for pool DEFAULT, 8192/* 完成对default pool的resize */AUTO SGA: Init aft rsz for request 0xdc9c2628AUTO SGA: Request 0xdc9c2628 after processingAUTO SGA: IMMEDIATE, FG request 0x7fff917964a0AUTO SGA: Receiver of memory is shared pool, size=17, state=0, flg=0AUTO SGA: Donor of memory is DEFAULT buffer cache, size=105, state=0, flg=0AUTO SGA: Memory requested=3896, remaining=0AUTO SGA: Memory received=16777216, minreq=3896, gransz=16777216AUTO SGA: Request 0x7fff917964a0 status is COMPLETE/* shared pool成功收到16M的granule */AUTO SGA: activated granule 0xca000000 of shared pool
以下为一个partial granule不完全内存颗粒的转移过程trace:
AUTO SGA: Request 0xdc9c2628 after pre-processing, ret=0AUTO SGA: IMMEDIATE, FG request 0xdc9c2628AUTO SGA: Receiver of memory is shared pool, size=82, state=3, flg=1AUTO SGA: Donor of memory is DEFAULT buffer cache, size=36, state=4, flg=1/* 此处的受益者仍为shared pool,而捐献者是default buffer cache */AUTO SGA: Memory requested=4120, remaining=4120AUTO SGA: Memory received=0, minreq=4120, gransz=16777216AUTO SGA: Request 0xdc9c2628 status is INACTIVEAUTO SGA: Init bef rsz for request 0xdc9c2628AUTO SGA: Set rq dc9c2628 status to PENDINGAUTO SGA: Moving granule 0x93000000 of DEFAULT buffer cache to activate listAUTO SGA: Moving 1 granule 0x8c000000 from inuse to quiesce list of DEFAULT buffer cache for an immediate req/* 以上将buffer cache中起始地址为0x8c000000的granule从使用中列表inuse list, 移动到静默列表quiesce list中 */AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: activated granule 0x93000000 of DEFAULT buffer cacheAUTO SGA: NOT_FREE for imm req for gran 0x8c000000/ * 等待dbwr写出0x8c000000 granule中所有的dirty buffer */AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: NOT_FREE for imm req for gran 0x8c000000AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: NOT_FREE for imm req for gran 0x8c000000AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: NOT_FREE for imm req for gran 0x8c000000AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: NOT_FREE for imm req for gran 0x8c000000AUTO SGA: Returning 0 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 0, 1, 20aAUTO SGA: NOT_FREE for imm req for gran 0x8c000000.........................................AUTO SGA: Rcv shared pool consuming 8192 from 0x8c000000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 90112 from 0x8c002000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 24576 from 0x8c01a000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 65536 from 0x8c022000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 131072 from 0x8c034000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 286720 from 0x8c056000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 98304 from 0x8c09e000 in granule 0x8c000000; owner is DEFAULT buffer cacheAUTO SGA: Rcv shared pool consuming 106496 from 0x8c0b8000 in granule 0x8c000000; owner is DEFAULT buffer cache...................../* 以上shared pool开始消费0x8c000000 granule中的chunk, 但此granule的owner暂时仍为default buffer cache */AUTO SGA: Imm xfer 0x8c000000 from quiesce list of DEFAULT buffer cache to partial inuse list of shared pool/* 以上将0x8c000000 granule从default buffer cache的静默列表转移到shared pool的不完整inuse list */AUTO SGA: Returning 4 from kmgs_process for request dc9c2628AUTO SGA: Process req dc9c2628 ret 4, 1, 20aAUTO SGA: Init aft rsz for request 0xdc9c2628AUTO SGA: Request 0xdc9c2628 after processingAUTO SGA: IMMEDIATE, FG request 0x7fffe9bcd0e0AUTO SGA: Receiver of memory is shared pool, size=83, state=0, flg=1AUTO SGA: Donor of memory is DEFAULT buffer cache, size=35, state=0, flg=1AUTO SGA: Memory requested=4120, remaining=0AUTO SGA: Memory received=14934016, minreq=4120, gransz=16777216AUTO SGA: Request 0x7fffe9bcd0e0 status is COMPLETE/* 以上一个partial transfer完成 */
对应于以上partial transfer我们可以通过DUMP_TRANSFER_OPS来了解该0x8c000000 partial granule的实际使用情况,如:
SQL> oradebug setmypid;Statement processed.SQL> oradebug dump DUMP_TRANSFER_OPS 1;Statement processed.SQL> oradebug tracefile_name;/s01/admin/G10R2/udump/g10r2_ora_21482.trc=======================trace content==============================GRANULE SIZE is 16777216COMPONENT NAME : shared poolNumber of granules in partially inuse list (listid 4) is 23Granule addr is 0x8c000000 Granule owner is DEFAULT buffer cache/* 该0x8c000000 granule在shared pool的partially inuse list, 但这里它的owner仍为default buffer cache */Granule 0x8c000000 dump from owner perspectivegptr = 0x8c000000, num buf hdrs = 1989, num buffers = 156, ghdr = 0x8cffe000/ * 可以看到该granule的granule header地址位于0x8cffe000, 其中共有156个buffer block,1989个buffer header *//* 以下granule中具体的内容,实际既包含了buffer cache也有shared pool chunk */BH:0x8cf76018 BA:(nil) st:11 flg:20000BH:0x8cf76128 BA:(nil) st:11 flg:20000BH:0x8cf76238 BA:(nil) st:11 flg:20000BH:0x8cf76348 BA:(nil) st:11 flg:20000BH:0x8cf76458 BA:(nil) st:11 flg:20000BH:0x8cf76568 BA:(nil) st:11 flg:20000BH:0x8cf76678 BA:(nil) st:11 flg:20000BH:0x8cf76788 BA:(nil) st:11 flg:20000BH:0x8cf76898 BA:(nil) st:11 flg:20000BH:0x8cf769a8 BA:(nil) st:11 flg:20000BH:0x8cf76ab8 BA:(nil) st:11 flg:20000BH:0x8cf76bc8 BA:(nil) st:11 flg:20000BH:0x8cf76cd8 BA:0x8c018000 st:1 flg:622202...............Address 0x8cf30000 to 0x8cf74000 not in cacheAddress 0x8cf74000 to 0x8d000000 in cacheGranule 0x8c000000 dump from receivers perspectiveDumping layoutAddress 0x8c000000 to 0x8c018000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c018000 to 0x8c01a000 not in this poolAddress 0x8c01a000 to 0x8c020000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c020000 to 0x8c022000 not in this poolAddress 0x8c022000 to 0x8c032000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c032000 to 0x8c034000 not in this poolAddress 0x8c034000 to 0x8c054000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c054000 to 0x8c056000 not in this poolAddress 0x8c056000 to 0x8c09c000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c09c000 to 0x8c09e000 not in this poolAddress 0x8c09e000 to 0x8c0b6000 in sga heap(1,3) (idx=1, dur=4)Address 0x8c0b6000 to 0x8c0b8000 not in this poolAddress 0x8c0b8000 to 0x8c0d2000 in sga heap(1,3) (idx=1, dur=4)
以上可以看到该granule真的是一个shared granule共享内存颗粒,其中不仅包含了部分buffer block,还包含了1号shared subpool共享池子池的durtaion为4的chunk,duration=4即execution duration;这类duration的chunk一般有着较短的生命周期,当其extent被置于quiesce list静默列表时将很有可能变得足够free。execution duration是共享池中唯一能可靠转移的,因此唯有该类duration所在的extent(一般来说一个extent占用一个granule)可以用来收缩。
以下我们列出一些有助于诊断ASMM问题的动态性能视图,仅供参考:
V$SGAINFO
Displays summary information about the system global area (SGA).V$SGA
Displays size information about the SGA, including the sizes of different SGA components, the granule size, and free memory.V$SGASTAT
Displays detailed information about the SGA.V$SGA_DYNAMIC_COMPONENTS
Displays information about the dynamic SGA components. This view summarizes information based on all completed SGA resize operations since instance startup.V$SGA_DYNAMIC_FREE_MEMORY
Displays information about the amount of SGA memory available for future dynamic SGA resize operations.V$SGA_RESIZE_OPS
Displays information about the last 400 completed SGA resize operations.V$SGA_CURRENT_RESIZE_OPS
Displays information about SGA resize operations that are currently in progress. A resize operation is an enlargement or reduction of a dynamic SGA component.V$SGA_TARGET_ADVICE
Displays information that helps you tune SGA_TARGET