DM内存结构
DM数据库有自己的内存管理系统,可以用于申请,释放内存,了解内存使用情况及发现内存泄漏和内存写越界的问题。
DM内存结构包括内存池,缓冲区,排序区,哈希区等。
内存池
内存池包括共享内存池和其他运行时内存池。
查看当前系统中所有的内存池:
SELECT *FROM V$MEM_POOL;
行号 ADDR NAME IS_SHARED CHK_MAGIC CHK_LEAK IS_OVERFLOW IS_DSA_ITEM ORG_SIZE TOTAL_SIZE
---------- -------------------- --------------------- --------- --------- -------- ----------- ----------- -------------------- --------------------
RESERVED_SIZE DATA_SIZE EXTEND_SIZE TARGET_SIZE EXTEND_LEN N_ALLOC N_EXTEND_NORMAL N_EXTEND_EXCLUSIVE N_FREE
-------------------- -------------------- -------------------- -------------------- ----------- ----------- --------------- ------------------ -----------
MAX_EXTEND_SIZE MIN_EXTEND_SIZE FILE_NAME FILE_LINE CREATOR
-------------------- -------------------- ------------------ ----------- -----------
1 269877400 SHARE POOL Y Y Y N N 209715200 209715200
26046464 18456942 1048576 0 0 1526 0 0 869
0 9223372036854775807 ..\knl\mem2.c 1391 4984
共享内存池
共享内存池是系统启动时申请的一大片连续内存,以减少频繁申请内存引起的线程切换,导致运行效率下降。
共享内存池在系统启动时初始化,其大小可以在dm.ini文件中设置(调整参数MEMORY_POOL
),初始为200M。当使用需求大于初始值时自动扩展,其扩展大小由参数MEMORY_EXTENT_SIZE
指定;扩展上限由参数MEMORY_TARGET
指定。
运行时内存池
运行时内存池是DM的一些功能模块在运行时申请自己使用的内存池,例如会话内存池,虚拟机内存池等等。
缓冲区
数据缓冲区
数据缓冲区是DM将数据页写入磁盘前及从磁盘读取数据页后暂时存储的地方。系统启动时,DM向操作系统申请一片连续内存并根据页大小格式化,置入“自由链“中等待使用。
DM使用LRU链算法来管理普通缓冲区中的数据页,其对最近使用的数据页根据时间进行排序,当自由链用完时,从LRU链中淘汰最近未使用的数据页,释放缓存空间。
LRU链(Lease recently used,最近最少使用的)算法
LRU链算法的基本概念是当内存的剩余空间不够时,缓冲区尽量保留最常用的数据,即优先清除不常用的数据,并释放空间。LRU算法广泛用于CPU的L1,L2缓存,共享池,Buffer cache池等等
具体实现
为实现LRU功能,数据库在内存中维护了若干条链表,将数据缓冲区中的内存块按照访问次数,访问时间排序串在链表中,将链表分为两半,两头称为冷端和热端。
热端 | 冷端 | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
A | B | C | D | E | F | G | H | I | G | K | L | M | N | O | P | Q | R | S | T |
当第一次访问某个块时,如果不在数据缓冲区中,则先读入。
新读入的数据需要放在缓冲区中,则需要选出一个牺牲着。一般将冷端的最后一个块选为牺牲者,将他替换,之后将新加入的块移动到冷端的最左侧。当再有新块进入时,替换最后一个,并放在上一个加入的右侧。
如此,可以保证访问时间越新的块,越不会被牺牲掉。但是否被牺牲还有一个决定参数”访问次数“。
可以设置,当访问次数大于2,则认为当前块被经常访问,不能被牺牲。此时可以对每个块添加一个记录访问次数的标志位,当值大于2,则认为可能会被经常访问,将其移动到热端的最左侧并重置访问次数。一些访问次数为2但位置在冷端左侧的块也不会被马上移动到热端。
脏块与脏链
一般先在数据缓冲区中修改值,然后等待时机将修改后的块刷入磁盘中,这由专门的刷盘线程负责。
当缓冲区中的内存块被修改后,将此块标记为脏块,因为此时数据已被修改且和磁盘数据不同。脏块中的修改被刷入磁盘后又重新变为干净块。
在LRU链表中,脏块保存用户对数据的修改,也不能被覆盖。但如果将脏块和干净块存储在统一链表中,每次读入数据选择牺牲者时还需判断避开脏块,由此产生时间成本。
此时再维护一条脏LRU链用来保存脏块,然后在一定条件下刷新脏链。
Oracle中块的位置变化时机是在LRU冷端搜索牺牲者时。
在实际使用中存在一部分经常被访问的数据,DM将这些数据分布在单独的内存区域且不参与一般的淘汰机制,保证数据一直留在数据缓冲区中。
DM中有四种类型的数据缓冲区,分别是 NORMAL、 KEEP、 FAST 和 RECYCLE。它们的初始大小可以在dm.ini中指定,默认为BUFFER(100MB) 、 KEEP(8MB) 、 RECYCLE(64MB) 、FAST_POOL_PAGES(3000) 。
在进行大量数据读取时,尽量减少磁盘I/O次数可以提高查询,修改效率。通过修改dm.ini中MULTI_PAGE_GET_NUM
参数(默认16页)可以指定一次读取数据页的个数。
日志缓冲区
日志缓冲区用于暂时存放重做日志。系统在运行时产生的日志并不会立即被写入磁盘,而是先放在日志缓冲区中。
独立设置日志缓冲区的原因如下:
- 重做日志的格式与数据页完全不同,无法统一管理
- 重做日志可连续写
- 重做日志比数据页IO优先级更高
在DM中通过RLOG_BUF_SIZE设置日志缓冲区大小,所需空间从共享内存池中申请,单位为页数量,且大小为2的N次方,默认大小512页。
字典缓冲区
字典缓冲区存储数据字典信息,比如模式,表,列,触发器等。这些是每次访问数据必须获取的内容,因此将他们放在缓存中可以提高访问效率。
DM将部分字典对象缓冲到内存中,并使用LRU算法进行管理。使用DICT_BUF_SIZE
参数设置大小,默认5M。
SQL缓冲区
SQL缓冲区提供在执行SQL语句过程中需要的内存,如计划,SQL语句,结果集缓存。
对于需要反复执行的SQL语句,可以将SQL和执行计划缓存起来,称为计划重用。
计划重用会加重内存压力,DM中使用USE_PLN_POOL
参数指定是否启用。0禁用,非0启用;使用CACHE_POOL_SIZE
设置SQL缓冲区大小,默认20M。
结果集缓存包括SQL查询结果集缓存和DMSQL程序函数结果集缓存,要启用需同事设置参数RS_CAN_CACHE=1
和USE_PLN_POOL
非0;
客户端结果集也可缓存,须在dm_svc.conf中设置参数:
ENABLE_RS_CACHE = (1) //启用缓存;
RS_CACHE_SIZE = (100) //缓存大小100M,取值范围1-65535
RS_REFRESH_FREQ = (30) //每30秒检查缓存的有效性
DM还可以通过配置dm.ini中的参数CLT_CACHE_TABLES
参数选择那些表的结果集需要缓存。
排序区
排序缓冲区用于数据排序,排序完成后释放内存。
DM中使用SORT_BUF_SIZE
参数指定排序缓冲区大小,建议使用默认值2M。
哈希区
哈希区是为哈希连接设定的缓冲区,是虚拟缓冲区。
数据库执行sql语句时,先通过查询优化器选择最优方式,生成执行计划。在查询语句中FROM字句包含多个表时,需要选择两张表的连接方式,例如哈希连接,嵌套连接,归并连接,外连接等。等值连接一般选择哈希连接方式。
哈希连接(HASH JOIN)
哈希连接是一种两个表做表连接时依靠哈希运算得到结果集的表连接方法。
哈希连接将两张表中经条件过滤后结果集较少的一张作为驱动结果集,用驱动结果集中的第一条记录探测另一张表,找到符合条件则放入最终结果集中。
SSD缓存区
固态硬盘相比机械硬盘速度更快,DM将SSD文件作为内存缓存与普通磁盘之间的缓冲层,称为SSD缓存。