本文内容按以下思路进行编写:
DM8 有哪些内存 → 这些内存如何分类 → 内存池如何工作 → Buffer 如何工作 → SQL 执行时又会产生哪些运行时内存 → 最后用参数把整个体系串起来。
第一部分 DM8 内存基础
数据库运行过程中会频繁进行内存申请和释放。如果每一次内存申请都直接调用操作系统,不仅会增加 OS 调用次数,也不利于数据库自身对内存资源进行统一管理。
因此,DM8 对内部内存对象进行了封装和统一管理,其主要目的包括:
从最宏观的角度,可以把 DM8 内存理解为:
从资源角度看,达梦数据库向操作系统申请的内存主要有两类对象:
内存池(Memory Pool)和缓冲区(Buffer)。
这两类内存对象彼此独立。数据库启动时按照参数策略进行初始化;Buffer 的资源占用相对固定,并通过内部淘汰算法管理;内存池则可能随着运行情况自动扩展,内部没有 Buffer 那样的淘汰机制。
这是整个 DM8 内存体系中最重要的第一层分类。
以下为内存池和缓冲区的主要区别:
内存池:动态内存资源,可按需扩展
缓冲区:缓存数据,有淘汰机制
两者解决的问题不同。
核心是:管理数据库运行过程中各种动态内存对象。 关注内存对象管理。
例如:
工作原理:申请-->使用-->扩展-->释放
核心是:缓存数据库运行过程中频繁访问的数据和相关信息。 关注数据缓存管理。
例如:
工作原理:磁盘内容-->Buffer-->访问-->长期保留 / 淘汰
内存池可以理解为:
DM8 为了统一管理动态内存资源而建立的一种内存管理对象。
每一个内存池都有三个非常重要的属性:
Original Size 初始大小 Extend Size 扩展大小 Target Size 目标大小
其中:
TARGET_SIZE >= ORIGINAL_SIZE
当内存池创建时,DM8 首先申请 ORIGINAL_SIZE 的内存;当池内可用资源不足时,则按照 EXTEND_SIZE 继续申请。
可以把内存池的基本工作过程理解成:
内存池不是一次申请完就永远不变,而是具有"初始 → 扩展 → 使用 → 释放"的生命周期。
| 属性 | 含义 | 学习关键词 |
|---|---|---|
| Original Size | 创建时申请的大小 | 起步多少 |
| Extend Size | 不够时每次扩展多少 | 一次扩多少 |
| Target Size | 希望维持的目标大小 | 希望保持多少 |
需要注意的是,TARGET_SIZE 并不能简单理解成"最大值"。
当
TARGET_SIZE=0时,不限制扩展;对于共享内存池,即使TARGET_SIZE不为 0,也不限制其扩展,TARGET_SIZE更多用于提示系统在空闲时尽快将内存占用释放到目标值以下。
因此:
Target 更接近"目标容量",而不是绝对的硬上限。
DM8 内存池从生命周期和资源共享方式来看,可以重点理解为:
共享池是 DM8 内存体系中的核心资源池。主要可以理解为:
其中比较重要的共享池对象包括:
SHARE POOLBACKUP POOLTRXDICT CACHESQL Cache 等。共享池除了自身为各种资源提供内存外,还有一个非常重要的作用:
为其他已经达到目标大小的内存池继续扩展提供内存。
这个过程可以用下图直观表示:
BACKUP POOL 是:
系统正常内存申请失败时使用的备用内存池。
其基本逻辑:正常申请失败后用于应急申请
MEMORY_BAK_POOL 用于控制系统备份内存池大小;正常内存申请失败时,可以从备用池申请。
TRX 是共享池体系中的一个内存池,其特点是:
WORKER_THREADS + 2 个池;TRX Pool:与事务相关的内存池。
DICT CACHE 即数据字典缓存。它负责存储部分数据字典信息,由 DICT_BUF_SIZE 控制。
DICT CACHE:实际在共享池中。
共享池逻辑上可以看作一个统一的共享内存池。
但在高并发情况下,如果所有线程都访问同一个池:
容易产生临界区竞争。
因此 DM8 可以对共享池进行物理分片:
分片数量由 MEMORY_N_POOLS 控制。
共享池分片有利于降低临界区资源冲突;分片数量过多则可能产生额外空间消耗。
DM8 内部存在不同的内存使用方式,其中重要的包括:
OS_MALLOC / OS_FREEHEAPMPOOLDM 内部内存管理方式 │ ├── OS_MALLOC / OS_FREE │ ├── HEAP │ └── 从预先申请的堆空间中分配 │ └── MPOOL └── 伙伴算法 + 扩展
直接调用操作系统的 malloc 等函数申请和释放内存。
先申请一片内存,后续从这块堆空间中继续分配;HEAP 也支持扩展。
通过伙伴算法先申请一片大小为 2^n 的初始内存,不够时继续扩展。
Buffer 与 Memory Pool 最大的区别是:
Buffer 主要用于缓存数据库内容,而不是单纯保存动态内存对象。
其中数据缓冲区用于存储:
数据页从磁盘读取之后,以及写入磁盘之前的数据。
Data Buffer 是最核心的 Buffer。其基本作用:
缓存数据库的数据页,减少磁盘访问,提高数据库访问性能。
可以理解为:
NORMAL 可以理解为:
普通数据页的默认缓存区域。
主要服务于普通的数据访问。
KEEP 的定位是:
为需要长期/重点保留的数据提供专用缓冲区。
对应参数:KEEP
RECYCLE 的定位是:
用于不希望长期占用普通 Buffer 的数据。
对应参数:RECYCLE
这样可以将不同类型的数据访问隔离开:
普通数据 热点/希望保留的数据 不希望长期占用普通缓存的数据 ↓ ↓ ↓ NORMAL KEEP RECYCLE
FAST Buffer 是 Data Buffer 体系中的特殊区域。主要用于存储高频访问的"热页"或回滚页,以加速特定类型的访问。
相关参数:FAST_POOL_PAGES,它以页数为单位控制快速缓冲区大小。
同时,ENABLE_FREQROOTS 等参数与 FAST POOL 管理方式相关。
面向快速访问场景的数据缓冲区域。
与共享内存池类似,Data Buffer 也是共享资源。
DM8 通过 BUFFER_POOLS 将大的系统缓冲区划分成多个小缓冲区:
其核心目的:
降低 Buffer 访问时的 mutex 竞争,提高并发性能。
BUFFER_POOLS 将大的系统缓冲区分割成多个部分,使不同数据页尽可能落到不同子池,从而降低竞争。
Log Buffer 用于:
缓存日志。
基本过程:事务操作-->产生日志-->Log Buffer-->日志文件
其中:
RLOG_BUF_SIZE:控制日志缓冲;RLOG_POOL_SIZE:控制日志缓冲区总大小。所有线程的日志缓冲区总大小不能超过 RLOG_POOL_SIZE,日志缓冲区所占内存从共享内存池中申请。
这里需要特别说明:Buffer 虽然在资源分类上与 Memory Pool 独立,但某些 Buffer 的具体资源来源仍然可能与共享池有关。
Dictionary Buffer:
缓存部分数据字典信息。
核心参数:DICT_BUF_SIZE
SQL Buffer 主要用于 SQL 相关缓存。
SQL │ ├── SQL 信息 ├── 执行相关缓存 └── SQL Cache
其资源扩展可以使用共享池提供的资源。
共享池的使用量可能因为 SQL Cache Management 等模块扩展而明显增加。
可以先用一句话定义:
SQL Runtime 是 SQL 执行过程中产生的运行时内存资源。
与 Buffer 的区别:
| 名称 | Buffer | SQL Runtime |
|---|---|---|
| 周期 | 偏长期 | 偏临时 |
| 功能 | 偏缓存 | 偏执行 |
| 对象 | 数据库内容 | 当前 SQL |
在SQL执行中会有以下几种运行时内存
当客户端连接数据库时,DM8 会为 Session 创建相应的会话内存池。
生命周期:
客户端连接-->Session 创建-->创建 Session 内存池-->保存 Session 信息 -->连接断开-->Session 内存池销毁-->内存归还上层内存管理器
Session Pool 是连接生命周期级别的内存。
PARALLEL_POLICY=0同时MPP_INI=0为私有内存池,否则就是共享内存池;
执行 SQL 时:
Session-->SQL-->创建 VM 内存池-->保存 SQL 执行信息-->SQL 执行-->执行完成-->VM 内存池销毁-->内存归还 Session Pool
SQL 执行时会从会话池申请 VM 内存池;SQL 执行完成后,VM 内存池销毁,内存归还会话池。
VM 更接近 SQL 执行级别,而 Session Pool 更接近连接级别。
当 SQL 需要排序时,例如:
ORDER BY
GROUP BY
DISTINCT
会产生排序内存需求。
逻辑过程:
SQL-->需要排序-->SORT Buffer-->执行排序-->SQL 完成-->释放
DM8 中排序缓冲区:
SORT_BUF_SIZE 控制排序缓冲区大小;因此要特别记住:
SORT Buffer 是运行时内存,不是像 Data Buffer 那样长期缓存数据页。
补充说明:由于排序缓冲区是运行时按需申请、用完即毁的,因此在
V$MEM_POOL中通常不会看到一个名为SORT_POOL的持久内存池,其内存使用体现在共享池的瞬时增长中。如需验证,可以在执行大规模排序操作的同时监控共享池的大小变化。
HASH Buffer 用于:HASH JOIN。
其特点:
HJ_BUF_SIZE 控制 HASH JOIN 数据缓存大小;VM → MEMOBJ;VM_MEM_HEAP 决定 VM MEMOBJ 的空间来源方式。因此 HASH 可以表示成:
SQL-->HASH JOIN-->VM-->MEMOBJ-->HASH Buffer-->SQL结束-->释放
VM_MEM_HEAP 有三种模式:
| 数值 | 含义 |
|---|---|
| 0 | 创建内存池,空间来自 OS |
| 1 | 创建堆,空间来自共享池 |
| 2 | 同时创建内存池和堆 |
SQL Runtime 并不是简单地对应某一个固定内存池,而是存在不同的底层内存资源获取方式。
| Runtime 对象 | 作用 | 生命周期 |
|---|---|---|
| Session Pool | 保存 Session 信息 | Session 生命周期 |
| VM Pool | 保存 SQL 执行信息 | SQL 生命周期 |
| SORT | 排序运行内存 | 当前排序操作 |
| HASH | HASH JOIN 运行内存 | 当前 HASH 操作 |
本节按照内存对象 → 参数 的映射关系来展开
首先是整个数据库实例层面的参数。
表示:
DM 服务器能够使用的最大内存占操作系统物理内存的百分比。
默认值为 100,建议一般情况下不要随意修改。
共享池核心参数:
| 参数 | 含义 |
|---|---|
MEMORY_POOL |
共享内存池初始大小 |
MEMORY_EXTENT_SIZE |
共享池每次扩展大小 |
MEMORY_TARGET |
共享池目标大小 |
MEMORY_N_POOLS |
共享池分片数量 |
MEMORY_BAK_POOL |
备用内存池大小 |
其中 MEMORY_EXTENT_SIZE 的单位为 MB,默认值为 32 MB。
系统缓冲区大小。默认值为 8000 MB,一般可用物理内存的 60%~80% 作为推荐参考。
系统缓冲区分区数量:
BUFFER │ ├── Pool 0 ├── Pool 1 ├── Pool 2 └── ...
每个分区大小:
BUFFER / BUFFER_POOLS
控制 FAST Buffer 页数。
KEEP Buffer 大小。
RECYCLE Buffer 大小。
RLOG_BUF_SIZE RLOG_POOL_SIZE
关系:
Log Buffer │ ┌───────┴────────┐ │ │ RLOG_BUF_SIZE RLOG_POOL_SIZE 单路日志缓冲 总日志缓冲
所有线程的日志缓冲区总大小不能超过 RLOG_POOL_SIZE。
DICT_BUF_SIZE │ ▼ DICT CACHE │ ▼ 部分数据字典信息
SQL Cache 相关资源主要与 SQL 缓存、执行相关缓存管理有关。
重点关注:CACHE_POOL_SIZE
SQL Cache 等模块的扩展可能进一步消耗共享池资源。
SQL Runtime 最重要的几个参数:
其中:
| 参数 | 对象 | 作用 |
|---|---|---|
VM_POOL_SIZE |
VM | VM Pool 大小相关 |
VM_POOL_TARGET |
VM | VM Pool 目标大小 |
VM_MEM_HEAP |
VM | VM 内存来源方式 |
SORT_BUF_SIZE |
SORT | 单会话排序缓冲区大小(会话级参数) |
SORT_BUF_GLOBAL_SIZE |
SORT | 全局排序内存上限(系统级参数) |
HJ_BUF_SIZE |
HASH | 单会话 HASH JOIN 缓冲区大小(会话级参数) |
HJ_BUF_GLOBAL_SIZE |
HASH | 全局 HASH 内存上限(系统级参数) |
还有几个参数虽然不是"某块业务内存的大小",但属于内存管理体系的重要参数。
用于开启内存泄漏检测:
0 → 不开启 1 → 开启
开启后,系统会对内存分配进行登记,并可通过 V$MEM_REGINFO 查看。
用于内存池校验:
0 → 不校验 1 → 普通校验 2 → 增强校验
| 内存体系 | 核心对象 | 主要参数 |
|---|---|---|
| 全局 | DM 可用内存范围 | MAX_OS_MEMORY |
| Shared Pool | 共享内存池 | MEMORY_POOL |
| Shared Pool | 扩展 | MEMORY_EXTENT_SIZE |
| Shared Pool | 目标 | MEMORY_TARGET |
| Shared Pool | 分片 | MEMORY_N_POOLS |
| Backup | 备用池 | MEMORY_BAK_POOL |
| Data Buffer | 系统缓冲区 | BUFFER |
| Data Buffer | 分片 | BUFFER_POOLS |
| FAST | 快速缓冲区 | FAST_POOL_PAGES |
| KEEP | KEEP Buffer | KEEP |
| RECYCLE | RECYCLE Buffer | RECYCLE |
| Log | 日志缓冲区 | RLOG_BUF_SIZE / RLOG_POOL_SIZE |
| Dictionary | 字典缓存 | DICT_BUF_SIZE |
| SQL | SQL Cache | CACHE_POOL_SIZE |
| Session | Session Pool | SESS_POOL_SIZE / SESS_POOL_TARGET |
| VM | VM Pool | VM_POOL_SIZE / VM_POOL_TARGET |
| VM | VM 内存来源 | VM_MEM_HEAP |
| SORT | 排序运行内存 | SORT_BUF_SIZE / SORT_BUF_GLOBAL_SIZE |
| HASH | HASH 运行内存 | HJ_BUF_SIZE / HJ_BUF_GLOBAL_SIZE |
| 管理 | 内存泄漏检查 | MEMORY_LEAK_CHECK |
| 管理 | 内存校验 | MEMORY_MAGIC_CHECK |
详细的参数说明可参考DM8 DBA系统管理员手册
注:上图中 Memory Pool 下的 "Shared Pool" 与 "Runtime/Private" 为管理维度的并列关系,不代表其资源来源完全独立。
如果从资源分配角度观察,图还可以进一步简化:
这里要特别强调:
"内存对象分类"和"内存资源来源"是两个不同维度。
例如:
SORT │ └── 属于 SQL Runtime │ └── SORT_BUF_SIZE │ └── 资源来源:SHARE POOL
LOG │ └── 属于 Buffer │ └── LOG_BUF_SIZE │ └── 资源来源:SHARE POOL
又例如:
HASH │ └── 属于 SQL Runtime │ └── HJ_BUF_SIZE │ └── VM → MEMOBJ │ └── 具体来源受 VM_MEM_HEAP 控制
DM8 Memory ├── Memory Pool └── Buffer
这是最重要的一级分类。
Memory Pool ├── Shared Pool ├── Runtime / Private Pool ├── Session ├── VM ├── TRX ├── DICT ├── LOG └── SQL Cache 等
它的核心特征是:
可以根据运行情况扩展,没有 Buffer 那样的数据页淘汰机制。
Buffer ├── Data Buffer │ ├── NORMAL │ ├── KEEP │ ├── RECYCLE │ └── FAST ├── Log Buffer ├── Dictionary Buffer └── SQL Buffer
核心特征:
缓存数据库内容,并通过相应的缓存管理/淘汰机制提高访问效率。
SQL Runtime ├── Session ├── VM ├── SORT └── HASH
核心特征:
生命周期通常与 Session 或 SQL/具体操作相关,用完释放。
参数分类图:
常见内存对象的划分
| 内存对象 | 功能分类 | 资源管理 | 实际来源 |
|---|---|---|---|
| 数据缓冲区 | 缓冲区 | 独立缓冲区资源 | OS |
| SQL 缓冲区 | 缓冲区 | 缓冲区 | 共享池 |
| 字典缓冲区 | 缓冲区 | 缓冲区 | 共享池 |
| 日志缓冲区 | 缓冲区 | 缓冲区 | 共享池 |
| Session Pool | 内存池 | 运行时内存池 | 根据配置决定 |
| VM Pool | 内存池 | 运行时内存池 | 根据 VM 机制决定 |
| Sort Buffer | 排序区 | 运行时内存池 | 共享池 |
| Hash Buffer | 哈希区 | 运行时内存池 | VM→MEMOBJ→VM_MEM_HEAP |
文章
阅读量
获赞
