目录
GBase 8s 总体架构包含三个重要组成部分:处理器、共享内存和磁盘。今天我们详细来讲述共享内存。整体架构如下图:

共享内存
共享内存是允许数据库服务器线程和进程通过共享对内存池的访问权来共享数据的一种操作系统功能。数据库服务器将共享内存用于以下用途:
- 数据库服务器进程通过共享内存池实现数据共享的目的
- 通过缓存磁盘数据来减少磁盘 I/O
- 通过共享内存为进程提供进程间通信的最快捷方式
- 为使用 IPC 通信的本地客户端程序提供共享内存通信渠道
共享内存的组成
共享内存按类型分成3类:常驻内存段(Resident Segment)、虚拟内存段(Virtual Segment)和消息段(Message Segment)
常驻段
包含缓冲区和其他系统信息
常驻内存,当下面两个条件同时存在时,才会有常驻内存段:
1、操作系统支持共享内存驻留
2、onconfig 文件中的 RESIDENT 参数已设置为 -1 或大于 0 的值固定大小,只有当数据库实例重启时才可以重新进行分配,常驻内存段内的各个组成部分的内存段的大小也是在启动时设定的,在数据运行阶段不能进行变更。
常驻段结构图如下图,相关内容解释如下表:
字段 | 描述 |
BUFFERPOOL | 指定驻留部分的最大的内存部分,它的值是由 BUFFERS 决定的。BUFFERPOOL的页面大小和dbspace的页面大小必须相同,16KB页面大小的dbspace的数据不能缓冲在8KB的缓冲池中。 |
LOGBUFF | 指定单个逻辑日志缓冲的大小,逻辑日志缓冲总共有3个,这个是不能调整的。当使用buffered 数据库时,它决定了当逻辑日志缓冲写到多少时,把它刷出到逻辑日志文件中。它的默认值是64KB。 |
PHYSBUFF | 指定单个物理日志缓冲的大小,物理日志缓冲总共有2个,这个是不能调整的。使用buffered 数据库时,它决定了当更新的前映像在物理日志缓冲写到多少时,把它刷出到物理日志文件中。它的默认值是64KB |
LOCKS | 用来决定初始的共享内存表的大小,当内存表用完时,会基于LOCKS参数在虚拟段中申请额外的空间。动态申请的内存表在数据库重新初始化之后才会释放。LOCKS 的默认值是2000,在64位的产品中,最大可以配置5亿个锁。 |
使用onmode -r/n命令可以打开/关闭共享内存的驻留(注意:这两个操作均不会影响 onconfig文件中 RESIDENT 配置参数)
- onmode -r:立刻为共享内存的常驻部分打开驻留,或使用admin()/task()函数。
EXECUTE FUNCTION task("onmode","r");
EXECUTE FUNCTION admin("onmode","r");2、onmode -n 命令:立刻为共享内存的常驻部分关闭驻留,或使用admin() 或 task() 函数:
EXECUTE FUNCTION task("onmode","n");
EXECUTE FUNCTION admin("onmode","n");
数据库服务器共享内存的常驻部分存储了下列数据结构,这些数据结构的大小在数据库服务器运行时不会更改:
1、共享内存头
共享内存头中包含共享内存其他部分的信息和指向各部分的指针。当虚拟处理首次连接到共享内存时,它会对共享内存头进行访问,读取各部分的位置信息以便引导到其他部分。共享内存头的大小一般200KB。
2、共享内存缓冲池
共享内存缓冲池存储从磁盘读取的数据库空间页的缓冲区,用来缓存数据库表从磁盘读取的数据,数据库在内存中对数据进行访问和修改,当数据发生变化后,将写回磁盘。其中每个缓冲区就是一个数据库服务器页的大小。
使用 BUFFERPOOL 配置参数可指定有关缓冲池的信息,包括缓冲池中缓冲区的数量。
共享内存缓冲池通过 LRU 队列的方式进行集体管理。LRU队列由空闲的队列(Free LRU 或 FLRU)和脏队列(Modified LRU 或 MLRU)组成。共享内存缓冲池按 LRU 队列对进行管理,一个是 Free,另外一个是Modify。同一个Page只能在LRU队列对中出现一次,比如:有一行记录被修改了,那么该行记录所在的 Page 将被从Free队列移动到Modify队列。
BUFFERPOOL 是驻留部分的最大的内存部分,它的值是由 BUFFERS 决定的。BUFFERPOOL的页面大小和dbspace的页面大小必须相同,16KB页面大小的dbspace的数据不能缓冲在8KB的缓冲池中。
有3种方法可以添加一个带有不同页大小的 dbspace 来添加一个 BUFFERPOOL 条目 1、 onspaces 实用程序添加一个带有不同页大小的 dbspace: 2、onparams 实用程序添加缓冲池: 3、运行带有 add bufferpool 参数的 SQL 管理 API task() 或 admin() 函数,也可以添加缓冲池,并在 onconfig 文件中动态地添加一个条目: |
我们可以通过onstat -R查看共享内存缓冲池的LRU队列情况。
1、 查看M队列中有脏数据:onstat -R
2、手动执行checkpoint:onmode -c
3、查看M队列中的脏数据已全部:onstat -R
3、逻辑日志缓冲区
逻辑日志缓冲区用来将在数据库处理过程中生成的逻辑日志缓存在内存中,当特定事件发生后,再一次性写入逻辑日志文件,通过逻辑日志缓存可以有效减少 I/O 次数,提高逻辑日志写入性能。GBase 8s 分配3 个逻辑日志缓冲区,循环使用。LOGBUFF 配置参数指定逻辑日志缓冲区的大小。
显示有关物理日志和逻辑日志的信息:onstat -l ,其中状态标志flags的解释见下表:
状态标志 | 逻辑日志文件的状态 |
A------ | 日志文件已添加且可用,但尚未使用。 |
D------ | 如果您删除具有状态 U-B 的日志文件,那么它会标记为已删除。该日志文件被删除,其空间得以释放。 |
F------ | 日志文件是空闲的且可供使用。逻辑日志文件在备份后得以释放,逻辑日志文件中的所有事务均关闭,存储在该文件中的最旧更新会清空到磁盘。 |
U | 日志文件已使用但尚未备份。 |
U-B---- | 日志文件已备份但仍需用于恢复。(当不再需要该日志文件用于恢复时,将释放该文件) |
U-B---L | 日志已备份但仍需用于恢复。包含上一个检查点记录。 |
U---C | 数据库服务器当前正在填充日志文件。 |
U---C-L | 当前日志文件包含上一个检查点记录。 |
逻辑日志包含以下五种类型日志记录(此外还有很多其他类型):
- 所有DDL语句(CREATE、DROP 、ALTER)
- 用于通过日志记录创建的数据库的 SQL 数据操作语句
- 对数据库的日志记录状态所作更改的记录
- 检查点记录
- 对配置所作更改的记录
清空逻辑日志缓冲区:数据库服务器使用共享内存逻辑日志缓冲区作为临时存储器用于存储向数据库服务器页描述修改的记录。这些更改记录将从逻辑日志缓冲区写入磁盘上当前的逻辑日志文件,并最终写入逻辑日志备份介质。五个事件导致当前逻辑日志缓冲区清空:
- 当前的逻辑日志缓冲区变满
- 事务已在unbuffered logging的数据库中准备或终止之后,即执行COMMIT/PREPARE语句:
- 使用nologging数据库或unbuffered logging数据库的会话终止时
- 出现检查点
- 当已修改不需要物理日志文件中的前映像的页时
4、物理日志缓冲区
物理日志缓冲区用来将在数据库处理过程中生成的物理日志缓存在内存中,在特定事件发生后,再一次性写入物理日志文件,通过物理日志缓存可以有效减少 I/O 次数,提高物理日志写入性能。GBase 8s 分配两个物理日志缓冲区,轮换使用。PHYSBUFF 参数指定物理日志缓冲区的大小,缺省值为 512 KB。双缓冲允许数据库服务器进程在其他缓冲区清空到磁盘上的物理日志时写入活动物理日志缓冲区。如图所示。

清空物理日志缓冲区:数据库服务器临时存储物理日志缓冲区中存储一些已修改的磁盘页的前映像。如果前映像已写入物理日志缓冲区但未写入磁盘上的物理日志,那么服务器会在将已修改的页清空到磁盘之前先将物理日志缓冲区清空到磁盘。以下事件将导致活动的物理日志缓冲区清空:
- 活动物理日志缓冲区变满。
- 共享内存中的已修改页必须清空,但前映像仍然在活动物理日志缓冲区中。
- 出现检查点。
5、锁表
GBase 8s 的锁机制采用全局的方式进行管理,在内存中标识表、行记录的锁使用状态, 如图所示,通过 ONCONFIG 参数 LOCKS 来定义数据库服务器启动时分配的锁的个数。每个锁占用 120 字节。如果online.log日志中有 dynamically allocated 200000 locks,则可以判断配置的 LOCKS 过小,此时新增锁将采用虚拟内存段分配内存。
onstat -k 命令查看到锁的内存地址信息及占用的内存大小,通过两个锁的内存地址可以计算出锁占用的内存大小:441a32d0-441a3028=02A8=680 Byte。
虚拟段
虚拟段用来存储数据库服务器中那些可以扩大或者缩小、可以重新分配的信息,包含线程和会话的信息、数据对象缓存、以及排序和并行数据查询等活动所需的临时数据。虚拟段的大小可以随着需求增长。按照不同的用途,将虚拟段分成了不同的内存池 Pool,包括如图所示的内存池。

Sort 池 | 用来排序的内存,GBase 8s 为每个 Session 分配固定大小的内存进行排序操作,如果内存不够,则将使用临时表空间进行磁盘排序。 |
Session 池 | 用来存储所有用户会话的信息。 |
Dictionary 池 | 用来存储系统字典表的信息。 |
Procedure 池 | 用来存储存储过程的信息,将系统或者用户定义的存储过程的可执行代码信息存储在该区域。 |
Big Buffer 池 | 在需要单独使用一个大的Buffer时可使用。如:Light append和Light Scan 操作将使用Big Buffer而不是常驻内存段的Buffer Pool来缓存数据。 |
我们可以通过 onstat -g mem 命令监控虚拟段内存的使用情况,部分字段解释如下:
Pool Summary | |
name | 池的名称 |
class | 在创建池的位置的共享内存段类型 |
addr | 池内存地址 |
totalsize | 池大小,以字节表示 |
freesize | 在池中的可用内存量 |
#allocfrag | 在池中的已分配的分片 |
#freefrag | 在池中的可用分片 |
Blkpool Summary | |
name | 池的名称 |
class | 在创建池的位置的共享内存段类型 |
addr | 池内存地址 |
size | 池大小,以字节表示 |
#blks | 池中的 block 数 |
管理共享内存的虚拟部分
数据库服务器使用内存池跟踪类型和大小类似的内存分配。在池中保留相关的内存分配将减少内存分段存储。它还使数据库服务器能够一次释放大块的内存,与释放组成池的每个块相对。
所有的会话都有一个或多个内存池。当数据库服务器需要内存时,它会先查看指定的池。如果池中可用的内存不足以满足请求,那么数据库服务器将从系统池添加内存。如果数据库服务器不能在系统池中找到足够的内存,那么它会动态地给虚拟部分分配更多的段。
数据库服务器通过已链接的列表跟踪可用空间的池,为自己的每个子系统(会话池、堆栈、堆、控制块、系统目录、SPL 例程高速缓存、SQL 语句高速缓存、排序池和消息缓冲区)分配虚拟共享内存。当数据库服务器分配内存的部分时,它将首先在池的可用列表中搜索大小足够的段。如果服务器未找到段,那么会将新的块从虚拟部分带入池中。如果释放了内存,那么被释放内存将作为可用段返回到池中并留在其中,直到池被删除。 例如,当数据库服务器启动了客户机应用程序会话时,它将为会话池分配内存。当会话终止时,数据库服务器会将已分配的内存作为可用段返回。
共享内存虚拟部分的大小
使用配置参数可指定共享内存虚拟部分的初始大小、以后要添加的段的大小以及可用于 PDQ 查询的内存量。
- 要指定虚拟共享内存部分的初始大小,请设置 SHMVIRTSIZE 配置参数。
- 要指定以后添加到虚拟共享内存的段的大小,请设置 SHMADD 和 EXTSHMADD 配置参数。
- 要指定 PDQ 查询可用的内存量,请设置 DS_TOTAL_MEMORY 参数。
如果要增加可用于非 PDQ 查询的内存量,并且 PDQ 优先级设置为 0(零),那么可以通过以下任一方式更改该内存量:
- 设置 DS_NONPDQ_QUERY_MEM 配置参数(DS_NONPDQ_QUERY_MEM 的最小值为 128 KB。支持的最大值为 DS_TOTAL_MEMORY 值的 25%)
- 运行 onmode -wm 或 onmode -wf 命令
消息段
消息段用于共享内存连接,适用于应用程序和数据库在同一台机器上的场景。当应用程序和服务器部署在同一台物理服务器上时,采用共享内存通信的机制,GBase 8s 将在内存中使用一块独立的内存来进行应用程序和服务器之间的通信,可以提高通信的效率。
共享内存管理
GBase 8s 共享内存在数据库实例启动时由操作系统根据 GBase 8s 的配置参数进行分配与管理。下图展示了操作系统如何分配 GBase 8s共享内存。
GBase 8s 共享内存在操作系统上内存地址的管理按照GBase 8s实例的SERVERNUM进行分配,通过 ipcs -m 查看操作系统内存段的情况,并通过onstat -g seg查看 GBase 8s 内存段的信息,二者一一对应。
共享内存的管理主要涉及以下几个方面:
- 设置共享内存配置参数;
- 为数据库服务器共享内存的常驻部分打开或关闭驻留;
- 将段添加到共享内存的虚拟部分;
- 为关键活动预留内存;
- 在具有内存限制的应用程序中保留目标内存量;
- 监视共享内存。
获取共享内存统计信息有两种方法:sysmaster中sysprofile表、onstat -p命令。
1、系统表sysmaster:sysprofile也可以获取共享内存统计信息。
2、onstat -p 输出sysprofile表中所有信息,另外加上ovbuff、usercpu和syscpu参数:




