System Global Area (SGA)概述
SGA是一个可读写的内存区域,它和oracle数据库后台进程一起组成oracle数据库实例。所有用户进程对应的服务器进程都可以在SGA中读取信息。一些进程会在数据库操作期间对SGA区域进行写操作。
SGA有多种作用,如下所示:
・维护多个进程和线程并发访问的内部数据结构
缓存来自磁盘的数据块
在重做日志写入在线重做日志文件之前缓存重做日志
存储sql执行计划
服务器进程和后台进程并不驻留在SGA区域中,而是存在于一个单独的内存空间中。
每个数据库实例都有它自己的SGA内存区。当实例启动时oracle数据库会自动为oracle实例分配SGA内存区,当实例关闭时SGA内存区也会被回收。
当使用sql*Plus或Oracle Enterprise Manager启动一个实例时,SGA的大小会显示出来,如下图所示:
如下图所示,SGA由几个内存组件组成,这些组件是用于满足特定需求所分配的内存池。除了重做日志缓冲区外,所有SGA组件都在一个称为颗粒的连续内存单元中分配空间。颗粒大小是由总的SGA大小决定,SGA越大则颗粒就越大,SGA越小则颗粒就越小。
最主要的SGA组件如下所示:
・数据库缓冲区(Database Buffer Cache)
・内存中的列存储(In-Memory Column Store・重做日志缓冲区(Redo Log Buffer・共享池(Shared Pool・大池(Large Pool・Java池(Java Pool・流池(Streams Pool・固定SGA区(Fixed SGA数据库缓冲区(Database Buffer Cache)详解
数据缓冲区,也称为缓冲区,它是存储从数据文件复制过来的数据块的内存区。
缓冲区是一个主存地址,其中缓冲管理器暂时缓存当前或最近使用的数据块。所有用户并发连接到数据库实例共享缓存区缓存。
数据库缓冲区缓存的目的
oracle数据库利用缓冲区缓存可以实现多个目标
目的包括:
・优化物理I/O
数据库更新缓存中的数据块,并存储关于重做日志缓冲区中更改的元数据。提交后,数据库将重做日志缓冲区的内容写入联机重做日志文件但此时不会将数据块写入数据文件,数据块随后会被DBWn后台进程惰性写入数据文件中。
在缓冲区缓存中保留经常被访问的数据块,并且将很少被访问的数据块写入磁盘
当启用数据库智能Flash缓存时,缓冲区缓存的一部分可以驻留在Flash缓存中。这个缓冲区缓存扩展存储在一个或多个闪存设备上,它们是使用闪存的固态存储设备。数据库可以通过缓存缓冲来提高性能,而不是直接从磁盘上读取数据。
使用DB_FLASH_CACHE_FILE和DB_FLASH_CACHE_SIZE初始化参数配置多个闪存设备。缓冲区缓存跟踪每个设备,并统一向设备分发缓冲区。
数据库智能Flash缓存只能在Solaris和Oracle Linux中使用。
缓冲状态
数据库使用内部算法来管理缓存中的缓冲区。
缓冲区可以存在于以下任意一种互斥的状态中:
Unused状态
缓冲区是可用的,因为它从来没有被使用过,或者是目前没有被使用过。这种类型的缓冲区是数据库中最容易被使用的。
Clean状态
这个缓冲区在之前已经使用过了,现在包含了一个在时间节点上一个块的可读一致性版本,该块包含数据但是它是“干净”的,因此不需要检查点。数据库可以确定块并重新利用它。
Dirty状态
缓冲区中包含修改过但是尚未写入磁盘的数据。数据库必须在重新使用前检查块。
每个缓冲区都有一个访问模式:固定或自由(未固定)。缓冲区被“固定”在缓存中,这样它就不会在用户会话访问内存时耗尽内存。多个会话不能同时修改一个固定的缓冲区。
缓冲模式
当客户机请求数据时,oracle数据库在当前模式下或一致模式下从数据库缓存中检索缓冲区。
这些模式的不同之处如下所示:
・当前模式
当前模式是当前在缓冲区缓存中出现的一个块的检索。例如:如果一个未提交的事务在一个块中更新了两行,那么当前的模式就会用这些未提交的行来检索块。数据库在修改语句中最常用的是db块,它必须只更新块的当前版本。
・一致性模式
一个一致的读get是对一个块的读一致性版本的检索。这种检索可用来撤销数据。例如,如果一个未提交的事务在一个块中更新了两行,如果另一个单独的会话中的一个查询请求该块,那么数据库就会使用undo数据来创建这个块(称为一致的read克隆)的一个一致的版本,不包括未提交的更新。通常,查询以一致模式检索块。
I/O缓冲区
逻辑I/O,也称I/O缓冲区,是指在缓冲区缓存中读写
当在内存中没有找到请求的缓冲区时,数据库执行物理I/O将缓冲区从闪存或磁盘复制到内存中。然后,数据库执行一个逻辑I/O来读取缓存的缓冲区。
Buffer更换算法
为了使缓冲区访问效率更高,数据库必须决定在内存中缓存哪些缓冲区,以及从磁盘访问哪些缓冲区。
数据库使用以下算法:
LRU-based,block-level置换算法(块级)
这个复杂的算法在默认情况下使用一个最近使用表(LRU),该表包含指向脏缓冲区和非脏缓冲区的指针。冷缓冲区是最近没有被使用过的缓冲区,热缓冲区是经常被访问并且最近也在被使用的缓冲区。从理论上讲只有一个最近使用表(LRU),但数据并发的角度来看,数据库实际上使用了许多的最近使用表(LRU)。
Temperature-based,object-level 置换算法(对象级)
从Oracle Database 12c Release 1(12.1.0.2)开始,自动大表缓存功能允许表扫描在以下场景中使用不同的算法:
Parallel queries(并行查询):在单实例和Oracle RAC集群数据库中当DB_BIG_TABLE_CACHE_PERCENT_TARGET初始化参数设置为非零值时,并行查询可以使用大表缓存,而PARALLEL_DEGREE_POLICY则设置为自动或自适应。
Serial queries(串行查询):在单实例配置中,当DB_BIG_TABLE_CACHE_PERCENT_TARGET初始化参数设置为非零值时,串行查询可以使用大表缓存。
当一个表比内存块大时,数据库会根据访问模式决定缓存哪些缓冲区。例如,如果只有95%的受欢迎的表适合于内存,那么数据库就可以选择将剩下的5%的数据块放在磁盘而不是周期性的读取块到内存中并将块写入磁盘------这种情况被称为抖动现象。当缓存多个大型对象时,数据库会考虑缓存更流行更热的表而不是更冷的表,这将会影响到哪些数据块会被缓存。DB_BIG_TABLE_CACHE_PERCENT_TARGET初始化参数设置了在这种算法下的缓冲区缓存的百分比。
Buffer Writes(缓冲区写)
数据库写(DBW)进程会定期将冷端数据和脏缓冲区数据写入磁盘。
在下列情况下DBW将会触发写操作:
服务器进程无法找到干净缓冲区来缓存数据块。
当缓冲区被弄脏时,空闲缓冲区的数量就会减少。如果数字下降到一个阈值,此时又需要干净缓冲区,那么服务器进程就会发出信号让DBW进程来工作。
表空间被改变至只读状态或离线状态。
数据库必须推进在redo线程中检查点的位置,实例恢复也必须从该检查点开始。
Buffer Reads(缓冲区读)
当可利用的缓冲区数量很低时,数据库必须从缓冲区缓存中删除缓冲区。
该算法取决于是否启用了flash缓存:
flash缓存不可用
数据库在需要的时候会重新利用每个干净缓冲区,用新的数据块覆盖它。如果随后需要被覆盖掉的数据块,数据库必须再次从磁盘中读取。
flash缓存可用
DBW进程可以将一个干净的缓冲区的主体写入闪存缓存,从而使其内存缓冲区可以得到重用数据库在内存的LRU列表中保存了一个缓冲区头,以跟踪缓冲区主体在flash缓存中的状态和位置。如果稍后需要此缓冲区,则数据库可以从闪存缓存中读取数据而不是从磁盘读取数据。
当客户端进程请求缓冲区时,服务器进程将会搜索缓冲区的缓存。如果在内存中找到缓冲区,此时就会发生缓存命中。搜索的顺序如下:
1、服务器进程在整个缓冲区缓存中搜索缓冲区。如果服务器进程找到了整个缓冲区,数据库就会执行缓冲区的逻辑读操作;
2、服务器进程会在flash缓存LRU表中查找缓冲区头。如果进程找到缓冲区头,那么数据库将执行一个物理读操作将数据从闪存缓存读到内存缓存。
3、如果服务器进程在内存中找不到缓冲区,此时服务器进程会执行以下操做:
a、执行一个物理I/O读操作,将磁盘里的数据文件块复制一份到内存中
b、到内存中的缓冲区执行逻辑读操作
下图演示了缓冲区搜索顺序。扩展缓冲区缓存包括内存缓冲区缓存(包含整个缓冲区)和flash缓存(包含缓冲体)。在图中,数据库在缓冲区缓存中搜索缓冲区但没找到时,它会将数据块从磁盘读入内存。
一般来说,通过cache hit 访问数据比通过cache miss访问数据要快。缓冲区缓存命中率度量数据库在不需要从磁盘读取数据的情况下,缓冲区缓存中找到被请求数据块的频率。
数据库可以执行来自数据文件或临时文件的物理读取。从数据文件读取数据后遵循逻辑I/O。从临时文件中读取,当内存不足迫使数据库将数据写入临时表并且随后读取数据,此时就会产生一个临时文件。这些物理读取都会绕过缓冲区缓存,不产生逻辑I/O。
Buffer Touch Counts(缓冲区触摸计数)
数据库会使用一个触摸计数来测量LRU列表上的缓冲区访问频率。该机制使数据库能够在缓冲区被固定时增加计数器,而不是在LRU列表上不断调整缓冲区。
数据库不会物理地移动内存中数据块的位置,移动指的是改变指针在表中的位置。
当缓冲区被固定的时候,数据库会决定触摸计数是不是上次增加的。如果计数值是3秒前增加的,那么计数值就会增加;否则计数值保持不变。这个3秒法则会防止缓冲区触摸计数值爆发式增长。例如,一个会话可能会在一个数据块中插入很多行,但是数据库会将这些行看作一个触摸值。
如果缓冲区在LRU的冷端,但是它的触摸计数值很高,那么缓冲区就会被移动到热端。如果触摸计数值较低,那么缓冲区就会被移出缓存。
Buffer Pools(缓冲池)
缓冲池是缓冲区的集合。
数据库缓冲区缓存被划分为一个或多个缓冲池,它们以相同的方式管理块。缓冲池老化块和缓冲块的算法没有本质的区别。
你可以手动配置单独的缓冲池,这些缓冲池会将数据保存在缓冲区缓存中。也可以在使用完数据块后立即为新数据块指定可用的缓冲池。然后将特定的模式对象分配到适当的缓冲池,用来控制如何从缓存中使用块。例如,可以将段划分为热、温、冷的缓冲池。
缓冲池的类别如下:
・Default pool(默认池)
这个池是数据块通常被缓存的位置。除非手动配置单独的池,否则默认池是唯一的缓冲池。其它池的可选配置对默认池没有影响。
自从oracle Database 12c Release 1(12.1.0.2)开始,大表缓存是默认池的可选部分,它使用对象级基于热度的替换算法。在单实例和oracle rac数据库中,当DB_BIG_TABLE_CACHE_PERCENT_TARGET的值不为零并且PARALLEL_DEGREE_POLICY设置为自动时并行查询可以使用大表缓存。在单实例配置中,当DB_BIG_TABLE_CACHE_PERCENT_TARGET参数设置为非零值时,串行查询可以使用大表缓存。
・Keep Pool(保留池)
这个池是留给那些被访问得很频繁但是由于缺少空间不得不老化离开默认池的块。保留池的目的是在内存中保留对象,从而避免I/O操作。
保留池管理缓冲区的方式与其它池相同:它不使用特殊的算法来固定缓冲区。“Keep”是一个命名约定。你可以放置那些你想保存在大的保留池中的表,你也可以放置那些你不想保留在小的循环池里的表。
・Recycle Pool(循环池)
这个池是为那些使用频率很低的数据块准备的。循环池可以防止对象在缓存中消耗不必要的空间。
数据库有标准块大小。自己也可以创建一个与标准大小不同的块大小的表空间。每个非默认块大小都有它自己的池。oracle数据库管理这些池中的块的方式与默认池相同。
下图展示了当使用多个池时缓冲区缓存的结构。缓存包含默认池、保留池和循环池。默认的块大小是8KB。该缓存结构包含了用于表空间的单独的池,使用的非标准的块大小为2Kb、4Kb和16Kb。
缓冲区和全表扫描
数据库使用一种复杂的算法来管理表扫描。默认情况下,当必须从磁盘读取缓冲区时,数据库会将缓冲区插入到LRU列表的中间。这样的话热块就可以留在缓存中,它们就不需要再次被从磁盘中读取了。
如果进行全表扫描,就需要将表中高水位下所有的行按顺序读取。假设表段中所有块的总大小大于缓冲区缓存的大小。对该表的完整扫描可以清除所有的缓冲区缓存,这将会防止数据库维护被频繁访问数据块的缓存。
全表扫描的默认模式
默认情况下,数据库采用保守的方法进行全表扫描,只有当表大小是缓冲区缓存的一小部分时,才会将小表加载到内存中。
为了确定中等大小的表是否应该被缓存,数据库使用了一种算法,它包含了和上次表扫描之间的时间间隔,缓冲区缓存的老化时间戳,以及缓冲区缓存中剩余的空间。
对于非常大的表,数据库通常会使用直接读取的方式,使大表绕过SGA直接加载进PGA缓存中,从而避免占满缓冲区缓存。对于中等大小的表,数据库会采用直接读取或缓存读取的方式,如果决定使用缓存读取的方式,数据库会将数据块放在LRU表的末尾,以防止扫描有效清除缓冲区缓存。
从Oracle Database 12c Release 1开始,数据库实例的缓冲区缓存会自动执行内部计算来确定SGA的内存是否足够缓存整个数据库,如果缓存正在被访问的表对性能有什么益处。如果整个数据库大小小于内存大小,并且满足其他的内部条件,那么数据库就会把所有的表看作是小表,并且认为它们可以被缓存,但是数据库并不会缓存带有NOCACHE标记的LOBs。
Parallel Query Execution(并行查询执行)
在执行全表扫描的过程中,数据库有时可以通过执行多个并行服务器进程来提高响应速度。
在某些情况下,当数据库有大量内存时,数据可以在SGA中缓存并行查询数据而不是通过直接路径将数据读入PGA中。通常情况下,由于潜在资源的使用,并行查询会出现在低并发数据仓库中。
CACHE Attribute(缓存属性)
在不需要默认缓存行为的情况下,可以使用ALTER TABLE ... CACHE来更改将大表中的块读取到数据库缓冲区缓存的方式。
对于具有缓存属性集的表,数据库不强制将块放入缓冲区缓存中。相反,数据库会用和其它表块相同的方式将块从缓存中移出。执行此操作时要小心,因为对大表的全扫描可能会清除在缓存中的其它块。
ps:执行ALTER TABLE ... CACHE不会导致表被缓存。
KEEP Attribute(保留属性)
对于大表,可以执行ALTER TABLE ... STORAGE BUFFER_POOL KEEP扫描这些表的数据块并将表加载进保留池。
将一个表放置在保留池中,可以更改存储块缓冲区缓存的一部分,数据在默认缓冲池中缓存它们。没有单独的算法控制保留池缓存。
Force Full Database Caching Mode(强制全数据库缓存模式)
为了在某些情况下提高性能,可以执行ALTER DATABASE ... FORCE FULL DATABASE CACHING语句来启动强制全数据库缓存模式。
与默认的自动模式相比,强制全数据库缓存模式认为包括NOCACHE LOBs在内的整个数据库都可以在缓冲区中缓存。此模式自Oracle Database 12c Release 1(12.1.0.2)开始。
启动全数据库缓存模式并不会强迫数据库进入内存。指的是整个数据库都具备被完全缓存在缓冲区缓存的条件,oracle数据库只在访问它们时缓存这些表。
oracle建议,只有当每个单独实例的缓冲区缓存大小大于数据库大小时,才能启用全数据库缓存模式。这条准则适用于单实例和oracle RAC数据库。但是,当oracle RAC应用程序进行良好分区并且所有实例的组合缓冲区(在实例间处理重复的缓存块的区域)容量比数据库大小更大时,可以启用全数据库缓存模式。
In-Memory Column Store(内存中的列存储)
从oracle 12c开始,内存中的列存储(IM列存储)是一个可选的静态SGA池,它存储了一个特殊的列格式的表和分区的副本,以进行快速扫描。
IM列存储并不能替代缓冲区缓存,而是作为一种补充,以便两个内存区域可以用不同的格式存储相同的数据。默认情况下,只有使用DDL指定为INMEMORY的数据对象才会被填充到IM列存储中。
对于填充在IM列存储的中的对象来说,不需要加载到数据库缓冲区缓存中。
列格式只存在于内存中。下图显示了存储在IM列存储中的sh模式的三个表:客户、产品和销售。IM列存储通过列而不是通过行来存储数据。数据库使柱状数据与缓冲区缓存保持一致。
IM Column Store的好处
IM列存储支持数据库执行扫描(scans)、连接(joins)和聚合(aggregates),比只使用on-disk模式要快的多。
通常IM列存储在如下情况可以被用到:
・扫描大量行的查询,并将使用诸如下列运算符的筛选器:=,< >
・从表或物化视图中选择一个小的列的查询,有大量的列,如从表中100列选择5列的查询
・将小的表连接到大表的查询
・聚合类的查询
业务应用程序、特别的分析查询和数据仓库工作负载最受益。而使用索引查找执行短事务的纯粹的OLTP数据库获益则比较少。
IM柱状存储还有如下优点:
・不需要更改应用
优化器会自动利用列格式
・配置简单
INMEMORY_SIZE初始化参数指定了在IM列存储中保留的内存数量。DDL语句可以指定要读入IM列存储的表空间、表、分区或列。
这些压缩技术使会话将更多的数据读入内存,从而提高了有效的内存带宽。
・需要更少的索引、物化视图和OLAP多维数据集
预构建对象数量的减少导致存储空间的减少,使处理开销明显减少。
双存储器格式:列和行
当获取数据时,oracle数据库可以读取IM列存储或数据库缓冲区缓存,或者是在同一个查询中同时读取列缓存和缓冲区缓存。
数据库会将OLTP查询(例如主键查找)发送到缓冲区缓存,并将解析和查询报告发送到IM列存储。因此,双内存格式是最好的选择。
在执行计划中,“TABLE ACCESS IN MEMORY FULL”可以指定使用IM列存储。
下图显示了一个示例IM列存储。sales表以传统的行格式存储在磁盘上。SGA在IM列存储中存储柱状格式的数据,并在数据库缓冲区缓存中以行格式存储数据。
IM列存储支持每个永久的、有组织的表的on-disk数据格式。列格式不影响存储在数据文件或缓冲区缓存中的数据格式,也不影响撤销、联机重做日志记录等。
无论IM列存储是否在使用,数据库都会以相同的方式处理DML修改:通过更新缓冲区缓存、联机重做日志文件、撤销表空间等。数据库会使用内部机制跟踪更改确保IM列存储与数据库的其余部分保持一致。例如,如果sales表驻留在IM列存储中,此时sales表中的一行被更新,那么数据库将自动确保IM列存储中的sales表的副本与事务保持一致。访问IM列存储库的查询结果总是返回同查询缓冲区缓存相同的结果。
IM列填充
数据库以行格式从磁盘读取数据,并将行转到创建列,然后将数据压缩到内存压缩单元(IMCUs)。
Wnnn进程在IM列存储中填充数据。每个worker进程都在对象的数据块的子集上操作。Population是一种流媒体机制,同时压缩数据并将其转换为柱状格式。
INMEMORY_MAX_POPULATE_SERVERS初始化参数指定用于IM列存储的worker进程的最大数量。默认情况下设置为cpu_count的一半。将此参数设置为系统环境下的最优值。更多的worker进程会让population更快,但是会使用更多的cpu资源;更少的工作进程导致了更慢的population,但是这样会减少cpu开销。
如果INMEMORY_MAX_POPULATE_SERVERS被设置为0,则population不可用。
实例启动时,IM列存储的population
每次数据库实例启动时,数据库都必须从磁盘重新构造完整的IM列格式。这个重构是十分必要的,因为IM列存储只驻留在内存中。
在响应查询时,IM列存储的population
在对象上设置IM属性意味着该对象是IM列存储中的一个候选对象,而不表示数据库立即会将对象填充到内存中。
默认情况下(内存优先级设置为NONE),数据库会推迟向IM列存储中填充表,直到数据库认为该表有用。当对象被设置IM属性时且数据库认为内存在其他地方有更好的用途,此时数据库可能不会选择将所有列填充到IM列存储中。IM列存储可以从表中填充列的子集。
下面是IM列存储中对象的填充过程:
假设你作为管理员连接到数据库。为了确定来自sh.customers表的数据是否被填充到IM列存储中,可以执行以下查询:
在本例中,由于表sh.customers此前未被访问过,所以没有在IM列存储区中填充段。先查询表sh.customers,然后再查询V$IM_SEGMENTS:
内存中的柱状压缩
IM列存储使用特殊的压缩格式是针对访问速度而不是减少存储。
数据库采用以下方式提高访问速度:
・压缩格式可以减少每列需要处理的内存总量,sql可以直接执行在压缩列上。
・数据库使用SIMD向量指令在一个cpu时钟周期中处理一组列值。在一个VECTOR上存储很多值,可以最大的利用SIMD处理性能。
可以在CREATE和ALTER语句的子句中使用MEMCOMPRESS语句来选择不同的压缩比。
默认的压缩选项是MEMCOMPRESS FOR QUERY LOW。这个选项提供了最快的读取速度,因为在这个模式下数据库不需要解压缩数据。其它的压缩选项,如FOR QUERY HIGH和FOR CAPACITY模式会使用层压缩,其中一些模式需要解压缩。在使用IM列存储存储表之前可以使DBMS_COMPRESSION。GET_COMPRESSION_RATIO可以报告压缩比,用它来估计需要用多少空间。
IM列压缩和混合列压缩有点相近,都需要处理一组列。主要区别是,IM列存储的列向量针对内存存储优化过,而混合压缩的列向量为磁盘存储优化过。
IM柱状压缩与混合柱状压缩密切相关。这两种技术都涉及到对列向量的处理集。主要的区别在于IM列存储的列向量是对于内存存储进行了优化,而混合式柱状压缩的列向量是对磁盘存储进行了优化。
IM列存储的扫描操作
列格式使得查询只需要扫描需要的列。
当使用缓冲区缓存时,数据库通常会扫描一个索引来查找产品ID,使用rowid将行从磁盘取出到缓冲区缓存,然后丢弃不需要的列值。在缓冲区缓存中以行格式扫描数据需要许多cpu指令,并且可能会导致cpu效率低下。
当使用IM列存储时,数据库只需要扫描请求的销售列,从而避免使用昂贵的磁盘I/O。在列格式管道中扫描数据只需要cpu对必要的列进行扫描从而可以提高效率。每个cpu核心使用SIMD向量指令扫描本地内存列。
重做日志缓冲区
重做日志缓冲区是SGA中的一个循环缓冲区,它用来存储重做条目,描述对数据库的更改。
重做记录是一种数据结构,它包含了通过DML或DDL操作对数据库进行重构或重做的必要信息。数据库恢复就是用重做日志在数据文件中重构丢失的更改。
数据库会将重做记录从用户内存空间复制到SGA中的重做日志缓冲区。重作记录会在缓冲区中占据连续的空间。LGWR后台日志写进程会将重做日志缓冲区的日志写入磁盘上的活动联机重做日志组。下图显示了重做日志缓冲区的原理:
LGWR将redo顺序写到磁盘上,而DBWn则将数据块分散的写到磁盘上。分散地写往往会比顺序写慢得多。由于LGWR进程使用户避免了等待DBWn完成它的慢写操作,从而使数据库具有更好的性能。
LOG_BUFFER初始化参数可以指定当缓冲重做记录时Oracle数据库使用的内存总量。与其它SGA组件不同,redo log buffer和fixed SGA buffer不会将内存划分为颗粒。