Oracle内存结构图
概述
Oracle的每个实例都有各自的SGA,当实例启动时会自动为SGA分配内存,当实例关闭时回收内存. SGA为实例下所有进程共享,所有进程都能用共享SGA内的信息
如图一示:,
SGA由几个内存组件组成,这些内存组件是用于满足特定类别的内存分配请求的内存池. 除重做日志缓冲区(Redo Log Buffer)之外的所有SGA组件都以称为粒度的连续内存为单位分配和释放空间. 粒度大小是特定于平台的,由总SGA大小决定.
可以查询V$SGASTAT视图以获取有关SGA组件的信息.
SGA有以下组件:
Database Buffer Cache
In-Memory Area
Redo Log Buffer
Shared Pool
Large Pool
Java Pool
Fixed SGA
Optional Performance-Related SGA Subareas
Database Buffer Cache
小名"Buffer Cache",Oracle将数据从磁盘中的数据文件读取到这部分内存中.
当前或最近使用的数据块主要缓存在Buffer Cache中,所有当前实例下正在活动的连接都能够共享这部分内存下的数据
Buffer Cache的作用
Oracle使用Buffer Cache来实现以下几个目的:
- 优化物理I/O
数据库更新缓存中的数据,并在Redo Log Buffer(重做日志缓冲区)中存储有关更改的元数据. 提交之后,将数据写入联机重做日志,但不会立即将数据写入磁盘的数据文件. 而是,调用database writer(DBW)后台进程在后台执行延迟写入.
- 将频繁被访问的数据放到缓存中,将不经常被访问的数据存到磁盘
当数据库启用智能闪存缓存(Smart Flash Cache)时,存储在缓存的那部分数据可以存在闪存缓存中.闪存缓存是有一块或多块固态组成的闪存磁盘,数据库可以通过在缓存中读取数据而不是从磁盘中读取来提高性能.
使用DB_FLASH_CACHE_FILE
和DB_FLASH_CACHE_SIZE
初始化参数配置多个闪存设备.
Buffer States
数据库使用内置算法来管理缓存
缓冲区可以处于以下互斥的任一状态中:
- Unused(未使用)
这种类型缓冲区允许被使用,因为它从未被使用过或者当前没有被使用,这种类型的缓冲区是最容易被使用用的
- clean(干净的)
这种类型的缓冲区在之前被使用过,但是还保存着一个数据块的读取一致版本,这个数据块虽然保存数据,但是是"干净的",所以不需要检查点(The block contains data but is "clean" so it does not need to be checkpointed),所以数据库可以命中并重用这个块.
- dirty(脏的)
这种类型的缓冲区存在已经修改过的,还没写入磁盘的数据,这时需要在使用前调用检查点,将脏数据写入磁盘
缓冲区有两种访问状态:命中(pinned)与空闲(free),当一个会话访问缓存中一个buffer时,它变为"pinned",那么它不会被内存换出. 多个会话不能同时修改一个被命中的缓冲区.
Buffer Modes
当客户端请求数据时,数据库以以下两种模式从buffer cache中取回数据
- Current mode(当前模式)
current mode get 或者称为 db block get ,这是一种在当前存在 buffer cache中存在的块中检索数据的操作. 例如,当一个未提交事物更新了一个块中的两行数据,那么current mode get 将这个块取回(包含未提交的行),在使用update语句时,db block get将被频繁使用.
- Consistent mode(一致性模式)
consistent read get是一种从读取一致版本的块中检索方式,这种检索方式可能会用到 undo data,例如,当一个未提交事物修改两行数据,而另一个会话需要访问这个表,那么数据库会根据Undo data克隆一个当前数据块的读取一致版本,读取一致版本将不包含未提交的修改,其他会话的请求将会从读取一致版本中返回. 基本上select获得数据是Consistent mode.
undo data:事物操作记录,主要在提交之前,数据库可以使用 undo data在逻辑上逆转SQL语句的效果,Undo data存在 Undo segments中
Buffer I/O
logical I/O(逻辑I/O),通常被称为buffer I/O,指的是在缓存区中对缓存进行读写操作
当需要的数据没有在缓存中找到,那么数据库将会执行physical I/O(物理I/O),从闪存(falsh cache)或者磁盘中将这部分数据复制到内存中,然后再执行logical I/O将数据从缓存中读取出来.
Oralce现在使用以下算法:
1.LRU-based, block-level replacement algorithm(基于LRU的块级替换算法)
这是一种oracle默认的复杂的算法,数据库在缓存中创建LRU list,该list用来存指向脏缓冲区和非脏缓冲区的指针. list分为热端和冷端; 热端指最近被经常访问的缓存区,冷端是指最近没有被访问的缓存区. 从概念上讲,只有一个LRU list,但实际因为数据并发,数据库使用了多个LRU list
Buffer Writes
database writer (DBW) 后台进程将缓存中脏的,而且处于冷端的数据块写入磁盘
DBW在下列情况会将将数据写入磁盘:
- 当服务进程不能找到干净的缓冲区读取新数据进缓存时
当缓冲区被标记为dirted时,状态为free缓冲区的数目就会减少,当数值减少到内部阀值时,且需要干净缓冲区时,服务进程会向DBW发送信号将数据写入磁盘
数据库使用LRU来确定要写入的脏缓冲区. 当脏缓冲区到达LRU的冷端时,数据库将它们从LRU移到写入队列. 如果可能,DBW使用多块写入将队列中的缓冲区写入磁盘. 此机制可防止LRU的末端被脏缓冲区阻塞,并允许找到干净的缓冲区以供重用.
- 数据库必须提前检查点(checkpoint),检查点是重做线程(redo thread)中必须从中开始实例恢复的位置.
- 表空间更改为只读状态或脱机.
Buffer Reads
当clean buffer或unused buffer数量少时,数据库必须从buffer cache中移除一些buffer.
以上过程根据flash cache开启而变化:
-
Flash cache disabled
数据库根据需要重用每个干净的缓冲区,将数据写入缓冲区. 在数据写入缓冲区之前,数据库必须从磁盘读取它.
- Flash cache enabled
DBW可以将缓存区中的数据写入flash cache,从而可以重用内存缓存区.数据库将数据缓存的信息保存在主内存的LRU list中,方便跟踪数据在flash cache中的位置与状态. 如果在之后需要再查询这部分数据时,则不需要从磁盘中读取.
当客户端进程请求缓存时,服务器进程在buffer cache搜索缓存. 如果数据库在内存中找到缓存,则会发生缓存命中. 搜索顺序如下:
- 服务进程在在buffer cache中搜索整个缓存,如果找到整个缓存,数据库会进行逻辑读取
- 服务进程从存有flash cache信息的LRU list中搜索对应的信息,,如果找到了,数据库会从flash cache中进行物理读取数据进入内存
- 如果服务进程在buffer cache中没有找到缓存,那么会执行下列步骤:
a.将数据从磁盘中读取到内存中(物理读取)
b.执行一次逻辑读取从缓存中读取数据
如图二所示:扩展缓存区包含buffer cache和flash cache,在图中,进程在扩展缓存区中没找到数据,那么就会从磁盘中将数据读取到缓存中
正常情况下,通过缓存访问数据要快于在缓存中找不到的情况.缓存命中率度量了数据库在内存中找到而不需要从磁盘中读请求的块的比例.
数据库可以在数据文件或临时文件中都会执行物理读取(physical reads)
从数据文件中读,后面将跟随一个逻辑I/O(logical I/O).
从临时文件中读,经常发生再当内存不足时强制数据库将数据写入到临时表(temporary table)然后在之后将其读回,这种物理读将不通过buffer cache 以及不会引起logical I/O.
Buffer Touch Counts(触摸计数)
数据库使用触摸计数的方式来统计LRU list上缓冲区的访问频率.这种方式允许数据库命中缓存后增加计数器中的值,而不是当数据库命中缓存后再LRU移动相应对象
当缓存被命中时,数据库会记录触摸计数的时间,当计数时间间隔超过3秒,计数会增加,否则计数保持不变. 三秒规则可防止缓冲区上的引脚突发计数多次触摸(touch). 例如,会话可以在数据块中插入多行,但数据库将这些插入视为一次触摸(touch).
如果缓冲区位于LRU的冷端,但其触摸计数很高,则缓冲区移动到热端. 如果触摸计数较低,则缓冲区会从缓存中老化.
Buffer Pools
缓冲池是一个缓存的集合.
数据库的缓存区(buffer cache)分为一个或多个缓冲池(Buffer Pools),这些缓冲池的的处理逻辑并没有大的区别.
也可以自己配置单独的缓冲池,这些缓冲池可以将数据保存在buffer cache中,也可以在使用数据块之后立即为新数据提供缓存.然后,您可以将特定的模式对象分配到适当的缓冲池,以控制块如何从缓存中过时.例如,可以将段划分为hot、warm和code等状态的缓冲池.
有以下几种缓冲池:
- Default pool(默认池)
通常缓存块存在这个池中,除非设置了单独的缓冲池,否则这就是唯一的默认池,其他池的配置与默认池无关.
从Oracle12.1.0.2开始,大表缓存是默认池的可选部分,它使用基于温度的对象级替换算法(temperature-based, object-level replacement algorithm). 在单实例和Oracle RAC数据库中,并行查询可以在DB_BIG_TABLE_CACHE_PERCENT_TARGET初始化参数设置为非零值时使用大表缓存,并PARALLEL_DEGREE_POLICY设置为auto或adaptive. 仅在单实例配置中,串行查询可以在DB_BIG_TABLE_CACHE_PERCENT_TARGET设置时使用大表缓存.
-
Keep pool
这种池是为访问频繁但总因为默认池空间狭小总被换出的块准备的,keep pool的目标是将对象保留在内存中,用来避免I/O操作.
- Recycle pool
这种池为偶尔被访问的池准备的,Recycle pool可以防止对象占用缓存中不必要的空间.
数据库有标准的块大小,但是你可以创建表空间块大小不同于标准块大小. 每个非默认的块大小都有一个它自己的pool. Oracle数据库管理这些池中的块的方式和默认池中的一样.
下图图三表示buffer cache使用了多个池的结构,buffer cache包含Default pool,Keep pool,Recycle pool,默认的块大小为8k. buffer cache还包含单独池,这些池的块大小事不标准的,可能是2k,4k,16k.
Buffers and Full Table Scans
数据库使用复杂的算法来管理表扫描. 默认情况下,必须从磁盘读取缓存时,数据库会将缓存插入LRU列表. 通过这种方式,热块(hot blocks)可以保留在缓存中,这样就不需要再次从磁盘读取它们.
当读取表的HWM(high water mark)下所有行的时候,全表扫描会有一个问题.假设当全表数据大于整个缓存容量时,执行全表扫描会清空缓存中的所有数据,这样会妨碍数据库维护高频率被访问的缓存.
Default Mode for Full Table Scans
在默认情况下,数据库保守采用全表扫描方式;在确认表大小只是buffer cache的一小部分时才将小表加载进内存中