
# 内存管理
#### Linux内存机制
- 物理内存 物理可见的硬件，即内存条。
  
- 虚拟内存 进程运行时所有内存空间的总和，以32位系统为例，每个进程有4GB的内存空间，各进程的内存空间具有类似的结构，如[图1]所示：
  图1Linux内存机制   
  ![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002620640661.png "点击放大")
  - 一个新进程建立的时候，将会建立起自己的内存空间，并将此进程的数据、代码等在需要被读写时从磁盘复制到自己的内存空间。进程中每一条数据的地址都由进程控制表中的task_struct记录。task_struct包含一张链表，用来记录内存空间的分配情况，哪些地址有数据、哪些地址无数据、哪些可读、哪些可写都可以通过这个链表记录。
   
  - 每个进程已经分配的内存空间，都与对应的磁盘空间存在映射关系。
    ![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/public_sys-resources/note_3.0-zh-cn.png)
    建议不要对进程虚拟内存大小进行限制。
    
   
- 物理内存地址和虚拟内存地址
  - 内存分页机制
    - 虚拟内存中的页（page）和物理内存页帧（page frame）相同大小。
    
    - 页表（page table）维护虚拟内存页到物理内存页的映射。
    
    - 页面失效（page fault）功能：操作系统根据特定的替换策略（如最少使用，LRU）选择一个虚拟页帧，使其失效，并将其内容写入磁盘。然后将需要访问的页加载到该页帧中，并更新页表中的映射关系。
     
  
  - 虚拟内存地址：页号（与页表中的页号关联）和偏移量（页的大小）组成。
  
  - 虚存寻址：页表中找到页帧号，若失效则调入页，页帧号及偏移量传给内存管理单元，组成一个物理存在的地址，即可访问物理内存数据。
   
- Buffer
  - 根据磁盘的读写设计的，把分散的写操作集中进行，减少磁盘碎片和硬盘的反复寻道，从而提高系统性能。
  
  - Linux有一个守护进程定期清空缓冲内容（即写入磁盘），也可以通过sync命令手动清空缓冲。
   
- Cache
  - 将读取过的数据保存起来，重新读取时若命中（找到需要的数据）则不读取硬盘，若未命中，则读取硬盘。其中的数据会根据读取频率进行组织，把读取最频繁的内容放在最容易找到的位置，把不再读的内容不断往后排，直至从中删除。
   
- OOM
  - 内存溢出（Out Of Memory，简称 OOM）是指应用系统中存在无法回收的内存或使用的内存过多，最终使得程序运行要用到的内存大于能提供的最大内存。此时程序无法运行，系统会提示内存溢出，有时会自动关闭软件，通过重启机器或软件释放掉一部分内存后又可以正常运行该软件，而由系统配置、数据流、用户代码等原因导致的内存溢出错误，即使用户重新执行任务依然无法避免。
   
 
#### GaussDB内存资源管理
- 内存管理简介
  - GaussDB的动态内存使用方法均基于内存上下文管理，在内存上下文的机制上，引入了逻辑内存管理机制，同时提供多项视图来追踪内存使用情况。 当前GaussDB的内存管理分为两级，分别是：
    - 内存节点级别控制（非透明多写）：通过max_process_memory参数限制CN、DN上可以使用的内存上限。
    
    - 内存作业级别控制：限制单条query语句最大可使用的内存上限。
     
   
- 节点级别内存控制
  - 在Linux操作系统中，内存空间通常由物理内存RAM和虚拟内存Swap组成。其中，物理内存由数据库程序和非数据库程序共同使用；而数据库程序的内存空间由GaussDB进程共同使用。如[图2]所示：
    图2Host Memory整体内存空间   
    ![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002620720531.png "点击放大") 
  
  - 由于非数据库程序也需要使用内存，一般建议GaussDB占系统可用内存的80%。
   
- 作业级别内存控制
  - 提供query_mem参数，根据能使用的内存量和能同时运行的作业数，指定每个作业执行时能够使用的内存量；提供query_max_mem参数，指定每个作业执行时能够使用的内存上限。
  
  - （非透明多写）提供work_mem参数，设置内部排序操作和Hash表在开始写入临时磁盘文件之前使用的内存大小；提供maintenance_work_mem参数，设置在维护性操作（比如VACUUM、CREATE INDEX、ALTER TABLE ADD FOREIGN KEY等）中可使用的最大内存。
   
- 逻辑内存管理机制
  - GaussDB内存管理是通过逻辑内存管理来实现的。其实现原理是在原始的内存分配之上增加一层逻辑内存管理，通过检查已分配内存是否超过规定的内存来决定是否为作业分配内存。逻辑内存管理没有更改原有的内存资源分配机制，仅在分配内存之前增加一个逻辑判断层，查看是否达到允许使用的内存上限，来决定是否分配内存。详情如[图3]所示：
    图3逻辑内存管理机制（非透明多写）   
    ![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002590201026.png "点击放大") 
   
- 内存分类
  图4内存分类（非透明多写）   
  ![](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/figure/zh-cn_image_0000002590360956.png "点击放大")
  GaussDB的内存使用分类如上所述，其每种类型的说明如下：
  - 动态内存
    - 全局动态内存
      - 全局缓存：全局缓存（Cache）用来提高访问效率，Cache中包括一个系统表元组Cache和一个表模式信息Cache（RelCache），SysCache中存放的是最近使用过的系统表的元组，而RelCache中包含所有最近访问过的表的模式信息（包含系统表的信息）。RelCache中存放的不是元组，而是RelationData数据结构，每一个RelationData结构表示一个表的模式信息，这些信息都由系统表元组中的信息构造而来。
      
      - 全局hash表：保存全局的统计信息或其他数据的内存hash结构。
      
      - 全局其他内存：保存全局变量等内容。
       
    
    - 线程/session级动态内存
      - 语句级内存：生命周期为整个SQL语句执行阶段，语句执行前内存被申请，执行结束后内存被回收释放。
      
      - 事务级内存：生命周期为整个事务内，事务开始前内存被申请，事务结束后内存被回收释放。
      
      - 算子级内存：生命周期为单个算子有效期内，算子执行结束后内存被释放回收。
      
      - 临时内存：一些临时变量申请的内存，使用后及时释放。
       
     
  
  - 共享缓存
    - Shared_buffers 缓存从磁盘读取的数据所使用的内存。
      
    
    - 其他缓存 线程所使用的槽位、锁等资源占用的内存。
      
     
    
- 内存上下文机制
  - 内存上下文介绍
    - 概念：内存上下文用来统一控制内存的申请和释放，内存上下文中记录了其所申请的总内存大小，每一处内存申请的大小，申请内存所在的文件、行号等信息以及与其他内存上下文之间的关联信息。
    
    - 目的：基于数据库中作业执行逻辑，通过内存上下文管理机制，提升查询内存分配、释放效率。
     
  
  - 逻辑内存管理（非透明多写）
    - 通过数据库进程内部记账，控制单进程内存使用的上限。
    
    - 可统计进程总体使用、共享和非共享内存上下文使用及重点模块内存使用（通信库、pooler等）。
    
    - 逻辑内存的生效机制：enable_memory_limit设置为on，并且max_process_memory减掉缓存等其他预留内存 \> 2GB，具体详情请联系管理员。
    
    - PV_TOTAL_MEMORY_DETAIL视图用于查询数据库进程的内存使用情况，便于分析内存失败后问题，具体字段可参考《参考》中"系统表和系统视图 \> 系统视图 \> 其他系统视图 \> PV_TOTAL_MEMORY_DETAIL"章节。
     
   
 
