Numa以及其他内存参数等对Oracle的影响


背景知识: Numa的理解

Numa 分一致性内存访问结构 主要是对应UMA 一致性内存访问而言的. 
在最初一个服务器只有一个CPU的场景下, 都是UMA结构, 他的优点是简单, 硬件实现, 操作系统, 编译器都简单

但是随着CPU多socket 多核心的发展. CPU后来又集成了内存控制器. 导致不同CPU核心范围内存的速度出现了不一致的情况. 
NUMA就是此场景情况下来出现的.

1. 最初内存连接到北桥时, 可以大体上理解为是UMA结构的.
2. CPU收编北桥集成内存控制器之后, 直连内存控制器的核心和内存速度最快.
   通过QPI/或者是AMD的链路进行连接的内存访问就会慢一一些. 

关于内存访问的速度. 
CPU访问内存的速度比寄存器和缓存要慢很多. 
一般情况下:
CPU 访问寄存器是一个周期. 2GHz的CPU就可以在 0.5ns内完成访问. 
L1 cache 分为 data和instruct 大约在 3个周期内完成访问. 大约是1.5ns左右的时间. 
L2 cache 大约比L1的缓存慢5倍总有,大约需要 8ns的时间. 
需要注意 L1和L2 Cacahe一般是核心独占的.
L3 cache 一般是需要50个以上的周期才可以访问, 一般至少需要 30ns以上. 

内存访问时因为有充电,寻址,以及后去等操作, 他的延迟会更高. 
访问内存跟内存的工作频率以及延迟等有关系.  一般可能要到100ns 左右的时间. 

跨越numa节点时需要QPI(intel) 执行远端内存的读写, 至少比本地内存要慢一倍. 

需要说明的是 现在 两路和四路服务器 每个CPU都会直连系统内的其他CPU, 所以实际上CPU之间的距离基本上是相等的. 

不过随着CPU Chiplet技术的发展. 同一个socket内部也有多个numa节点, 所以跨numa节点内存访问速度又问题 同socket和跨socket

背景知识: numa的简单举例

鲲鹏 920服务器的numa情况
HUAWEI Kunpeng 920 5250 *2
node distances:
node   0   1   2   3
  0:  10  12  20  22
  1:  12  10  22  24
  2:  20  22  10  12
  3:  22  24  12  10

Kunpeng 920-6426 * 2 
node   0   1   2   3 
  0:  10  16  32  33
  1:  16  10  25  32
  2:  32  25  10  16
  3:  33  32  16  10

Intel的思路服务器的情况
Golden 6150 *4 
node distances:
node   0   1   2   3
  0:  10  21  21  21
  1:  21  10  21  21
  2:  21  21  10  21
  3:  21  21  21  10

Intel 比较早的CPU 单个核心基本上那个是一个numa节点.
最新的第四代可扩展志强的HCC架构已经跟AMD一样采用Chiplet模式了.  
所以也分为两种numa的延迟构型. 

需要注意 numactl -H的结果实际上一个概要的比率, 并不是严格反馈具体的实验信息.
另外也可以看出来

鲲鹏 5250的延迟比 6426的要好一些. 应该是新一代的产品.

背景知识-服务器上面的内存

内存并不是无穷无尽的资源, 并且一般服务器的内存是需要按照CPU的插座进行插入内存.

一般情况下建议内存是CPU核心的偶数倍. 
最好是 CPU插座数 * CPU的内存通道数的内存条数. 
现阶段服务器其实都是有buffer机制的. 理论上一个内存通道可以插两个内存, 并且理论上可以增加两倍的字节位宽.
这种情况下 时延没有提升, 但是带宽会有提升. 

内存建议都是相同的参数配置,  并且尽可能高的工作频率. 
内存的时序也尽量低一些. 这样机器的性能会更好. 寻址和访问速度都会快. 

服务器的BIOS里面会有一个内存刷新频率的设置. 一般会设置为 32ms 后者是64ms. 
此参数指的是32ms之内 要求所有的内存单元必须进行一次充电. 理论上数值越大,性能越好.

但是间隔越久越容易出现电容漏电导致出现一定的字节翻转导致错误数据, 所以需要根据情况进行设置.

Linux的Bug

Study From: 
https://plantegg.github.io/2021/05/14/十年后数据库还是不敢拥抱NUMA/

本身linux有一个内核参数:
zone_reclaim_mode  当为 "0" 时理论上可以在当前节点内存不足时在其他节点申请内存.
避免大量的内存被回收导致卡顿. 

但是Linux 内核 在2014年之前的版本曾经存在一个bug. 会导致操作系统不关闭numa时. 
Linux 会尽量在本地节点执行内存回收和分配, 这样会导致一个节点的内存使用量居高不下. 
内存的分配与回收时一个比较重的操作, 需要将脏数据乱盘, 或者是执行其他验证. 会导致机器的卡顿. 

2.6.32 是2009年的Linux Kernel. 所以是带着类似的Bug的.  

这个文章里面的资料反馈:
"可以看到zone_reclaim_mode 改成 1,node0内存不够了也没有分配node1上的内存,
而是从PageCache回收了40G内存,整个分配64G内存的过程也比不回收PageCache慢了12秒,这12秒就是额外的卡顿 "

关于 zone_reclaim_mode 参数

# echo 0 > /proc/sys/vm/zone_reclaim_mode
# # 意味着关闭zone_reclaim模式,可以从其他zone或NUMA节点回收内存
# echo 1 > /proc/sys/vm/zone_reclaim_mode
# # 表示打开zone_reclaim模式,这样内存回收只会发生在本地节点内
# echo 2 > /proc/sys/vm/zone_reclaim_mode
# # 在本地回收内存时,可以将cache中的脏数据写回硬盘,以回收内存。
# echo 4 > /proc/sys/vm/zone_reclaim_mode
# # 可以用swap方式回收内存。

调整的影响
默认情况下,zone_reclaim模式是关闭的。这在很多应用场景下可以提高效率,比如文件服务器,或者依赖内存中cache比较多的应用场景。
这样的场景对内存cache速度的依赖要高于进程进程本身对内存速度的依赖,所以我们宁可让内存从其他zone申请使用,也不愿意清本地cache。

如果确定应用场景是内存需求大于缓存,而且尽量要避免内存访问跨越NUMA节点造成的性能下降的话,则可以打开zone_reclaim模式。
此时页分配器会优先回收容易回收的可回收内存(主要是当前不用的page cache页),然后再回收其他内存。

打开本地回收模式的写回可能会引发其他内存节点上的大量的脏数据写回处理。
如果一个内存zone已经满了,那么脏数据的写回也会导致进程处理速度收到影响,产生处理瓶颈。
这会降低某个内存节点相关的进程的性能,因为进程不再能够使用其他节点上的内存。
但是会增加节点之间的隔离性,其他节点的相关进程运行将不会因为另一个节点上的内存回收导致性能下降

关闭的效果

大部分重内存计算的应用都建议关闭numa. 

Oracle数据库尤其建议关闭numa. 

其实理论上关闭numa相当于掩耳盗铃.
但是能够避免操作系统过分的折磨同一个node 导致性能衰退.

其他内存设置问题

1. 开启Linux的大页
2. 关闭透明大页
3. 适当处理SGA+PGA
4. 按照SGA的大小设置 内存共享段大小.

1. 大页的优缺点

内存的分配和寻址是以页面为粒度进行的. 
   现代操作系统都是段页式的内存管理. 
   段式管理主要是涉及段的类型,比如数据.文本,堆区,栈区等. 主要是权限和安全性. 
   页式管理主要是快速的进行寻址分配等操作. 以及页内小块内存的管理分配等. 
   
   页式内存的寻址和分配效率会决定内存的工作效率. 
   增加页的大小,会减少同样大小内存使用的内存页项目, 会减少TLB miss 系统性能会更好一些. 

   但是大页的分配可能会有部分浪费. 会导致内存随便和内存使用的增多.

2. 透明大页

1:从RedHat 6, OEL 6, SLES 11 and UEK2 kernels 开始,
系统缺省会启用 Transparent HugePages :用来提高内存管理的性能透明大页(Transparent HugePages )和之前版本中的大页功能上类似。
主要的区别是:Transparent HugePages 可以实时配置,不需要重启才能生效配置;

2:Transparent Huge Pages在32位的RHEL 6中是不支持的。

3: ORACLE官方不建议我们使用RedHat 6, OEL 6, SLES 11 and UEK2 kernels 时的开启透明大页(Transparent HugePages ), 
因为透明大页(Transparent HugePages ) 存在一些问题:

在RAC环境下 透明大页(Transparent HugePages )会导致异常节点重启,和性能问题
在单机环境中,透明大页(Transparent HugePages ) 也会导致一些异常的性能问题

大页和透明大页的区别
标准大页管理是预分配的方式,而透明大页管理则是动态分配的方式。 

Redis 也不建议使用透明大页. 建议使用小页面, 因为redis的对象都比较小一些.

3. 适当处理SGA+PGA

专用型数据库 建议可以将 70%-80% 的内存用于SGA+PGA
SGA和PGA的大小跟业务模式关系较大. 

如果是OLTP 大部分都是小型SQL 但是用户数据访问量达可以适当的增加PGA的区域.  
保证用户可以使用的内存. 

如果是数据量较大. 但是用户并发度小. 都是大型SQL为主. 需要大量数据进入缓存
可以适当调整一下SGA的大小, 将尽可能多的block 添加到 sga区中可以加快访问和查询速度
一般SGA:PGA 可以设置为 4:1 左右.

4. 共享段管理

1 oracle sga 里存储的那些addr值(其实就是指针)是线形地址
2 共享sga的后台进程,以及server porcess都会将sga绑定在进程里的相同线形地址上
  如果不这样,那么这些进程不可能通过addr来访问在sga里的对象
3 ibm文档上说的"段"最大为256M只会影响进程分配多少pagetable项,并不会影响OS段的大小
4 shmmax的最大值只受OS管理段时的数据结构的一些限制
5 由于使用线形地址,sga分为多个共享段只会影响空间的使用,对性能几乎没有影响

http://www.itpub.net/thread-838186-1-1.html

这一块我一直没太理解共享段对性能的影响.
理论上如果只有一个共享段, 那么进程连接SGA时只需要挂载一个共享段. 这样的管理效率应该会好一些. 
网上没有找到对应的关系, 这一块对性能的影响需要进一步进行学习.

5. 其他关于内存页的调优参数

vm.swapiness :60 改成 10
vm.dirty_ratio:90 改成 10
vm.dirty_background_ratio:60 改成 5
vm.dirty_expire_centisecs:3000改成500
vm.vfs_cache_pressure:100 改成 500

6. Oracle RAC 操作系统的内存信息

[cwgl@cwgxracnode12 ~]$ cat /proc/meminfo
MemTotal:       529193176 kB
MemFree:        322735912 kB
Buffers:         2400752 kB
Cached:         20403220 kB
SwapCached:            0 kB
Active:         24413904 kB
Inactive:       12459564 kB
Active(anon):   14504300 kB
Inactive(anon):   850352 kB
Active(file):    9909604 kB
Inactive(file): 11609212 kB
Unevictable:      430388 kB
Mlocked:          430388 kB
SwapTotal:      33234936 kB
SwapFree:       33234936 kB
Dirty:              6084 kB
Writeback:             0 kB
AnonPages:      14787404 kB
Mapped:           510920 kB
Shmem:            923172 kB
Slab:            5729792 kB
SReclaimable:    4811720 kB
SUnreclaim:       918072 kB
KernelStack:       32704 kB
PageTables:       430044 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    218394724 kB
Committed_AS:   22871784 kB
VmallocTotal:   34359738367 kB
VmallocUsed:     1572068 kB
VmallocChunk:   34358082700 kB
HardwareCorrupted:     0 kB
AnonHugePages:     12288 kB
HugePages_Total:   77575
HugePages_Free:     5516
HugePages_Rsvd:     5510
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        4096 kB
DirectMap2M:     2027520 kB
DirectMap1G:    534773760 kB

第一列
Mem 内存的使用信息
Swap 交换空间的使用信息
第一行
total 系统总的可用物理内存大小
used 已被使用的物理内存大小
free 还有多少物理内存可用
shared 被共享使用的物理内存大小
buff/cache 被 buffer 和 cache 使用的物理内存大小
available 还可以被应用程序使用的物理内存大小

free 与 available 的区别
free 是真正尚未被使用的物理内存数量。
available 是应用程序认为可用内存数量,available = free + buffer + cache 
(注:只是大概的计算方法)

具体解释:

buffer和cache是两个在计算机技术中被用滥的名词,放在不通语境下会有不同的意义。
在Linux的内存管理中,这里的buffer指Linux内存的:Buffer cache。这里的cache指Linux内存中的:Page cache。翻译成中文可以叫做缓冲区缓存和页面缓存。
在历史上,它们一个(buffer)被用来当成对io设备写的缓存,而另一个(cache)被用来当作对io设备的读缓存,这里的io设备,主要指的是块设备文件和文件系统上的普通文件。
但是现在,它们的意义已经不一样了。在当前的内核中,page cache顾名思义就是针对内存页的缓存,说白了就是,如果有内存是以page进行分配管理的,都可以使用page cache作为其缓存来管理使用。
当然,不是所有的内存都是以页(page)进行管理的,也有很多是针对块(block)进行管理的,这部分内存使用如果要用到cache功能,则都集中到buffer cache中来使用。
(从这个角度出发,是不是buffer cache改名叫做block cache更好?)然而,也不是所有块(block)都有固定长度,系统上块的长度主要是根据所使用的块设备决定的,而页长度在X86上无论是32位还是64位都是4k。

1、什么是page cache?
Page cache主要用来作为文件系统上的文件数据的缓存来用,尤其是针对当进程对文件有read/write操作的时候。
如果你仔细想想的话,作为可以映射文件到内存的系统调用:mmap是不是很自然的也应该用到page cache?
在当前的系统实现里,page cache也被作为其它文件类型的缓存设备来用,所以事实上page cache也负责了大部分的块设备文件的缓存工作。

2、什么是buffer cache?
Buffer cache则主要是设计用来在系统对块设备进行读写的时候,对块进行数据缓存的系统来使用。
这意味着某些对块的操作会使用buffer cache进行缓存,比如我们在格式化文件系统的时候。
一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,page cache的内容会被改变,而buffer cache则可以用来将page标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。
这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个page写回,而只需要写回修改的部分即可。