MSMM
10G前SGA的管理是通过手工设置一系列的参数来实现的,例如重要的参数有以下几个:
● buffer_cache_size
● shared_pool_size
● large_pool_size
● stream_pool_size
● log_buffer
SGA是一大块内存的统称,由上面列出的组件组成,10G之前的版本,DBA需要对SGA的各个内存区域进行手工设置,这可能对一个DBA无形中要求变得非常的高,需要了解业务,了解热点数据量,了解SQL的使用方式,是否存在大量不同文本的SQL等等来决策来给每个区域分配多少内存。使用过这个版本的DBA基本都遭遇过一个经典的报错:ORA-04031,一般是由于shared pool内存不够导致,这个不够的原因可能有很多,共享池内存的严重碎片化,大量的硬解析,巨大的PL/SQL文本等等都可能会导致这个问题。下面我们来看下,手工管理SGA情况下,如何考虑为buffer cache和shared pool这两个最重要的内存组件分配内存。
Buffer Cache
Buffer Cache俗称数据块高速缓存,是用来缓存数据块的拷贝的,Oracle通过改进后的LRU算法以期待这些数据块后续被高效的重用。服务器进程从磁盘读入的数据就放在这块内存中,修改数据时,也是在这个区域对数据进行修改。并没有一个给buffer cache分配内存的黄金法则,还是要根据操作系统的内存大小,业务的热点数据量、业务的类型等因素来决定给buffer cache分配多大的内存大小。我们先看下如何决定整个SGA的大小,对于SGA的分配原则,官方建议如下:
上面的公式格式跟PGA的公式大同小异,刨去20%内存给操作系统之后,分为了不同的系统类型,基本上是OLTP系统,大部分的内存给buffer cache,DSS类的系统,buffer cache没那么重要,可以给到50%甚至更低。 至于SGA中多少可以分配给buffer_cache,要看实际情况。DBA可以通过查看buffer cache的命中率来粗略了解是否给buffer cache分配了足够的内存。但是熟悉我的朋友都知道,我是OWI方法论的践行者,对于命中率的崇拜在Oracle里已然不应该再存在,但是命中率作为一种性能诊断的辅助手段依然具有它的价值。如果系统的SQL都足够优化后,命中率不高,一定程度上意味着可能buffer cache给小了或者你的业务访问的数据非常的离散,热点数量太少。这里肉丝给出的优化思路是自顶向下,命中率低是一个表象,最上层的应用是不是调用了大量不需要调用的SQL,这些SQL是不是缺少索引,SQL都足够优化后,DBA是不是考虑加大buffer cache来提升性能,加大之后还是不行,是不是要考虑增加系统内存来进一步提高buffer cache的大小,最后是不是需要为数据库多加几个盘或者使用SSD来提高性能。采用自顶向下的分析方法有利于真正发现问题所在而且以最低的代价解决问题,如果仅仅是一个SQL由于缺少索引而导致整个数据库系统缓慢,DBA直接就去加盘扩容IO,那么最终会发现花费了这么大的代价,治标不治本,问题依然没有解决。上面给出的公式并不适用于所有情况,特别是OLTP系统,如果进程数非常的多,那么需要进一步降低SGA占用内存的比例,以预留出更多内存给PGA使用。
Shared Pool
Oracle占用量最大的两块内存除了buffer cache区就是Shared Pool的内存了,它的结构非常的复杂,而且由于要缓存SQL代码这种非标准大小的文本,经常会产生大量的碎片化内存,shared pool总体上包含了两大部分,一块是library cache区,用来缓存SQL、PL/SQL代码,保存它们的执行计划,提高SQL的解析效率,如果你的应用代码从来不使用绑定变量,那么这一块的内存对你来说是一个很大的负担,但是Oracle里是无法关闭library cache区的,因此对于OLTP系统,请确保SQL都使用了绑定变量。第二大块区域是row cache区,用来缓存数据库的数据字典,由于保存在里面的信息是以行的形式存在,因此叫row cache。对于这一块的内存,依据数据库中元信息(metadata)的多少而决定,如果数据库中有几十万的对象,那么这一块的内存就会占用比较大,同时表上的很多列都有直方图信息,也会导致这一区的内存占用比较大。在一个稳定的系统中,这一区域的内存基本上是静态的,Oracle中几乎没有操作会频繁修改row cache区。有个例外情况是没有cache属性的sequence,如果这种sequence调用频繁,就会触发频繁的修改sequence的属性值,进而可能会产生row cache lock的一些等待,优化的办法是为每一个sequence设置足够的cache值。 >如果应用程序没有使用绑定变量,而且难以修改,可以通过设置cursor_sharing为force来尝试解决问题。
这里肉丝再讲一个亲身经历的案例,老DBA嘛,可能技能已经不多,但故事还是有的????,曾经帮一位客户解决了一个free buffer waits的案例,这个等待事件的出现一般说明buffer cache过小,或者全表扫描、写入操作比较多、写脏数据比较慢,从AWR报告看,free buffer waits排在TOP EVENT的第一名,占用的DB TIME已达到了百分之七八十,而且从awr报告中发现客户的shared pool内存占用已经达到了接近50G,而分析数据库一个月之前的AWR报告,shared pool的内存只有3个G左右的大小,基本上可以认定为shared pool内存占用过大,而导致buffer cache不够用,进而出现了free buffer waits等待事件,经过跟客户沟通后知道,以前一直比较稳定,最近做过的大的变更是给数据库使用了oracle 的flahcache,通过MOS上搜索flashcache关键字最终发现,在11.2.0.4,Oracle RAC如果使用了flash cache,那么会在shared pool 中分配额外的内存存放GCS资源。多出的内存为:208 bytes * the total number of (flash) buffer blocks。因为这里提醒广大DBA朋友,如果你打算使用Oracle的FlashCache那么请为它预留出足够的shared pool内存。
ASMM
10G 版本Oracle推出了ASMM,自动SGA管理,它的出现一定程度上帮助DBA解决了管理SGA的问题,通过设置参数sga_target为非0值来启用ASMM。但是在10GR1,包括10GR2的早期版本,ASMM还不够成熟存在比较多的BUG,导致了比较多的问题,所以当时DBA在一些核心的生产环境,还是沿用了9I时候的手工SGA管理。自动SGA管理,不再需要为每个内存组件设定值,当然如果你设置sga_target的同时,设置了db_cache_size这些参数,那么db_cache_size这些参数值会作为最小值要求。
如果sga_target设置为0,memory_target也为0,则回归到传统的MSMM管理。通过使用ASMM可以很大程度上解决上面提到的ORA-04031的错误。在使用了ASMM后,去查看spfile文件,会发现多了一些双下划线打头的内存参数如__db_cache_size,这些内存参数是Oracle实例运行过程中动态产生固化到spfile中的,如果实例运行的时间足够长,这些参数的值被固话后,相当于有了一个基于自身环境的最佳实践参数,数据库实例重启后,会沿用固化在spfile中的这些参数值。 > 11G版本即使sga_target设置为0,memory_target也设置为0,也可能会发现SGA的pool之间Granule的移动,这是11G的新Linux特性,通过参数_memory_imm_mode_without_autosga来控制。 >
Granule
ASMM技术的实现,内部是通过Granule在内存组件之间移动来实现。这里对Granule做一点解释,Oracle 10G引入的自动共享内存管理,目的是为了解决下面的问题,“我应该把db_cache_size设置为多大?我应该把shared_pool_size设置多大?那么10G以后,Oracle给出的答案是,“你不用再考虑这个问题了,我通过算法帮你搞定“。简单来说,就是通过比较通过增加数据块缓存减少的磁盘IO时间与通过增加共享池优化而节省的时间,通过比对,来得出是否需要在两者间来移动内存。为了让内存在db_cache_size和shared_pool_size之间高效移动,Oracle在9I版本中重新构建了SGA,也就是使用固定大小的内存块-Granule,Granule会随着操作系统、Oracle版本以及SGA大小的不同而不同,读者可以通过如下的SQL语句来查看当前实例的Granule的大小:
AMM
有了PGA的自动管理,SGA的自动管理,Oracle 这个强迫症患者终于在11G这个版本推出了AMM,自动内存管理,由于使用了AMM后就不能使用大页,因此这个功能其实一直没被广泛使用,通过这个功能DBA只需要设置memory_target一个参数就可以完成整个数据库实例内存的配置,就像ASMM导致了一些双下划线隐含参数的产生,AMM同样导致了一些双下划线隐含参数的产生,例如:__pga_aggregate_target,__sga_target。而且在11.2.0.2版本之前,DBCA建库默认就是AMM管理(有些时候Oracle胆子真的是很大),11.2.0.3以后开始DBCA建库时会检测操作系统内存大小,大于4G内存默认为ASMM,小于4G内存默认为AMM。同样如果你设置AMM后,也设置了SGA_TARGET等参数,那么这些参数会作为最小值要求。
AMM最大的问题是不能使用大页内存。关于使用大页的重要性会在本文后面部分详细介绍。 >Doc 749851.1 > > Doc 1453227.1.
11G后由于出现了AMM,如果你做DBA时间足够长,你一定遇到过下面这个错误
这个错误给人莫名其妙的感觉,“MEMORY_TARGET特性不被支持”,其实不是特性不被支持,这是由于AMM使用的是操作系统的共享内存文件系统,位于/dev/shm下,如果配置的内存文件系统比较小,小于了memory_target的值就会报错,一般在Linux主流的操作系统上,这个内存共享文件系统的值是内存大小的一半,如果读者遭遇了这个问题,要不去调小memory_target的参数值,要不通过如下办法去修改这个共享内存文件系统的大小:
这里指出一点,11G ASM默认会使用AMM,官方强烈建议不要修改这个默认行为。ASM里要求MEMORY_TARGET的最小值为256M。如果设置了低于256M的值,Oracle会自动增加内存到256M。 对于ASM的发布版:11.2.0.3/11.2.0.4/12.1,Oracle强烈建议把memory_target设置为1536M,这个值被证明在绝大部分的环境里都是足够的。
使用了ASM后,数据库实例的shared_pool内存需要重新评估计算,如下公式是在正常shared pool的基础上需要额外增加的大小:
? 如果ASM磁盘组是external redundancy,需要在2MB基础上,每100G的空间,增加1MB。
? 如果ASM磁盘组是normal redundancy,需要在4MB基础上,每50G的空间,增加1MB。
? 如果ASM磁盘组是high redundancy,需要在6MB基础上,每33G的空间,增加1MB。
ASM & Shared Pool (ORA-4031) (文档 ID 437924.1)
大页
对于类Linux系统,CPU必须把虚拟地址转换程物理内存地址才能真正访问内存。为了提高这个转换效率,CPU会缓存最近的虚拟内存地址和物理内存地址的映射关系,并保存在一个由CPU维护的映射表中,为了尽量提高内存的访问速度,需要在映射表中保存尽量多的映射关系。这个映射表在Linux中每个进程都要持有一份,如果映射表太大,就会大大降低CPU的TLB命中率,主流的Linux操作系统,默认页的大小是4K,对于大内存来说,这会产生非常多的page table entries,上面已经提到,Linux下页表不是共享的,每个进程都有自己的页表,现在随便一个主机的内存都配置的是几十个G,几百个G,甚至上T,如果在上面跑Oracle不使用大页,基本上是找死,因为Oracle是多进程架构的,每一个连接都是一个独占的进程,大内存+多进程+不使用大页=灾难,肉丝在8年的DBA生涯里,至少帮助不下5个客户处理过由于没有使用大页而导致的系统故障,而这5个客户都是近三四年遇到的,为什么大页这个词提前个三五年并没有被频繁提起,而当下,大页这个词在各种技术大会,最佳实践中成为热门词汇?这是因为最近几年是Linux系统被大量普及应用的几年,也是大内存遍地开花的几年,而且现在一个数据库系统动不动就是几百上千个连接,这些都促成了大页被越来越多的被关注到。
大页的好处
我们来看一下使用了大页的好处:
● 少的page table entries,减少页表内存
● pin住SGA,没有page out
● 提高TLB命中率,减少内核cpu消耗
在没有使用大页的系统上,经常可能会发现几十上百G的页表,严重情况下,系统CPU的sys部分的消耗非常大,这些都是没使用大页的情况下的一些症状。
大页的特点/缺点
● 要预先分配
● 不够灵活,甚至需要重启主机
● 如果分配过多,会造成浪费,不能被其他程序使用。
大页的分配方法
通过在文件/etc/sysctl.cnf中增加vm.nr_hugepages参数来为大页设定一个合理的值,值的单位为2MB。或者通过echo 一个值到/proc/sys/vm/nr_hugepages中也可以临时性的对大页进行设定。 至于应该为大页设定多大的值,这个要根据系统SGA的配置来定,一般建议大页的总占用量大于系统上所有SGA总和+2GB。
HugePages on Oracle Linux 64-bit (文档 ID 361468.1),AIX页表共享,一般不用设置大页。
大页的原理
以下的内容是基于32位的系统,4K的内存页大小做出的计算: 1)目录表,用来存放页表的位置,共包含1024个目录entry,每个目录entry指向一个页表位置,每个目录entry,4b大小,目录表共4b*1024=4K大小 2)页表,用来存放物理地址页的起始地址,每个页表entry也是4b大小,每个页表共1024个页表entry,因此一个页表的大小也是4K,共1024个页表,因此页表的最大大小是1024*4K=4M大小 3)每个页表entry指向的是4K的物理内存页,因此页表一共可以指向的物理内存大小为:1024(页表数)*1024(每个页表的entry数)*4K(一个页表entry代表的页大小)=4G 4)操作系统将虚拟地址映射为物理地址时,将虚拟地址的31-22这10位用于从目录表中索引到1024个页表中的一个,将虚拟地址的12-21这10位用于从页表中索引到1024个页表entry中的一个。从这个页表entry中获得物理内存页的起始地址,然后将虚拟地址的0-12位用作4KB内存页中的偏移量,那么物理内存页起始地址加上偏移量就是进出所需要访问的物理内存地址。
由于32位操作系统不会出现4M的页表,因为一个进程不能使用到4GB的内存空间,有些空间被保留使用,比如用来做操作系统内核的内存。而且页表entry的创建出现在进程访问到一块内存的时候,而不是一开始就创建。
页表内存计算
在32位系统下,一个进程访问1GB的内存,会产生1M的页表,如果是在64位系统,将会增大到2M。 很容易推算,如果一个SGA设置为60G,有1500个Oracle用户进程,64位Linux的系统上,最大的页表占用内存为:60*2*1500/1024=175G,是的,你没看错,是175G!但是实际情况看到的页表占用可能没有这么大,打个百分之四五十的折扣,这是因为只有服务器进程访问到SGA的特定区域后,进程才需要把这一块对应的页表项加入到自己的页表中。
11.2.0.3版本
11.2.0.3版本之前,如果分配的大页数量不足,那么Oracle启动过程中不会使用大页,转而使用小页,但是在11.2.0.3版本后,Oracle在启动时同样会尝试使用大页,如果大页的数量不够,那么会把当前配置的大页使用完,不够的部分再从小页中去获取,这一行为实际上是通过Oracle的一个参数来控制USE_LARGE_PAGES,后面会对这个参数做详细解释。通过数据库实例的alert文件可以清楚的看到这一情况的发生:
Total Shared Global Region in Large Pages = 1024 MB (85%),代表只有85%的SGA被放在了大页中。RECOMMENDATION部分,建议你至少增加89个大页来让SGA完全放到大页中。
USE_LARGE_PAGES
这个参数用来控制数据库实例启动时,对于大页内存的使用行为。有3个值在11.2.0.3版本之前,11.2.0.3版本多了一个值auto:
? true 默认值,尽可能使用大页,有可能有一些页在大页中,有一些在小页中,这种情况下通过操作系统命令ipcs -ma可能会看到内存分段(内存分段可能有多重情况都会导致,例如开启numa也可能会导致内存分段)
? false 不使用大页
? only 选项代表强制使用大页,否则无法启动
? auto (11.2.0.3)在实例启动时,通过后台进程dism echo xxx > /proc/sys/vm/nr_hugepages 来尽可能的使用大页
以下代码为在参数设置为auto情况下alert的输出:
可以看到启动实例过程中,优先启动了DISM进程,通过这个进程来自动完成大页的配置。$Oracle_HOME/bin/oradism的权限也是root权限,因为如果是grid权限不能完成大页的配置echo xxx > /proc/sys/vm/nr_hugepages。