OpenGauss 与 NVM
OpenGauss 与 NVM
[openGauss](javascript:void(0);) 2023-08-07 18:00 发表于中国香港
以下文章来源于 yanzongshuaiDBA ,作者 yzshover
yanzongshuaiDBA.
专注于 MySQL、PostgreSQL 等开源数据库
OpenGauss 与 NVM
NVM(非易失性内存),也叫 PM(持久内存)具有可字节寻址、大容量、非易失型和堪比 DRAM 的速度等特性。随着英特尔傲腾产品的出世,现有数据库适配这种新型硬件显得更加有必要。
OpenGauss 在这方面也做了探索,实现了 DRAM-PM-DISK 三层存储架构。当然,实现方式参考了 2018 年 SIGMOD 会议的一篇论文《Managing Non-Volatile Memory in Database Systems》,感兴趣可以查看。我们这里介绍下 OpenGauss 中的 DRAM-PM-DISK 三层存储架构。
1、当前存储架构问题
当前 opengauss 的架构是 DRAM-DISK,也就是面向磁盘的数据库。当数据量大于 shared_buffers 时,共享内存的数据页淘汰流程就会称为瓶颈。甚至于导致共享内存淘汰数据页处的逻辑占用 CPU 较高,进而影响 pagewriter 线程刷写,让淘汰更慢,影响正常工作。
引入持久内存 PM,形成 buffer pool 的三层 DRAM-PM-DISK 架构的设计,利用成本相对较低的 PM 对 shared_buffers 进行扩容,可减少淘汰流程的触发。并且,还通过数据页的访问频率来控制各个缓存层之间的页面迁移,实现热数据在 DRAM,温数据在 PM,冷数据在 DISK。
2、OpenGauss 启用 PM 的相关配置项
1)enable_nvm:启用 nvm buffer 管理。默认是 false
2)nvm_buffers:设置 nvm buffer 大小,即 BLCKSZ 的个数
3)nvm_file_path:指定 NVM 的路径,因为 NVM 以-o dax 方式挂载(跳过操作系统缓存)后,需要将其 map 到内存。
4)bypass_dram:控制 by pass 内存的概率
5)bypass_nvm:控制 by nvm 的概率
3、OpenGauss 的 DRAM-PM-DISK 架构实现机制
整体架构如上图所示,shared buffer pool 由两部分组成:DRAM buffer pool 和 NVM buffer pool,统一由 buffer 管理器进行管理。用户后台线程可以访问 DRAM 也可以访问 NVM。NVM 中的页根据访问热度会迁移到 DRAM。
3.1 buffer pool
Buffer pool 中的 block 分为 3 部分:DRAM 部分、NVM 部分和 SEG buffer blocks。这里我们只关注 DRAM 和 NVM。
(1)buffer pool 的初始化
在共享内存中申请 BufferBlocks,若开启 nvm,则通过 nvm_init 来初始化 NVM 的 BufferBlocks。NVM 通过 mmap 操作将 NVM 设备映射到内存。映射大小由 nvm_buffers 配置项控制:
这里为什么不适应 pmdk 呢?pmdk 在用户态管理 NVM 可以减少内存上下文的切换,比 mmap 更具有优势。
(2)BufferAlloc 的申请
1)ReadBuffer_common 完成数据页的读取。通过 BufferAlloc 来判断是否在内存中命中需要的页,若命中则返回该内存块的描述符。未命中的话,就需要调用 ReadBuffer_common_ReadBlock 将磁盘上的页通过 smgrread 加载到内存。当然这里是加载到 DRAM 还是 NVM,则由 buf 描述符来指定。需要观察 BufferAlloc 函数实现机制
2)BufferAlloc 函数:开启 NVM 时,通过 NvmBufferAlloc 函数获取 buf 描述符
3)NvmBufferAlloc 机制:
(1)因为 NVM 和 DRAM 都是内存,由 t_thrd.storage_cxt.SharedBufHash 哈希表统一管理,所以首先从这个 hash 表中查询需要的页,看下是否在内存
(2)若在 hash 表中没找到则表示没在内存中命中,则根据是否 bypassnvm 来决定使用哪块的 buf 描述符:bypassnvm:使用 DRAM 块的 buf 描述符;否则使用 NVM 处的描述符。
(3)若在 hash 表中找到,则表示内存中命中。进一步判断:Buf id 位于[0,NvmBufferStartID)是 DRAM 的块,位于[NvmBufferStartID,SegmentBufferStartID)的是 NVM 的块。
(4)命中 DRAM 的块,直接使用
(5)命中 NVM 的块,bypassdram,直接使用
(6)命中 NVM 的块,非 bypassdram,则需要将 NVM 的块拷贝到 DRAM。这里同样使用 memcpy,使用 pmdk 的函数效果更好。
评论