oracle:Per-Process PGA memory limit

前几日,东区某客户的19c rac出现了ORA-04030,从报错的trace来看,使用了32g的PGA,对于单进程内存上限众说纷纭,有很多不同意见,有些说2g、有些说4g。。。,本篇文章就深入分析一下oracle进程内存上限。

说到PGA不得不关联到UGA和CGA,下面参考个人觉得写得很好的blog,对PGA、UGA、CGA进行了详细的概述。

https://blog.titanwolf.in/a?ID=01450-1639cbbd-7123-4e8c-afe0-4d8899dd8951

The Process Global Area (PGA) can be understood as either Process Global Area or Program Global Area. Its memory segment is in the Process Private Memory instead of Shared Memory. It is a global area It means that it contains all the global variables and data structures that the code may enter, but it is not shared by all processes. Each Oracle server process contains its own PGA, which only contains the specific related to the process Information. The structure in the PGA does not need to be protected by latches, because other processes cannot enter it to access it.

The PGA contains information about the operating system resources that the process is using and the state of the process, while the Oracle shared resources used by other processes are in the SGA. PGA is private rather than shared, this mechanism is necessary , Because these resources can be cleared and released when the process dies.

PGA contains two main areas: Fixed PGA and Variable PGA or PGA Heap.The role of Fixed PGA is similar to Fixed SGA, and both contain atomic variables (inseparable), small data structures and pointers to Variable PGA.

Variable PGA is a heap. Its Chunks can be viewed from the Fixed Table X$KSMPP. The structure of this table is the same as the X$KSMSP mentioned earlier. PGA HEAP contains some permanent memory related to the Fixed Table. It It is dependent on the settings of some parameters. These parameters include DB_FILES, LOG_FILES, CONTROL_FILES.

UGA (User Global Area) contains information about a specific session, including the following:

  • The duration and runtime area of ​​the opened cursor
  • Package status information, specific variables
  • Java session state
  • Available ROLES
  • Tracking events that are ENABLE
  • Effective NLS parameter settings
  • Open DBLINK
  • Conversation entry control

Like PGA, UGA is also composed of two areas: Fixed UGA and Variable UGA, also known as UGA HEAP.Fixed UGA contains about 70 atomic variables, small data structures and pointers to Variable UGA.

Chunks in UGA HEAP can obtain relevant information from their own sessions by looking at the table X$KSMUP. The structure of this table is the same as X$KSMSP. UGA HEAP contains some permanent memory segments related to fixed tables, and some The settings of the parameters are dependent. These parameters are OPEN_CURSORS, OPEN_LINKS, and MAX_ENABLE_ROLES.

The location of UGA in memory depends on the configuration mode of the session. If the configuration mode of the session connection is dedicated server mode (DDS), that is, a session corresponds to a process, UGA is placed in the PGA. In the PGA, Fixed UGA is One of them is a Chunk, and UGA HEAP is a subheap of PGA. If the session connection is configured as shared server mode (MTS), Fixed UGA is a Chunk in the SHARED POOL, and UGA HEAP is in the SHARED POOL Subheap

Unlike other global areas, the Call Global Area exists temporarily. It only exists during the call to the data, generally when the lowest-level call to the instance requires CGA, as follows:

  • Analyze a SQL statement
  • Execute a SQL statement
  • To take out the output of a SELECT statement,

a single CGA is needed for recursive calls. During the analysis of the SQL statement, the recursive call to the data dictionary information is needed, because the SQL statement is grammatically analyzed, and the statement During the optimization period, the execution plan must be calculated. When the PL/SQL block is executed, the execution of the SQL statement is also required to be recursively called, and the trigger execution is also required to be processed when the DML statement is executed.

Regardless of whether UGA is placed in the PGA or in the SGA, CGA is a subheap of the PGA. An important inference of this fact is that the session must be a process during a call. For an Oracle database process in an MTS Understanding this point during application development is very important. If there are more calls, the number of processes must be increased to accommodate the increase in calls.

Without the data structure in CGA, CALLS cannot work. In fact, the data structure related to a CALL is generally placed in UGA, such as SQL AREA, PL/SQL AREA and SORT AREA, they must all be in UGA. Because they must always exist and be available between the CALLS. The data structure contained in the CGA must be released after a CALL. For example, the CGA contains information about recursive calls, direct I/O BUFFER, etc. There are other temporary data structures.

对于PGA上限值,首先需要说明的是“_smm_max_size”和“_pga_max_size”非常具有迷惑性,它们仅仅与PGA自动管理时的workarea部分相关。同时 _pga_max_size和 _smm_max_size 的值与pga_aggregate_target也关系密切。

对于UGA和CGA的内存分配,oracle在9.2开始,使用Real-Free Memory Allocator,这样的好处是使用完成之后可以立即释放给操作系统,在11g中, Real-Free Memory Allocator 的分配机制是每次分配参数“_realfree_heap_pagesize_hint”的page size,默认为65536。在12c以上版本参数名字改为了“ _realfree_heap_pagesize ”

通过strace也可以验证每次分配内存都是使用 “_realfree_heap_pagesize_hint” 大小

每次分配进程都会持有一个虚拟内存的map,而每个进程能持有的最大map数量由vm. max_map_count 控制,默认为65530

所以在11g当中,所有参数默认的情况下,进程能持有的最大内存是 max_map_count * _realfree_heap_pagesize_hint =4G。

但是如果不使用 Real-Free Memory Allocator 去分配内存的话,即“_use_realfree_heap”为false时,就与“ _realfree_heap_pagesize_hint ”无关了,此时每次分配的内存依然一致,是以“ _uga_cga_large_extent_size ”为大小去分配,默认为 262144 。

同样通过strace跟踪可以验证,这样的话理论上的内存上限为 max_map_count * _uga_cga_large_extent_size =16G,但是这一点我不敢确认,因为我的测试环境内存太小无法验证。

回到案例,那么为什么在19c中,所有参数都是默认的情况下,PGA内存达到了32G之后才报出ORA-04030呢?我们看看报错的trace文件

可以看到 blksz还是65536,但是PGA确实达到了32G之后报出了ORA-04030,搜索Process Map Dump可以看到仅仅使用了四百多个map就达到了32G的内存,充分说明每次分配的大小并不是65536。

查看具体的map信息发现,有些map映射的虚拟内存空间非常大达到了 256M。这是怎么回事呢?

通过strace跟踪发现,19c的Real-Free Memory Allocator采用了一种动态分配的方式,有点类似data extent的auto管理方式。不确定12c是否是这种方式没有去验证。

可以看到初始分配大小仍然采用“ _realfree_heap_pagesize”进行分配,分配到大概50次之后开始进行调整,调整粒度是乘以2,一直递增下去,最大应该是能达到256M,这也是为什么19c的进程使用了如此少的map内存就能达到32g的原因,那么19c的内存上限与vm.map_max_count的设置就没有关系了,因为根本不需要65530那么多的map,进程内存就可以达到32g的进程内存上限。

那么19c PGA的内存上限由什么决定呢?从trace里面看个人猜测应该是 blksz * maxblk ,blksz就是参数” _realfree_heap_pagesize”,而 maxblk 并没有找到出处,从11g-19c的maxblk都是524288,应该是代码层面写死的,11g之所以只能达到4G的上限是因为统一分配的内存大小受到了vm.map_max_count(65530)的限制。而19c是动态分配的大小,并且都是” _realfree_heap_pagesize ”的整数倍,当 iniblk= maxblk 时,就会达到内存上限。

那么19c中,如何避免PGA使用如此32G大内存呢?

  • resource manager 设置session_pga_limit
  • event 10261

oracle online系列(三):online move table

在12c之前,move table属于一个离线操作,由于TM锁不兼容会阻塞dml操作,并且move之后表上索引会变成unusable,这对于7*24小时的业务场景是不可接受的,如果想实现online move table必须要使用在线重定义来完成。而在线重定义操作步骤还是相对比较繁琐的。

为了简化online move table功能,oracle在12.1推出了move table partitions和sub-partitions online功能,并且在12.2引入了online move table去替代12c之前繁琐的在线重定义,但并不是在线重定义的所有使用场景都能替代,毕竟在线重定义功能非常强大,使用场景也非常多,比如:在线修改字段名、在线增删字段、按某个字段列排序重组表(多半是为了降低某些索引的聚簇因子)等等,online move table就无法实现。

老样子,本文也是通过测试验证来深入解析oracle是如何实现online move table的。

测试环境与对象:

第一步发起online move命令表上会立刻持有2号TM锁,之后调用ctcsoo_setup_online_op开始online操作会将表锁模式改为3号TM锁。

第二步调用ctccjt_create_journal_table函数创建JOURNAL表, JOURNAL表是一个IOT表,之后调用kkzuRmtCreate函数创建一个RMTAB_H表,该表是一个堆表,创建完成两个内部表之后,开始move表数据,并且产生表的临时段。

其中 JOURNAL表用于记录表的dml变更记录, RMTAB_H表用于记录原表与目标表记录的rowid对应关系。在后续merge JOURNAL表记录时将起到一个非常重要的作用。

在move期间测试插入9条记录删除1条数据,发现该阶段并不会构建rowid对应关系,但是会记录在 JOURNAL表中。

第三步move完成之后会调用ctcmerge函数构建rowid关联关系,RMTAB_H会比实际表少8条,因为我们刚才插入了9条数据删除了1条。

第四步调用kkzuRmtCreateINT函数创建RMTAB_I表,这是一个IOT表,其实就是从 SYS_RMTAB$$_H 中CTAS来的,这个表的作用是为了处理索引的。

第五步调用kkzuRmtDrop函数删除 SYS_RMTAB$$_H,它的使命已经完成。

第六步调用kkpocim_create_indexes_modpart,创建一个不可见索引,从10046跟踪来看,读取的是新表的segment,这是在同步表上的索引了。

第七步调用ctcmj_merge_journal开始进行merge操作。merge完成之后,修改数据字典切换表和索引,并删除老的索引和JOURNAL表

可以看到切换后索引状态正常因为是基于新的segment创建的。最后再次调用kkzuRmtDrop函数删除 RMTAB_I表

至此online move table过程结束。

案例:expdp dmp损坏恢复

本案例来自某客户的expdp dmp从服务器上下载下来存档之后,删除了服务器上的dmp,后来需要导入的时候发现导入报错。

看名字应该是每天都会对用户进行expdp导出。对于这类损坏的dmp文件应该如何恢复呢?oracle内部提供了DUL恢复工具,当然DUL的作用并不仅仅是抽取dmp文件,其他功能本文就不介绍了。

DUL针对exp dmp和expdp dmp分别使用unexp和unpump命令去处理,基本用法如下所示:

由于本案例是expdp dmp,所以只会演示expdp dmp的抽取。

可以通过dump expdp头部去查看dmp基本情况

从dmp的头部dump可以看到dmp的version以及字符集等等信息。由于该dmp是一个按用户导出的dmp,下面我只以某个表为例开始进行恢复演示。

首先扫描dmp文件,可以看到dmp里有多少个表,以及每个表的magic number offset。需要注意的是导出时产生的master table export开头的全局临时表也在其中。都在dmp的最后面记录。

使用hexdump或者od -x去看对应的offset可以发现Magic number 基本都是0xffff2424。 Magic number个人认为意义在于能更快更好的定位到dmp中表的位置,比如我们这里要抽取表SP_SURVIVALVALIDATION,可以通过一些脚本去首先定位该表的 Magic number ,比如我这里的方法:

左边是表在导出库里的object_id,可以看到 SP_SURVIVALVALIDATION 在第14行,那么该表在dmp中的Magic number offset为16309780480

可以看到表结构的xml信息以及从 Table data from 16309790584 until 16336012118 可以看出该表数据的offset区间。下面开始抽取工作:

可以看到数据完整的抽取了出来,抽取出来的文件需要用sqlldr去导入

该表已经成功导入,整体的使用过程还是非常简单的,但是如果dmp中表非常多,那么手工单独去处理还是比较麻烦的,必须得用脚本来处理。

案例:ORA-00600: internal error code, arguments: [4097]

本案例来自某省电信一套11.2.0.4的rac,应用的存储过程调用一直在报ORA-00600: internal error code, arguments: [4097],对于经常搞恢复的人来说,这个错误非常熟悉,都不用分析直接重建undo即可,但是作为一个专业的troubleshooter,还是多少分析一下来龙去脉吧。。。

查看trace发现报错的sql是一个dblink插入远程库的sql

我们知道ORA-00600[4XXX]都是与undo息息相关的报错,而在call stack里并未找到ktu相关函数调用,所以怀疑是remote端数据库的undo异常导致的。

查看remote端,数据库为单实例10.2.0.4,从报错trace里可以看到同样的sql也是报的ORA-00600[4097]

通常在trace的最后一个Block header dump极有可能是访问报错的block,所以搜索”Block header dump“先看看

报错的block是一个索引,dataobj#为 0x5f48f ,这明显是一个业务表上的索引,从ITL上的slot 0x01看出读取该块需要访问8号undo段头事务表的slot 27做块清除找到符合该次逻辑读的commit scn,那么看看8号undo段头事务表slot 27

可以看到索引块ITL中的XID的wrap#(0x149736)居然比事务表27 slot的wrap#要大,这明显是不可能的事情,所以报出了ORA-00600[4097]。

解决方法很简单就是重建undo表空间并删除老的undo表空间。 ORA-00600[4097] 一般在不一致的open数据库经常遇到,由于该环境为正常的生产环境,通常不会出现 ORA-00600[4097] ,所以个人怀疑有写丢失的可能性。

oracle online系列(二):online indexbuild

online indexbuild (online create或者rebuild index)是oracle的一个非常常用的online操作,我们知道当创建索引或者重建索引没有加online关键字的话,会请求表对象上的4号TM锁,而DML请求的是3号TM锁,3和4的TM锁并不兼容,所以在索引创建或者重建期间是无法进行DML操作的,等待事件为enq: TM – contention。为了改进这一缺陷,在oracle 8i推出了online indexbuild功能,并且在11.1该功能得到了进一步增强。online indexbuild就不会影响其他DML操作的正常运行。本文将深入解析 online indexbuild的工作原理。

在分析online indexbuild实现原理之前,先介绍一下 indexbuild online与非online除了对表持有的TM锁级别不同之外的第二个比较大的区别,就是 indexbuild的执行计划不同。

  • indexbuild online只能使用全表扫描的方式
  • indexbuild非online,遵循CBO最小cost原则去选择执行计划,索引快速全扫描或者全表扫描