Linux 大页与透明大页

一、 大页

对于类Linux系统,CPU必须把虚拟地址转换程物理内存地址才能真正访问内存。为了提高这个转换效率,CPU会缓存最近的虚拟内存地址和物理内存地址的映射关系,并保存在一个由CPU维护的映射表中,为了尽量提高内存的访问速度,需要在映射表中保存尽量多的映射关系。这个映射表在Linux中每个进程都要持有一份,如果映射表太大,就会大大降低CPU的TLB命中率,主流的Linux操作系统,默认页的大小是4K,对于大内存来说,这会产生非常多的映射表。Linux下页表不是共享的,每个进程都有自己的页表,现在随便一个主机的内存都配置的是几十个G,几百个G,甚至上T,如果在上面跑Oracle不使用大页,基本上是找死,因为Oracle是多进程架构的,每一个连接都是一个独占的进程,大内存+多进程+不使用大页=灾难

1. 大页的优点

我们来看一下使用了大页的好处:

  • 内存映射表较小,减少页表内存
  • pin住SGA,没有page out
  • 提高TLB命中率,减少内核cpu消耗

在没有使用大页的系统上,经常可能会发现几十上百G的页表,严重情况下,系统CPU的sys部分的消耗非常大,这些都是没使用大页的情况下的一些症状。

2. 大页的缺点

  • 要预先分配
  • 不够灵活,需要重启主机生效
  • 如果分配过多,会造成浪费,不能被其他程序使用。

3. 大页的分配方法

通过在文件/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页表共享,一般不用设置大页。

4. 大页的原理


Linux 大页与透明大页_第1张图片

以下的内容是基于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的创建出现在进程访问到一块内存的时候,而不是一开始就创建。

5. 页表内存计算

在32位系统下,一个进程访问1GB的内存,会产生1M的页表,如果是在64位系统,将会增大到2M。 很容易推算,如果一个SGA设置为60G,有1500个Oracle用户进程,64位Linux的系统上,最大的页表占用内存为:60*2*1500/1024=175G,是的,你没看错,是175G!但是实际情况看到的页表占用可能没有这么大,打个百分之四五十的折扣,这是因为只有服务器进程访问到SGA的特定区域后,进程才需要把这一块对应的页表项加入到自己的页表中。

6. 11.2.0.3版本

11.2.0.3版本之前,如果分配的大页数量不足,那么Oracle启动过程中不会使用大页,转而使用小页,但是在11.2.0.3版本后,Oracle在启动时同样会尝试使用大页,如果大页的数量不够,那么会把当前配置的大页使用完,不够的部分再从小页中去获取,这一行为实际上是通过Oracle的一个参数来控制USE_LARGE_PAGES,后面会对这个参数做详细解释。通过数据库实例的alert文件可以清楚的看到这一情况的发生:


Linux 大页与透明大页_第2张图片

Total Shared Global Region in Large Pages = 1024 MB (85%),代表只有85%的SGA被放在了大页中。RECOMMENDATION部分,建议你至少增加89个大页来让SGA完全放到大页中。

7. 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的输出:
Linux 大页与透明大页_第3张图片

可以看到启动实例过程中,优先启动了DISM进程,通过这个进程来自动完成大页的配置。$Oracle_HOME/bin/oradism的权限也是root权限,因为如果是grid权限不能完成大页的配置echo xxx > /proc/sys/vm/nr_hugepages。

 

二、 透明大页

从RedHat6, RedHat7, OL6, OL7 SLES11 和 UEK2 kernels开始transparent hugepage被默认开启,它允许大页做动态的分配,而不是系统启动后就分配好,根据Oracle MOS DOC:1557478.1,transparent hugepage导致了很多的问题,建议将其关闭 查看是否启用了transparent hugepage cat /sys/kernel/mm/transparent_hugepage/enabled [always] never []内的值是当前启用的值,上面的输出说明启用了transparent hugepage。 可以通过 /etc/grub.conf 文件来关闭transparent hugepage。

通过增加关键字transparent_hugepage=never来讲transparent hugepage关闭。 也可以通过开机启动后,echo相应的值到对应的文件来动态关闭transparent hugepage。

OS层面查看大页使用情况

HugePages_Total为所分配的页面数目,和Hugepagesize相乘后得到所分配的内存大小。43000*2/1024大约为84GB。

HugePages_Free为从来没有被使用过的Hugepages数目。即使Oracle sga已经分配了这部分内存,但是如果没有实际写入,那么看到的还是Free的。这是很容易误解的地方。

HugePages_Rsvd为已经被分配预留但是还没有使用的page数目。在Oracle刚刚启动时,大部分内存应该都是Reserved并且Free的,随着Oracle SGA的使用,Reserved和Free都会不断的降低。

HugePages_Free – HugePages_Rsvd 这部分是没有被使用到的内存,如果没有其他的Oracle instance,这部分内存也许永远都不会被使用到,也就是被浪费了。在该系统上有11.5GB的内存被浪费了。

最佳实践

对于Oracle来说,这是一个最佳实践泛滥的时代,所有的最佳实践其实都有特定的应用场景,而不是放之四海皆准,但是当今信息爆炸时代(数据库种类爆炸时代),很多DBA没有那么多的耐心去专注学习一门数据库,只是拿来主义从网上找出一些最佳实践来,不管三七二十一就用到自己的环境中(甚至是生产环境中),一定程度上来说,崇拜最佳实践是懒惰的结果。

针对于Oracle的内存分配方式,肉丝给大家推荐在核心系统上: 1.使用手工的SGA管理,这种管理方式已经非常的成熟,但是这种方式对DBA要求较高,一定程度上需要了解业务特点。 2.如果第一种方法你感觉到比较难,那么使用ASMM,但是为buffer cache,shared pool设定最小值的方式,这种方式是我非常推荐的方式。

至于AMM这种内存分配方式,由于不能使用大页,建议不要去用。

具体到内存如何分配,针对是OLTP场景,还是OLAP场景,有着不同的内存分配原则,即使是OLTP场景,针对进程数的多少,并发数的多少,SQL语句的特点,都会导致产生不同的内存分配方法。作为DBA要掌握基本的Oracle内存使用原理,然后根据实际情况去根据业务自身特点分配内存。

不管是OLTP系统或者OLAP系统,一般都建议至少为操作系统预留出20%的内存空间,这些内存空间用于kernal、page table以及一些其他的进程需要,例如rman备份,文件系统缓存。这个也是Oracle官方的建议。

把这一部分的内存刨去后,针对OLTP系统,可以考虑先尝试预留出20%的内存给PGA使用,80%的内存给SGA使用。如果系统上进程数比较多,特别是同时活跃的进程数比较多,需要给PGA预留出更多的内存,可以按照每个进程12M的PGA占用进行计算。

针对OLAP系统,刨去操作系统的预留内存后,可以考虑预留出50%的内存给PGA使用,50%的内存给SGA使用。毕竟在纯OLAP下,buffer cache其实不需要那么大的内存空间。

SGA、PGA分配好后,要考虑到是否需要为buffer cache、shared pool来手工设定一个值,就像上文已经提到过的,可以参考buffer cache 的命中率,Library Hit的命中率作为一个辅助参考.如果命中率较低,有可能是内存分配的不够合理。当然你还需要借助于像AWR报告这样的工具,根据SQL的执行计划等内容来进一步的做诊断,比如命中率低的原因可能不是分配的buffer cache小,而是存在大量的低效的全表扫描,进而导致命中率低,这样的话,需要优化的就是SQL,而不是继续加大buffer cache。再比如,如果你发现Library cache Hit比较低,可能并不是shared pool比较小,还可能是系统的SQL存在大量未使用绑定变量的情况,导致硬解析过重。

优化经常是个系统工程,不能一蹴而就,特别像Oracle是一个很庞大的复杂系统,对于问题的出现,更是要仔细推敲,不能轻易的下结论。随着做DBA时间越来越长,你也会越来越懂TOM的一句话:It depends。

 

其它白皮书

SQL MONITOR: http://pan.baidu.com/s/1gfO2DtL

Parallel原理实现: http://pan.baidu.com/s/1i5xun9F

12C IN-MEMORY http://pan.baidu.com/s/1ge7r1oZ

参考

http://blog.itpub.net/28218939/viewspace-2083178/

http://blog.itpub.net/28218939/viewspace-2083183/

你可能感兴趣的:(Oracle,linux,性能)