Oracle数据库由两部分组成:实例和数据库
实例是由一块共享内存区域以及一些后台进程组成。
1.SGA
SGA为共享内存区域,也叫系统全局区
SGA的特点:
-是共享的,是所有使用当前实例的用户都可以读取的内存部分。
-一个SGA只能服务一个实例,多实例间不能互相访问SGA。
-实例关闭的时候,系统回收内存,SGA即消失。
如图所示SGA中包含如下部分:
1.shared pool
2.database buffer cache
3.redo log buffer
4.java pool
5.large pool
6.stream pool
7.fixed sga
一:share pool共享池
包含以下几个部分
1.library cache:该区域主要的作用在于存放SQL语句的执行计划,以及函数,存储过程的编译结果,用于SQL共享。
2.Data Dictionary Cache:数据字典缓存,Oracle在运行时,需要不断的通过数据字典来验证用户的权限以及模式对象的状态。
3.result cache:结果集缓存,服务进程可以直接在result cache中获得结果来返回给客户端。
SQL语句的执行过程:
当客户端通过监听与服务端建立连接后,服务端会启动一个服务进程为其处理请求,当客户端执行一条SQL语句时,服务进程首先会进行语法,语义的检查,如果检查无误,会将该SQL语句进行hash运算,然后服务进程会拿着hash值去library cache中去寻找是否有相同的hash值,如果有相同的hash值,说明存在相同的执行计划,直接执行即可,也叫软解析。如果没有相同的hash值,说明不存在相同的执行计划,此时要进行硬解析,也就是生成执行计划的过程,Oracle会选择最优的执行计划来执行,并将该执行计划放到library cache中。
在share pool中最小的内存块为chunk,共享池内存的分配,就是不断重用,切割的过程。能找到对应大小的,就使用对应大小的chunk,找不到就从更大的chunk中切割出符合要求大小的chunk使用,而这个更大的chunk被切割后剩余的部分,将按照其大小重新链到链表上。
例如:当进行硬解析时,需要去Free Lists中寻找合适大小的chunk存放执行计划。如果一个执行计划需要的空间为10k,如果没有10k的chunk,找到了11k的chunk,那么就会将11k的chunk进行切割,分为一个10k的chunk以及1个1k的chunk,10k的chunk用于存放执行计划,将其挂到library cache的链表中,1k的chunk继续将其挂载到Free Lists上。
二:database buffer cache数据库高速缓存
主要作用:减少物理IO
buffer cache分为以下几个区域:
default:当一个数据块读入buffer cache中,默认会进入该区域,该区域的buffer使用LRU链表(下面解释)进行管理。
keep:在该区域的buffer,不会因为内存空间不足而被置换出内存,会长久存在于buffer cache中。
recycle:回收区域,读入一次以后,就将其置换出内存。
nk:非标准块缓冲区大小,常用于非标准块传输表空间。
LRU链表:buffer cache中defautt所有的buffer都会被链到LRU链表上用于管理,LRU链表上记录了每个buffer被访问的次数,在进行物理读前,需要通过LRU链表找到可以覆盖的buffer,LRU链表又分为主LRU以及辅助LRU链表(下面会叙述)。
通过物理读来说明LRU链表的作用。
物理读的过程
首先服务进程会在buffer cache中寻找可以覆盖的buffer,也就是搜索LRU链表,首先从辅助LRU链表尾部开始搜索可以覆盖的buffer,可以覆盖的标准为TCH小于2的,不可以覆盖的标准为
(1)TCH大于2的
如果搜索到TCH大于2的buffer,会将其移动到热端头并将TCH清0。
原因:因为是被频繁访问的buffer,避免重复读入,将其保留在内存中。
(2)TCH大于2的脏块
如果搜索到TCH大于2的脏块,也会将其移动到热端头并将TCH清0。
原因:因为是被频繁修改的脏块,为了避免重复多次读入,将其保留在内存中。
(3)TCH小于2的脏块
如果搜索到TCH小于2的脏块,服务进程会将其移动到LRUW链表中(唉,LRUW链表会在后面加以叙述,与DBWR写进程有关)
原因:由于访问频率低于2,为了加快下次搜索LRU链表的速度,将其移动到LRUW链表中,等待DBWR 3秒醒来将该脏块写回数据文件。
最后如果搜索到TCH小于2的buffer,服务进程会将其移动到冷端头,等待被覆盖,这个buffer就是我们需要的buffer,然后再发起一次物理读,将数据块读取到该buffer中,继续被LRU链表管理。
辅助LRU链表:
辅助LRU链表的出现就是加快可覆盖buffer的搜索效率,因为SMON进程会定期会搜索主LRU链表,找到一些TCH小于2的buffer,将其放到辅助LRU链表中,当进行搜索时,从辅助LRU尾部开始搜索,这样就可以更加快速的寻找到可覆盖的buffer,有时可能比较繁忙,辅助LRU链表上没有buffer,那么就会从主LRU链表尾端开始搜索。
逻辑读的过程:
直接从buffer cache中读取需要的数据为逻辑读
那么如何得知需要读取的数据是否存在于buffer cache中呢:
1.Oracle服务进程会读取数据字典,来找到所需要的数据的文件号,块号。
2.首先服务进程根据文件号,块号进行hash运算,生成hash值。
3.根据运算的hash值,直接定位到hash bucket。
4.搜索hash bucket后的CBC链表。
(hash bucket存放的是指向CBC链表头的指针)
5.找到目标BH(buffer header)。
6.从BH中找到目标BA(buffer address)
7.服务进程根据buffer address去访问buffer。
如果搜索不到buffer,那么就说明所需要的数据不存在于buffer cache中,此时就需要进行物理读。
cbc latch,buffer pin lock这两个锁,此处不再叙述,有兴趣的可以自己看下。
Buffer cache中buffer的类型:
unused:一次也未被使用的。
clean:干净的 –说明buffer里面的数据信息和数据文件对应的数据块的存储内容是一致的。
dirty:脏块 –说明buffer里面的数据信息和数据文件对应的数据块的存储内容不一致。
buffer的状态:
0.Free 空闲块
1.Xcur 从数据文件中读到内存中的块,(单实例下的独占当前块)
2.Scur 多实例下的共享当前块(RAC)
3.CR 读时可以读CR或Xcur,修改时只能修改xcur。
4.Read 正在从数据文件中读。
5.Mrec 介质恢复
6.Irec 实例恢复
7.Write 正在往磁盘的数据文件写。
一致性读情况CR块:
当A会话update修改一行数据从1修改为2,修改前会将buffer的后映像记录到redo中,会将buffer的前映像记录到undo块中,此时事务未提交。
当B会话进行读取这一行数据时,服务进程检查该buffer头部有未提交的事务,所以会复制一个当前块,结合这个undo块,生成一个CR块,也称为一致性读块。
另一种情况下的CR块:
读不阻塞写,先读后写:
简单的说:A会话正在读取一个buffer B会话想要修改这个buffer,服务进程会将源buffer进行clone克隆,然后将源buffer的状态改为CR,将新复制的buffer的状态改为xcur。
复杂的说:
因为当一个会话A读取buffer时,首先获得CBC latch,然后修改buffer pin的锁为共享模式,然后释放CBC latch,开始读取,此时会话B想要修改buffer,首先获得CBC latch,发现buffer pin上有共享锁,B会话进程会克隆一个这个buffer,克隆后,将源buffer的状态改为从XCUR改为CR,新复制的buffer状态改为XCUR,然后获取CBC latch,修改新复制的buffer 的buffer pin为独占模式,准备修改buffer。
当此时再有一个会话C的进程读取buffer时,需要读取这个xcur块,读取需要修改这个buffer pin的状态为共享模式,但此时发现该buffer上有独占buffer pin。所以只能等待,此时会产生buffer busy wait等待事件。
三:redo buffer在线重做日志缓冲区
作用:记录数据块的变化,实现Oracle的快速提交特性,以及确保已提交的数据不会丢失。
数据块进行修改前,会将其修改后的数据(后映像数据)记录到redo buffer中,实现是先记录日志,再修改buffer。
生成redo buffer的过程:先在PGA中记录后映像数据,将其移动到redo buffer中。
快速提交:当一个事务提交后commit,如果想要数据不丢失,那么就需要将内存中修改的数据块写回数据文件,如果commit命令驱使在内存修改完的数据块写回数据文件的话,那么commit的等待时间会非常的长,由于数据块的写入属于离散写,并且如果事务很大,那么需要写回的数据块就会更多,commit等待事件也会更长,Oracle为了实现事务快速提交机制,引入了redo日志的机制,commit仅会触发LGWR将redo buffer中的信息写入到磁盘的redo log中,并且只有redo buffer成功写入到redo log中,commit命令才会成功,并且由于redo buffer非常的小,而且写入redo log也属于顺序写,不需要进行磁盘寻道,这也是快速的原因。并且redo log记录的是数据块的变化,当实例崩溃时,Oracle会自动读取redo log来实现实例恢复,来确保已提交的事务不会丢失。
redo buffer中的记录,生成后不会马上写到磁盘上的redo log文件,LGWR会根据自己的触发条件将其写入redo log中.。
redo log的作用:
1.实例恢复
2.备份恢复的介质
3.DG standby database的使用
4.日志挖掘
2.PGA
PGA也叫程序全局区,是一块非共享区域。
每一个服务进程以及后台进程会分别会有一个PGA,一般所说的PGA都是服务进程的PGA,服务进程也就是用来处理客户端请求的,服务进程所需的内存区域就为PGA。
当进行排序运算,以及多表关联,hash运算时,会在PGA中的SQL Work Area区域进行,当PGA空间不足时,超出部分会在临时表空间中进行运算。
Oracle服务进程处理请求分为两种模式:
1.独占模式:Oracle会为每一个客户端请求都分配一个服务进程,来处理请求,此时PGA与UGA是在一起的。
2.共享模式:Oracle会为多个客户端请求分配几个服务进程,来共享处理请求,此时PGA与UGA是分开的,UGA会存放在share pool中,如果配置了large pool,那么UGA就存放在large pool中。
UGA:简单的说就是存放会话信息的内存区域。