一、内存的基本构成
大的分类就是两种:BUFFER和运行时内存
- 所谓 BUFFER,就是数据文件的数据页在内存里面的镜像(它在内存里的格式和在数据文件里的格式是一模一样的)。我们通常配置的 BUFFER/MAX_BUFFER/RECYCLE/FAST POOL/KEEP 都是此类,这类的内存是固定不可动态调节的,配置了多大就是多大,不会溢出。不同类型的缓冲区主要表现为淘汰机制不同,除 BUFFER 外其他的 BUFFER_POOL 都确定大小,BUFFER 最大扩展到 MAX_BUFFER大小。
- 另外就是运行时内存,包含各种各样的 POOL 和 MEM HEAP,INI 里的配置,这种 POOL 没有上限配置,只有 TARGET,也就是目标大小。运行过程中可能会超出 TARGET SIZE,然后使用完毕后会回落。
- 还有一类是一些游离或者系统调用产生的内存,这些内存不在监控的范围内。DM 的内部封装了一些 MALLOC/FREE 的接口,我们假定它叫 DM_MALLOC/DM_FREE。通过这些接口调用的内存,在 MAX_OS_MEM 配置不为 100 或者 0 的情况下,会纳入总使用内存的统计,达到配置上限后,依赖各个函数自己的异常处理逻辑处理后续情况(返回错误或者刷盘归并)。但是整个代码中还是存在一些游离的普通 MALLOC 以及系统调用(比如创建线程/信号量/MUTEX的基础内存开销还有系统调用自己产生的隐式开销等)产生的内存申请并不在我们自己的监控范围内。
详细的分类是三种:缓冲区、内存池、其他内存区
- 缓冲区:数据缓冲区、日志缓冲区、字典缓冲区、SQL缓冲区
- 内存池:主内存池、其他运行时内存池
- 其他内存区:排序区、HASH 区
(1)缓冲区
数据缓冲区:
- NORMAL:普通数据页,缓冲区满时淘汰
- FAST:数据页、回滚页,常驻缓冲区
- SF_SET_FAST_POOL_FLAG:给表一个 FAST 标记,可以在数据库启动时将这个表加载到 FAST_POOL 中。
- RECYCLE:临时表数据页,正常淘汰
- MTAB 的 PAGE 会落到 RECYCLE。所以 SORT/HJ/HAGR 需要关心 RECYCLE PAGE CHANGED
- KEEP:普通数据页,很少淘汰
- 这个实际生产基本没有使用,理论上如果你有部分表需要常驻在 BUFFER 里面,就需要为这些表单独创建一个表空间,并把其表空间的默认 BUFFER 配置到 KEEP,具体的效果没有太多的参考价值。
日志缓冲区:
日志是数据库与磁盘间的一层缓存,将随机的磁盘写转换成顺序写,日志缓冲区是数据库和日志间的缓存对应 INI 参数 RLOG_BUF_SIZE,将直接的磁盘 I/O 转换成缓存 I/O。
DML 语句发起 I/O 请求 ===> RLOG_BUF 进行刷盘 ===> 记录日志:修改…… 并刷盘
SQL缓冲区:
SQL CACHE POOL,简称 SCP,对应 INI 参数 CACHE_POOL_SIZE,是用来存储包信息(PACKAGE)、执行计划、结果集缓存的一片专用缓存区域。
对于SQL类别比较多,或者 PKG 比较多、复杂的系统,建议将该参数调大。
对应的参数 CACHE_POOL_SIZE,默认的 TARGET 大小是配置大小的 3 倍。例如:CACHE_POOL_SIZE = 2G,那么 TARGET 大小就是 6G,不宜配置过大。
字典缓冲区:
字典缓冲区是存在数据库对象的一片缓冲区,对应 INI 参数 DICT_CACHE_SIZE,DM 里面数据对象其实对应的是系统表上的一些信息,内存中的数据对象是通过将系统表上的信息取出并解析出来得到的。
该缓冲区一是避免了频繁向磁盘请求获取系统表信息,二是可以减少系统表信息解析开销,在数据对象较多(比如存在非常多分区很多的表)时建议放大。
V$DB_CACHE ===> LRU_DISCARD(由于缓存池已满导致字典对象被淘汰的次数) ===> 如果有则说明 DICT_CACHE_SIZE 大小不够,需要调大。
(2)内存池
主内存池:
服务器启动时从操作系统申请的一大片内存,后续服务器运行过程中,一般情况下,很多需要内存分配的地方都是从该池分配。
如果需要的内存大于配置值(MEM_POOL),该池也会自动扩展,一般情况下不收缩,最大扩展到 MAX_OS_MEMORY 大小。
MEMORY_POOL:申请一大片内存,留作一些情况下进行使用,部分操作会直接从主内存池拿内存。最大的使用情况是当其他内存池扩展时超过 TARGET 之后,其扩展内存来源均为 MEMORY_POOL。
其他运行时内存池:
服务器运行过程中,内存的使用有两种模式,一种是直接从内存池申请需要的内存大小,另外一种方式是从操作系统申请一大片内存来做成自己模块的内存池使用(VM_POOL、SESS_POOL、RT_HEAP、INS_POOL 等等)。这样可以减少频繁向数据库主内存池申请内存的开销。
一般来说一个会话可以理解为一个单独的运行环境,有自己的私有内存池(HASH SORT 等操作都是从自己的私有池上申请内存)。
部分少量从主内存池申请,另外还有部分线程或者子系统也拥有自己独立的内存池,如 CKPT 刷盘线程有 CKPT_POOL,序列管理有 NSEQ_POOL。
内存池初始的大小来源都是操作系统,初始大小以及扩展到 TARGET 之前都是操作系统的内存。
(3)其他内存区:
排序区:
SQL 运行过程中,如果存在排序、分区、去重等操作,会使用到排序区内存,排序分为内排序和外排序两种。
内排序指所有排序可以在内存中完成,外排序指的是需要排序的数据不能完全放入内存,这时则需要将内存中的部分数据刷到磁盘上,最终再进行归并完成整个排序。
对于内排序,仅受参数 SORT_BUF_SIZE 影响,但是要注意的是,每个会话在执行 SORT 时都会根据 SORT_BUF_SIZE 申请内存,此参数不宜配置过大。
对于外排序,其具体大小受 SORT_BUF_SIZE 影响,内存放不下的部分根据配置有三种处理方式,MTAB(普通临时表)、FTAB(临时文件表)、MMT(内存映射表)三种方式。
一些参数:
- SORT_FLAG
- SORT_BLK_SIZE
- SORT_BUF_SIZE
- SORT_BUF_GLOBAL_SIZE
- SORT_ADAPTIVE_FLAG
HASH 区:
服务器中 HASH 相关的操作需要用到 HASH 相关的内存片,实际上服务器上是没有单独申请专门的内存给 HASH 使用的,HASH 操作使用的还是运行时内存池或者内存堆。
HASH 相关的操作主要有 HASH JOIN、HASH GROUP BY(哈希分组)。相关的 INI 参数主要有:
- HJ_BUF_GLOBAL_SIZE:全局 HASH JOIN 最大内存空间
- HJ_BUF_SIZE:单次 HASH JOIN 操作最大内存空间
- HJ_BLK_SIZE:单次 HASH JOIN 操作中每次申请的内存空间
- HAGR_BUF_GLOBAL_SIZE:全局 HASH GROUP BY 最大内存空间
- HAGR_BUF_SIZE:单次 HASH GROUP BY 操作最大内存空间
- HAGR_BLK_SIZE:单次 HASH GROUP BY 操作中每次申请的内存空间
- HAGR_HASH_SIZE:HASH GROUP BY 操作 HASH 表创建时的桶数
- JOIN_HASH_SIZE:HASH JOIN 等操作初始化时 HASH 表的桶数
HASH JOIN 和 HASH GROUP BY 类似,这里以 HASH JOIN 为例说明:
一个 HASH JOIN 准备执行时,首先根据左表创建 JOIN_HASH_SIZE 桶数的 HASH 表。后续每次需要分配内存时,分配 HASH_BLK_SIZE 大小的内存,直至 HASH_BUF_SIZE 大小,之后放不下的部分类似 SORT 操作,以各种方式缓存在磁盘/内存中,再等待后续操作进行合并。值得注意的 JOIN_HASH_SIZE、HAGR_HASH_SIZE 两个操作,初始化 HASH 表桶数越多,查询时的 HASH 冲突可能越少,但是太大的桶数可能操作 HASH 初始化操作缓慢(存在内存清零操作)。所以需要测试调整设置适当的值来平衡性能和稳定。
推荐值:
HJ_BLK_SIZE:1~5MB
HJ_BUF_SIZE:200MB~2GB
HJ_BUF_GLOBAL_SIZE:总内存*10%
注意
MAX_OS_MEMORY:早起遗留参数,原本不可用,但是目前已对其进行了补充完善,可用于控制内存总大小。配置后维护一个全局变量,全局变量就是当前使用的内存大小。
*操作系统内存相关参数
除数据库本身的参数外,操作系统中存在内存相关参数可能影响数据库性能等,这里注意关注两个参数。
- Ulimit 中的 stack size
- 该参数表示操作系统为每个线程预备的栈空间,由于 DM 是多线程机制,每个会话都是一个线程,如果该参数太大,可能导致内存不足或者线程创建失败,一般设置为8192或者更小即可。
- overcommit_memory
- 该参数是控制分配内存是否可以超过 CommitLimit,默认是 0 ,即启发式的 overcommitting handle,会尽量减少 swap 的使用,root 可以分配比一般用户略多的内存。
- 1 表示允许超过 CommitLimit,2 表示不允许超过 CommitLimit。
- CommitLimit = Swap 大小 + overcommit_ratio * 物理内存大小(可以通过 grep -i commit /proc/meminfo 查看)
- 如果操作系统该参数设置为 2 ,在此时操作系统分配内存的方案为:Swap 大小 + overcommit_ratio * 物理内存大小。
- 其中 overcommit_ratio 值取自 /proc/sys/vm/overcommit_ratio,这是 OS 所能用的最大内存。
- 如果出现了操作系统内存充足但数据库反馈申请内存失败的情况,检查该参数,如果不为 0 则将 /proc/sys/vm/overcommit_memory 参数修改为 0 ,这也是大部分操作系统的默认取值。
二、内存信息的查看
V$MEM_POOL:
详细地记录了当前系统中所有内存池的状态,可通过查询这个动态视图掌握 DM Server 的内存使用情况。
主要关注字段:NAME、ORG_SIZE、TOTAL_SIZE、RESERVED_SIZE、DATA_SIZE、N_EXTEND_NORMAL、N_EXTEND_EXCLUSIVE、CREATOR(映射到 V$SESSIONS.THRD_ID 再映射到操作系统中的 PID)
| 序号 | 列 | 数据类型 | 说明 |
| 1 | ADDR | BIGINT | 内存结构地址 |
| 2 | NAME | VARCHAR(128) | 内存池名称 |
| 3 | IS_SHARED | CHAR(1) | 是否是共享的 |
| 4 | CHK_MAGIC | CHAR(1) | 是否打开了内存校验 |
| 5 | CHK_LEAK | CHAR(1) | 是否打开了泄漏检查 |
| 6 | IS_OVERFLOW | CHAR(1) | 是否已经触发BAK_POOL的分配 |
| 7 | IS_DSA_ITEM | CHAR(1) | 是否是DSA(DamengShareArea)项目,目前一律为N |
| 8 | ORG_SIZE | BIGINT | 初始大小,单位为字节数 |
| 9 | TOTAL_SIZE | BIGINT | 当前总大小,单位为字节数 |
| 10 | RESERVED_SIZE | BIGINT | 当前分配出去的大小,单位为字节数 |
| 11 | DATA_SIZE | BIGINT | 当前分配出去的数据占用大小,单位为字节数 |
| 12 | EXTEND_SIZE | BIGINT | 每次扩展的块大小,单位为字节数 |
| 13 | TARGET_SIZE | BIGINT | 可以扩展到的大小,单位为字节数。 当TARGET_SIZE为0时,不限制此内存池的扩展; 对于共享内存池,即使TARGET_SIZE不为0,也不限制其扩展,TARGET_SIZE用于提示系统尽快把内存占用释放到TARGET_SIZE以下 |
| 14 | EXTEND_LEN | INTEGER | 扩展链长度 |
| 15 | N_ALLOC | INTEGER | 累计分配了几次 |
| 16 | N_EXTEND_NORMAL | INTEGER | TARGET范围内累计扩展次数 |
| 17 | N_EXTEND_EXCLUSIVE | INTEGER | 超过TARGET累计扩展次数 |
| 18 | N_FREE | INTEGER | 累计释放次数 |
| 19 | MAX_EXTEND_SIZE | BIGINT | 保留字段,始终返回NULL |
| 20 | MIN_EXTEND_SIZE | BIGINT | 保留字段,始终返回NULL |
| 21 | FILE_NAME | VARCHAR(256) | 本池创建点所在的源文件名 |
| 22 | FILE_LINE | INTEGER | 创建点所在的代码行 |
| 23 | CREATOR | INTEGER | 创建者线程号 |
| 24 | EXTEND_MODE | CHAR(1) | 内存池的扩展模式,O:扩展时从操作系统(OS)申请扩展块;S:扩展时从共享池(SharePool)申请扩展块 |
*MAX_SESSION_MEMORY:单个会话的运行内存上限,单位 MB,取值范围 0~262144,0 表示不限制
V$MEM_REGINFO:
显示系统当前已分配并未释放的内存信息,当 MEMORY_LEAK_CHECK 为 1 时才会在此动态视图注册信息。
| 序号 | 列 | 数据类型 | 说明 |
| 1 | POOL | VARCHAR(128) | 注册项来源内存池名称 |
| 2 | FNO | INTEGER | 文件编号 |
| 3 | LINENO | INTEGER | 文件中的代码行编号 |
| 4 | REFNUM | INTEGER | 引用次数 |
| 5 | RESERVED_SIZE | BIGINT | 调用点累计分配未释放的内存量,单位为字节数 |
| 6 | DATA_SIZE | BIGINT | 调用点累计分配未释放的净数据量,单位为字节数 |
| 7 | FNAME | VARCHAR(256) | 源文件名 |
| 8 | ADDR | BIGINT | POOL的内存地址 |
备注:老版本是 V$MEM_REGINFO 一般与 V$MEM_FNAME 做联合查询,V$MEM_FNAME 存放 DM 内部所有源码文件名以及编号信息也就是现在的第 7 列 FNAME。
V$CACHEITEM:
显示缓冲区中缓冲项的相关信息。在 ini 参数 USE_PLN_POOL !=0 时才统计。记录当前 SQL CACHE POOL(SCP) 的使用情况。
| 序号 | 列 | 数据类型 | 说明 |
| 1 | ADDRESS | BIGINT | CACHE项的地址 |
| 2 | TYPE$ | VARCHAR(8188) | CACHE项的类型:SQL,计划(PLN),结果集(RS),包信息(PKG_INFO) |
| 3 | OVERFLOW | CHAR(1) | 是否溢出 |
| 4 | IN_POOL | CHAR(1) | 是否在内存池中 |
| 5 | DISABLED | CHAR(1) | 是否可用 |
| 6 | N_FIXED | INTEGER | 此缓冲项被引用的次数 |
| 7 | TS_VALUE | INTEGER | 时间戳 |
| 8 | ITEM_SIZE | BIGINT | 缓存节点大小(字节数) |
| 9 | N_HIT | INTEGER | 节点命中次数 |
| 10 | N_DIS_FIXED | INTEGER | 内部保留字段 |
一个常用的查询语句如下:
SELECT SUM(ITEM_SIZE) FROM V$CACHEITEM GROUP BY TYPE$;
来计算 SQL CACHE POOL 的大小
或直接
SELECT * FROM V$MEM_POOL WHERE UPPER(NAME) LIKE 'SQL%';
V$DICT_CACHE:
记录当前字典缓冲区的使用情况。
| 序号 | 列 | 数据类型 | 说明 |
| 1 | ADDR | VARCHAR(20) | 地址 |
| 2 | POOL_ID | INTEGER | 缓冲区ID |
| 3 | TOTAL_SIZE | BIGINT | 总大小,单位BYTE |
| 4 | USED_SIZE | BIGINT | 实际使用大小,单位BYTE |
| 5 | DICT_NUM | INTEGER | 字典对象总数 |
这个目前没什么参考价值,而是查询如下视图 V$DB_CACHE。
V$DB_CACHE:
数据字典缓存表,用于记录数据字典的实时信息。
主要关注字段: LRU_DISCARD (由于缓存池已满导致字典对象被淘汰的次数) ===> 如果有则说明 DICT_CACHE_SIZE 大小不够,需要调大。
| 序号 | 列 | 数据类型 | 说明 |
| 1 | DB_ADDR | VARBINARY(8) | 数据字典地址 |
| 2 | POOL_ID | INTEGER | 缓存池ID |
| 3 | TOTAL_SIZE | BIGINT | 缓存池总空间,单位字节 |
| 4 | USED_SIZE | BIGINT | 实际使用的空间,单位字节 |
| 5 | DICT_NUM | INTEGER | 缓存池中字典对象的总数 |
| 6 | SIZE_LRU_DISCARD | BIGINT | 由于缓存池已满导致字典对象被淘汰的空间的总和,单位字节 |
| 7 | LRU_DISCARD | INTEGER | 由于缓存池已满导致字典对象被淘汰的次数 |
| 8 | DDL_DISCARD | INTEGER | DDL操作导致字典对象被淘汰的次数 |
| 9 | DISABLED_SIZE | BIGINT | 被淘汰字典对象的空间,单位字节 |
| 10 | DISABLED_DICT_NUM | INTEGER | 缓存池中被淘汰字典对象的总数 |
V$BUFFERPOOL:
记录了当前数据缓冲区的使用情况。
主要关注字段: RAT_HIT
| 序号 | 列 | 数据类型 | 说明 |
| 1 | ID | INTEGER | 缓冲区ID |
| 2 | NAME | VARCHAR(20) | 缓冲区名称NORMAL/KEEP/RECYCLE/FAST |
| 3 | PAGE_SIZE | INTEGER | 基缓冲区页大小,不包括扩展池页,单位BYTE |
| 4 | N_PAGES | INTEGER | 页数 |
| 5 | N_FIXED | INTEGER | 数据页被引用的次数 |
| 6 | FREE | INTEGER | 空闲页数目 |
| 7 | N_DIRTY | INTEGER | 脏页数目 |
| 8 | N_CLEAR | INTEGER | 非空闲页数目 |
| 9 | N_TOTAL_PAGES | INTEGER | 页总数,包括扩展池页 |
| 10 | N_MAX_PAGES | INTEGER | 最多的页数 |
| 11 | N_LOGIC_READS | BIGINT | READ命中的次数 |
| 12 | N_DISCARD | BIGINT | 淘汰的页数 |
| 13 | N_PHY_READS | BIGINT | READ未命中的次数 |
| 14 | N_PHY_M_READS | BIGINT | READ未命中,批量读的次数 |
| 15 | RAT_HIT | DOUBLE | 命中率 |
| 16 | N_EXP_BUFFERPOOL | INTEGER | 扩展缓冲区个数 |
| 17 | N_UPD_REMOVE | BIGINT | 从update链表删除页总数 |
| 18 | N_PHY_WRITE | BIGINT | 物理写入磁盘总次数 |
| 19 | N_UPD_PUT | BIGINT | DSC远程读取数据后,加入update链表总数 |
| 20 | N_UPD_SEARCH | BIGINT | DSC远程读取数据后,查找update链表插入位置扫描总数 |
| 21 | N_DISCARD64 | BIGINT | 淘汰的页数 |
| 22 | N_PHY_READS64 | BIGINT | READ未命中的次数 |
| 23 | N_PHY_M_READS64 | BIGINT | READ未命中,批量读的次数 |
| 24 | N_UPD_REMOVE64 | BIGINT | 从update链表删除页总数 |
| 25 | N_PHY_WRITE64 | BIGINT | 物理写入磁盘总数 |
| 26 | N_UPD_PUT64 | BIGINT | DSC远程读取数据后,加入update链表总数 |
| 27 | N_UPD_SEARCH64 | BIGINT | DSC远程读取数据后,查找update链表插入位置扫描总数 |
| 28 | N_BUFMEM_ALLOCED | INTEGER | 已分配出去的缓冲区页数 |
| 29 | N_PTX_BI_ALLOCED | INTEGER | 分配给PTX前镜像页的缓冲区页数 |
特别是对于 NORMAL 来说,如果命中率(RAT_HIT)低于 90% 则说明 BUFFER 设置的太小,甚至是内存太小,不足以放下经常使用的文件。需要考虑将 BUFFER 扩大,或者调整 SQL 减少大表的全表扫描。
另外通过 SQL 可以获取到当前数据缓冲区总共占用了多少内存:
SELECT SUM(N_PAGE * PAGE_SIZE) FROM V$BUFFERPOOL;
进阶
查看数据缓冲区和所有内存池
SELECT
(SELECT SUM(CAST(N_PAGES AS BIGINT)* PAGE_SIZE)/1024/1024 FROM V$BUFFERPOOL)||' MB' AS BUFFER_SIZE,
(SELECT SUM(CAST(TOTAL_SIZE AS BIGINT))/1024/1024 FROM V$MEM_POOL)||' MB' AS MEM_POOL,
(SELECT SUM(CAST(N_PAGES AS BIGINT) * PAGE_SIZE)/1024/1024 FROM V$BUFFERPOOL)+(SELECT SUM(CAST(TOTAL_SIZE AS BIGINT))/1024/1024 FROM V$MEM_POOL)||' MB' AS TOTAL_SIZE
FROM DUAL;
三、内存信息相关INI参数
dm.ini 中与内存相关的配置,在调优过程中如果碰到类似内存相关引发的性能问题,可以调整相关参数是否可以提升性能。
| 参数名 | 说明 |
| MEMORY_POOL | 内存主池大小 |
| MEMORY_SHARE_TARGET | 内存主池的计划大小,超出该值的部分释放时直接返回给操作系统 |
| MEMORY_EXTENT_SIZE | 内存池每次扩展的大小 |
| MEMORY_LEAK_CHECK | 是否开启内存泄漏检测,开启后会一定程度的影响数据库性能 |
| MEMORY_MAGIC_CHECK | 是否开启内存写覆盖检测,开启后会一定程度的影响数据库性能 |
| MEMORY_BAK_POOL | 备用池大小,当从操作系统申请内存失败时从该池申请内存 |
| HUGE_MEMORY_THRESHOLD | 单次申请内存超过该值时,从 HUGE BUF 申请内存,该参数已作废 |
| HUGE_MEMORY_PERCENTAGE | HUGE BUF 占内存的比例 |
| HUGE_BUFFER | HUGE 表使用的内存缓冲区大小 |
| BUFFER | 主数据缓冲池初始大小 |
| BUFFER_POOLS | 主数据缓冲池个数,由于主数据缓冲池存在淘汰,进入时必须通过临界区,配置多个缓冲池有助于减少并发冲突【质数】 |
| FAST_POOL_PAGES | 常驻的数据缓冲池页数,该池不存在淘汰,进入退出不需要进过临界区,常用的页会被自动加载到该池中 |
| FAST_ROLL_PAGES | 常驻的回滚页数据缓冲池 |
| KEEP | KEEP 缓冲区大小 |
| RECYCLE | 临时数据页缓冲池,临时表等操作会引用到该池 |
| RECYCLE_POOLS | 临时数据页缓冲池个数,配置多个有助于减少临时表相关操作的并发冲突 |
| MULTI_PAGE_GET_NUM | 单次抓取数据缓冲页数,很多时候需要的数据是分布在连续的页上的,配置该参数有助于减少 IO 次数从而提升性能 |
| MAX_BUFFER | 主数据缓冲池的最大大小 【已废弃】 |
| SORT_BUF_SIZE | 排序缓冲区大小,该参数影响排序时的内存消耗,但该参数是会话级的,也就是每个会话上面有排序操作时都会去申请,不宜配置过大 |
| HAGR_HASH_SIZE | HASH 分组时,HASH 表的桶数,HASH 分组过程中,桶太少会导致多个值的 HASH 值一样,造成 HASH 冲突,过多会消耗无意义的内存,需要谨慎配置 |
| HJ_BUF_GLOBAL_SIZE | HASH JOIN 全局内存大小,所有会话使用在 HASH JOIN 上的内存之和不能超过该值 |
| HJ_BUF_SIZE | 单个语句所能使用的最大的 HASH JOIN 内存大小,如果单个 HASH JOIN 使用的内存大小超过该值会报错,需要调整参数或者优化 SQL |
| HJ_BLK_SIZE | HASH JOIN 过程中,每次申请的内存大小,可以计算参与 HASH 的数据大小来 HINT 适当的 BLK 值,减少分配次数 |
| HAGR_BUF_GLOBAL_SIZE | HASH 分组全局内存大小,所有会话使用在 HASH 分组上的内存之和不能超过该值 |
| HAGR_BUF_SIZE | 单个语句所能使用的最大的 HASH 分组内存大小 |
| HAGR_BLK_SIZE | HASH 分组过程中,每次申请的内存大小 |
| MTAB_MEM_SIZE | 临时表,内部临时视图等的内存数据块占用内存的大小 |
| FTAB_MEM_SIZE | MTAB 大于该值时转换成 FTAB |
| DICT_BUF_SIZE | 字典缓存池的大小 |
| HFS_CACHE_SIZE | HUGE 表 DML 操作中,数据使用的缓冲区大小 |
| VM_STACK_SIZE | 系统执行时虚拟机堆栈大小 |
| VM_POOL_SIZE | VM 自带内存池大小 |
| VM_POOL_TARGET | VM 自带内存池计划内大小 |
| SESS_POOL_SIZE | 会话自带内存池大小 |
| SESS_POOL_TARGET | 会话自带内存池计划内大小 |
| VM_MEM_HEAP | 是否采用堆的方式管理虚拟机运行时内存,否则使用直接在 VM 上创建内存池的方式【tpcc测试开启】 |
| VM_SQL_TREE_CACHE_SIZE | 根据执行计划生成的实际执行树是否缓存 【已废弃】 |
| RFIL_RECV_BUF_SIZE | 启动恢复过程中 REDO 日志缓存的大小,调大有助于提高启动速度 |
| N_MEM_POOLS | 【已废弃】 |
| COLDATA_POOL_SIZE | 【已废弃】 |
| HAGR_DISTINCT_HASH_TABLE_SIZE | 利用 HASH 分组做 DISTINCT 操作时,创建的 HASH 表大小,桶个数的影响参见 HAGR_HASH_SIZE |
| CNNTB_HASH_TABLE_SIZE | CNNT BY 操作时创建的 HASH 表桶数 |
| GLOBAL_RTREE_BUF_SIZE | 空间索引所有 RTREE 结构占用的缓存大小限制 |
| SINGLE_RTREE_BUF_SIZE | 空间索引单个 RTREE 结构占用的缓存大小限制 |
| USE_DHASH_FLAG | 是否使用动态 HASH。0:不使用;1、2:使用 DHASH2;3:使用 DHASH3,使用动态 HASH 时推荐选择 3【配置 3 时会自适应的进行 HASH 桶的分配 生产先不要使用】 |
四、内存泄漏/异常的检查
如果发现内存突然飙升,或者占用过大,或者操作系统反复 OOM 时,需要考虑是否是服务器存在内存泄漏问题,按照目前动态视图所能提供的信息,需要获取以下内容:
- 数据库的内存使用情况
- 是否存在扩展到非常大的内存池
- 确认该内存的详细使用情况
SQL> SELECT * FROM V$SYSSTAT WHERE CLASSID = 11;
行号 ID CLASSID NAME STAT_VAL
---------- ----------- ----------- -------------------------------- --------------------
1 110 11 bytes allocated from os 3038236813
2 111 11 memory pool size in bytes 952500096
3 112 11 memory pool used bytes 196760168
4 113 11 memory pool extended from os 169803776
5 114 11 hj buffer total used(MB) 0
6 115 11 hj buffer merge used(MB) 0
7 116 11 hagr buffer used(MB) 0
8 117 11 package info cache used in bytes 0
9 131 11 huge buffer total pages 20480
10 132 11 huge buffer free pages 51200
11 138 11 huge buffer mem use pages 0
12 139 11 huge buffer mem alloc count 0
13 140 11 huge buffer mem free count 0
14 141 11 huge buffer reserve count 0
15 177 11 sort buf global total size(MB) 1000
16 178 11 sort buf global used size(MB) 0
主要关注字段:
- memory pool size in bytes
- memory pool used bytes
- memory pool extended from os
- hj buffer total used(MB)
- hagr buffer used(MB)
- sort buf global used size(MB)
操作系统级别的命令:top 和 Windows 资源管理器,查看数据占用多少内存
数据库级别的判断:数据库使用的内存大致等于 BUFFER_SIZE + POOL_SIZE,对应的 SQL 语句为:
SELECT ((SELECT SUM(N_PAGES * PAGE_SIZE) FROM V$BUFFERPOOL) + (SELECT SUM(TOTAL_SIZE) FROM V$MEM_POOL))/1024/1024 || ' MB' FROM DUAL;
进阶
SELECT
(SELECT SUM(TOTAL_SIZE)/1024/1024 || ' MB' FROM V$MEM_POOL) AS MEM_POOL,
(SELECT SUM(N_PAGES * PAGE_SIZE)/1024/1024 || ' MB' FROM V$BUFFERPOOL) AS BUFFER_SIZE,
(SELECT ((SELECT SUM(N_PAGES * PAGE_SIZE) FROM V$BUFFERPOOL) + (SELECT SUM(TOTAL_SIZE) FROM V$MEM_POOL))/1024/1024 || ' MB') AS TOTAL_SIZE
FROM DUAL;
用CAST
SELECT
(SELECT SUM(TOTAL_SIZE)/1024/1024 FROM V$MEM_POOL)||' MB' AS MEM_POOL,
(SELECT SUM(N_PAGES * PAGE_SIZE)/1024/1024 FROM V$BUFFERPOOL)||' MB' AS BUFFER_SIZE,
(SELECT ((SELECT SUM(N_PAGES * PAGE_SIZE) FROM V$BUFFERPOOL) + (SELECT SUM(TOTAL_SIZE) FROM V$MEM_POOL))/1024/1024)||' MB' AS TOTAL_SIZE
FROM DUAL;
一般来说,发生内存泄漏的都在 MEM_POOL 上,比如操作系统上看到内存占用 60G ,但 BUFFER 已经占用到 50G ,实际上发生泄露的概率较小,应该适当调小 BUFFER,空出内存给内存池使用。
是否存在使用内存过多的 SQL?
相关系统视图:
- V$SESSION_STAT
- V$SQL_STAT
- V$SQL_STAT_HISTORY
视图上字段比较丰富,主要关注字段:MAX_MEM_USED
一般来讲,如果是某条或者特定的几条 SQL 导致内存增长过多,可以通过这两个视图查询出来
SELECT MAX_MEM_USED, SQL_TEXT FROM V$SQL_STAT ORDER BY MAX_MEM_USED DESC;
可以确定使用内存较大的 SQL,可以针对行的优化(消除 HASH, SORT, DISTINCT 等)
注意:该查询只能查询当前活动 STMT 上的语句消耗情况,历史情况需要查询 V$SQL_STAT_HISTORY ,该视图上保留一万行 SQLSTAT 历史信息。以上 MAX_MEM_USED 列的单位均为 KB。
是否存在扩展过大的内存池?
前面介绍过,正常使用过程中,内存池的大小一般会维持在 TARGET_SIZE 左右,不会发生太大的差距,如果发生非常多次计划外的扩展,需要考虑该池是否发生内存泄漏,与之相关的系统视图和系统视图字段为:V$MEM_POOL
主要关注字段:N_EXTEND_EXCLUSIVE
相应的查询语句为:
SELECT * FROM V$MEM_POOL WHERE N_EXTEND_EXCLUSIVE > 0;
确认池后,通过 V$MEM_POOL 的 CREATOR 也就是创建这个池的线程号去 V$SESSIONS 或者 V$SESSIONS_HISTORY 里面确认会话号,然后再去 V$SQL_STAT_HISTORY 中寻找内存消耗大的 SQL 即可。
查看存在超出计划大小扩展的内存池的计划外扩展次数以及大小,如果池大小为可以通过 INI 调整的(如 VM_POOL_TARGET, SESS_POOL_TARGET, 但这两个参数慎用,调整过大对并发影响较大)可以适当放大计划大小,再运行观察计划外扩展情况是否有改善,如果仍然存在大量的计划外扩展则需要继续跟进内存使用实况。
确认内存的详细使用情况
详细的内存使用情况需要视图 V$MEM_REGINFO,该视图在 INI 参数 MEM_LEAK_CHECK 为 1 时有效,可动态开启,开启后登记数据库中所有内存的申请释放信息。
查询:
SELECT SUM(RESERVED_SIZE), FNAME, LINENO FROM V$MEM_REGINFO GROUP BY FNAME, LINENO ORDER BY SUM(RESERVED_SIZE) DESC;
按大小降序,或者根据文件名、行号分组,查看是否存在大量某文件某行使用内存未释放的情况,转交研发处理。
额外关注字段:REFNUM
REFNUM 引用次数,如果次数很多需要关注。
大致思路:REFNUM ===> POOL ===> CREATOR ===> SESSION ===> SQL
排查内存泄露的 SQL 思路:
大致思路是打开 MEM_LEAK_CHECK 之后,通过 V$MEM_REGINFO 确认堆积内存最多的文件和行号,或者通过 REFNUM 找到引用最多的内存,从中取到 POOL,再通过 V$MEM_POOL 找到 THRD_ID,进而确认 SQL。
另外可以结合 V$SQL_STAT/V$SQL_STAT_HISTORY 来寻找使用大量内存的语句。
讲句好笑的,阿里云dns云解析网页版白版以后内存泄露,动都动不了超级卡
直接重启大法