大页

对于类Linux系统(modern OS都一样),CPU必须把虚拟地址转换程物理内存地址才能真正访问内存。为了提高这个转换效率,CPU会缓存最近的虚拟内存地址和物理内存地址的映射关系,并保存在一个由CPU维护的映射表中,为了尽量提高内存的访问速度,需要在映射表中保存尽量多的映射关系。这个映射表在Linux中每个进程都要持有一份,如果映射表太大,就会大大降低CPU的TLB命中率,主流的Linux操作系统,默认页的大小是4K,对于大内存来说,这会产生非常多的page table entries,上面已经提到,Linux下页表不是共享的,每个进程都有自己的页表,现在随便一个主机的内存都配置的是几十个G,几百个G,甚至上T,如果在上面跑Oracle不使用大页,基本上是找死,因为Oracle是多进程架构的,每一个连接都是一个独占的进程,大内存+多进程+不使用大页=灾难。为什么大页这个词提前个三五年并没有被频繁提起,而当下,大页这个词在各种技术大会,最佳实践中成为热门词汇?这是因为最近几年是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。

 装有Rhel5.3的40G内存的机器上有一个oracle数据库,数据库的SGA设置为20G,当运行业务时,一个业务高峰期时,发现swap频繁交换,CPU 100%,Load很高,基本体现为内存不足。此时的连接数在600个左右。按内存的计算:每个连接占用内存基本在5M,这样600个连接只占用3G内存,SGA内存20G,操作系统占用内存1G,这样总占用的内存为24G,而总共内存有40G,怎么会内存不足呢?当时是百思不得其解,于是做了大量的压力测试,首先是写了一个java程序,启动多个线程,每个线程与数据库建一个连接,然后循环运行一个简单的SQL,这个SQL按一个随机函数生成的ID去查询一个很大的表(有索引)。当启动1000个连接后,使用free -m查看内存:
#free -m

total       used       free     shared    buffers     cached

Mem:         40210      25842                0          9        177

-/+ buffers/cache:            14554

Swap:        20481        479      20001

发现free的内存值很小,used的内存值为断增长,运行大约20分钟后,当free减少到40M左右的时候, 系统的CPU一下子到100%,Load从15升到600。

从这个结果看到,还是内存不足,当时还写了一个脚本,查看所有oracle进程的内存情况,也没有发现oracle进程占用内存太多。所以一直没有找到原因。

最后试着用cat /proc/meminfo查看内存时,终于找到了原因,没有加压力时,cat /proc/meminfo看到的结果为:

root@xxxx:/proc/sys/vm>cat /proc/meminfo

HighTotal:           0 kB

HighFree:            0 kB

LowTotal:     41175744 kB

LowFree:      27603324 kB

SwapTotal:    20972816 kB

SwapFree:     20070348 kB

Dirty:            1232 kB

Writeback:           0 kB

AnonPages:      240500 kB

PageTables:      34004 kB

当压力上来时:

root@bopspri:/proc/sys/vm>cat /proc/meminfo

HighTotal:           0 kB

HighFree:            0 kB

LowTotal:     41175744 kB

LowFree:        375212 kB

SwapTotal:    20972816 kB

SwapFree:     20070340 kB

Dirty:             184 kB

Writeback:           0 kB

AnonPages:     4375088 kB

PageTables:   23005464 kB

可以看到压力上来后,PageTables占用的内存居然高达23G。PageTables是Linux下虚拟内存到物理内存中做映射时映射表占用的空间,这个映射表居然占用了这么大的内存,真让人不可思议。

为了解决这个问题,想到了Linux的大页管理,正常的页大小为4k,而大页管理的页大小为2M,通过大页管理后,映射表占用的空间将会大大减少。

于是把数据库停了,启动大页管理,给大页管理分配20G内存:

echo 10240 > /proc/sys/vm/nr_hugepages

增加

root                soft    memlock -1

root                hard    memlock -1

oracle              soft    memlock -1

oracle              hard    memlock -1

把数据库的lock_sga改成true后,再做压力测试,系统终于能稳定运行了,free -m查看到的空闲内存一直空闲13G:
————————————————
版权声明:本文为CSDN博主「jnrjian」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jnrjian/article/details/126102034

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐