oralce数据库管理-详细解析oracle数据库update整体执⾏过程
客户端SQL Plus请求连接,监听接受客户端的TCP连接,并获取客户端发过来的TNS数据包。
监听进程打开⽤于与⼦进程通信的管道,同时fork⼀个⼦进程,称为“监听⼦进程1”的⼦进程,然后监听进程⼀直等待,直到这个“监听⼦进程1”结束。
监听⼦进程1 Fork出⼦进程2。
完成上⾯⼀步,⼦进程1马上退出并结束⼦进程1。
⼦进程2收集本进程所在的主机名、IP地址及进程号等信息,并把⼦进程2重名成server process(这⾥我们也把server process叫前台进程或叫服务器进程),申请占⽤⼀⼩块PGA内存。
前台进程把主机名、IP地址及进程号发送给监听进程。
监听进程收到前台进程的信息,并返回客户端的信息(⽐如⽤户密码环境变量等)给前台进程。
前台进程查询USER$、PROFILE$等数据字典,校验⽤户名密码是否合法,如果⽤户密码错误就报错⽤户名密码⽆效,否则就与客户端进⾏交互。
客户端收到前台进程的信息与之交互,整个连接创建完成。
客户端发起⼀个连接,服务器端的监听创建⼀个shadow process,并把这个影⼦进程指派给⽤户,后期⽤户所有的操作都提交给shadow,shadow会替⽤户完成数据库内的操作,并把结果返回给⽤户。(中间件有点不同)
客户端发起update语句,根据客户端环境字符集转换成对应的编码,传到服务器端,
数据库端会的server进程,与⽤户进程组成⼀个会话。然后在server端的pga区,处理sql请求。服务器端根据服务器端字符集转换。
process接受语句,放在PGA中,将修改前的值放在PGA
在⽤户的private sql area区域,每个字符包括空格转化成ASCII码后,再拿这⼀堆ASCII码通过HASH函数⽣成⼀个sql_hash值
确定这条语句的执⾏计划
搜索当前⽤户的session缓存中(在PGA中的UGA)是否存在相同的散列版本。
如果存在,则直接通过游标link到位于PGA的private SQL AREA( private SQL area),此时成为软软解析。
如果不存在,检查初始化参数SESSION_CACHED_CURSORS是否被设置,如果被设置,且有相同的sql,则同样可以通过游标指向到位于PGA的私有SQL AREA,否则,结束软软解析,尝试软解析。
批量更新sql语句如果不存在,创建⼀个游标。拿这个值去shared pool中执⾏计划,根据hash值,判断这个块应该在哪个桶中。
从内存中申请⼀个lock structure,在其中记录“锁模式,进程ID”等重要信息
然后看能否⽴刻获得资源访问权,如果不能,则把这个lock structure 挂到resource structure的waiter链表中,如果能获得,则把lock structure 挂到resource structure的owner链表中。
锁定数据块头链表,逐个遍历上⾯的块头,看有没有与这条语句相同的哈希值
如果有,如果检查到共享库中有⼀个语句具有相同的哈希值,则数据库执⾏语义和环境检查, 以确定其含义是否相同。即使两个语句在语义上是相同的,某个环境差异也可能使其强制进⾏硬解 析。在这种情况下,环境是可以影响执⾏计划⽣成的全部会话设置, 如⼯作区⼤⼩或优化器设置等。取出,执⾏软解析
否则,执⾏硬解析
检查sql的语法、语义、权限,
查询相关的数据字典
根据CBO或者RBO⽣成执⾏计划
CBO,直⽅图,动态采样
Oracle⾸先扫描shared pool的freelist中是否有⾜够空间,如果有则使⽤,如果没有⾜够的空间,则判断此次内存请求是⼀次large请求,还是⼀次small请求 。
若是large请求,则在reserved pool 查是否有可⽤的空间,如果到了可⽤的内存(chunk)则作size检查,并对内存(chunk)做截断操作,截取所需内存⼤⼩使⽤,如果在reserved pool 中依然没有到可⽤的内存(chunk),会重复上⼀步,如果依然没有到,则对reserved pool 中的对象做LRU算法的age out操作,age out⼀些reserved pool 中的对象,来满⾜本次的内存(chunk)请求操作,如果还是没有到可⽤的内存,重复LRU算法,直到到可⽤内存(chunk)。
若是small请求,则在shared pool 的free list中查是否有可⽤的内存(chunk),如果到可⽤的内
存(chunk)则作size检查,并对内存(chunk)做截断操作,截取所需内存⼤⼩使⽤,如果没有到,则对shared pool中的对象做LRU算法的age out操作,并再次查是否有可⽤的内存,知道到可⽤的chunk
shared pool
查空闲内存,如果没有发现⼤⼩正好合适的空闲chunk,就查更⼤的chunk,如果到⽐请求的⼤⼩更⼤的空闲chunk,则将它分裂,写⼊执⾏计划,挂到library cache的链上,多余部分继续放到空闲列表中。因为过多的硬解析加剧了内存段分配的需求,这样就产⽣了碎⽚问题。系统经过长时间运⾏后,就会产⽣⼤量⼩的内存碎⽚。当请求分配⼀个较⼤的内存块时,尽管shared pool总空闲空间还很⼤,但是没有⼀个单独的连续空闲块能满⾜需要。这时,就可能产⽣ ORA-4031错误。
完成硬解析
完成解析
如果使⽤了,绑定变量,将实际的变量值代⼊SQL语句中。
读取要修改的数据块。
在dbbuffer中要修改的块
查询SEG$等数据字典,到要修改表段头
从段头读出Extent Map,按执⾏计划开始扫描数据块
将块所在的file#,block#哈希算计算后,相应hash bucket,获得保护这个bucket的cbc latch
遍历桶中所有CBC链。获得cache buffers chains latch,遍历那条buffer chain直到到需要的buffer header。此时需要注意,如果执⾏计划是⾛全表扫描,或者是带有唯⼀约束的索引(update),是以独占模式获取CBC链,会引起CBC链busy。
⽐较buffer header上所记录的数据块的地址(rdba),如果不符合,则跳过该buffer header。
跳过状态为CR的buffer header。(说明有别的进程正在进⾏⼀致性读,所以才构造了这个cr块,如果我也要这个块的原块,我需要⾃⼰再重新构造⼀个新的cr块,不会使⽤这个旧的cr块,如果我不是这个块的原块,那我不需要构造,所以这两种情况下都是跳过cr块)
如果遇到状态为READING(正在从磁盘上读出的数据块)的buffer header,则等待,⼀直等到该buffer header的状态改变以后再⽐较所记录的数据块的地址是否符合。(说不定是之前的查询,有可能就是这条sql语句,也有可能是之前的(⾃⼰⽤户或者其他⽤户的sql)语句,正好也需要读这个块内的数据,正在往内存⾥读,这下我就可以直接⽤前辈的努⼒就可以了)
block在内存中。如果发现数据块地址符合的buffer header,则查看该buffer header是否位于正在使⽤的列表上,如果是位于正在使⽤的列表上,则判断已存在的锁定模式与当前所要求的锁定模式是否兼容,如果是兼容的,则返回该buffer header所记录的数据块地址,并将当前进程号放⼊该buffer header所处的正在使⽤的列表上
根据需要进⾏的操作类型(读或写),它需要在buffer header上获得⼀个共享或独占模式的buffer pin或者buffer lock
若进程获得buffer header pin,它会释放获得的cache buffers chains latch,然后执⾏对buffer block的操作,若进程⽆法获得buffer header pin,它就会在buffer busy waits事件上等待。(进程之所以⽆法获得buffer header pin,是因为为了保证数据的⼀致性,同⼀时刻⼀个block只能被⼀个进程pin住进⾏存取,因此当⼀个进程需要存取buffer cache中⼀个被其他进程使⽤的block的时候,这个进程就会产⽣对该block的buffer busy waits事件。)
在段头块上还是⽬标块?加上TM、TX锁,获取⽬标块的latch,通过块头到块在内存中的地址,
如果有锁的冲突,则产⽣阻塞。
wait⽅式
pin :当⼀个会话⽆法获得需要的latch时,会继续使⽤CPU(CPU空转),达到⼀个间隔后,再次尝试申请latch,直到达到最⼤的重试次数。
sleep:当 ⼀个会话⽆法获得需要的latch时,会等待⼀段时间(sleep),达到⼀个间隔后,再次尝试申请latch,如此反复,直到达到最⼤的重试次数。
no wait⽅式
不会发⽣sleep或者spin., 转⽽去获取其它可⽤的Latch
如果是⼲净块,修改完后,挂到ckpt链上,在LRU链上的使⽤次数+1,(特定条件后)移动到LRUW上,并添加到检查点链
block在硬盘。 如果⽐较完整个hash chain以后还没发现所要的buffer header,则从磁盘上读取数据⽂件。
会话产⽣⼀个shadow process将块读到内存,
在读取数据之前,Server进程需要扫描辅助LRU List寻Free的Buffer,扫描过程中Server进程会把发现的所有已经被修改过的Buffer 注册到LRUW List上
如果Server进程扫描LRU超过⼀个阀值仍然不能到⾜够的Free Buffer,将停⽌寻,转⽽通知DBWn去写出脏数据,释放内存空间。这时进程会处于free buffer wait等待
(⾮全表扫描)到⾜够的Buffer之后,Server进程就可以将Buffer从数据⽂件读⼊Buffer Cache,并将buffer移动到主LRU,如果⾮全表扫描较多,辅助LRU中块会越来越少,为了保持⽐例(辅助LRU占整个LRU总块数的20%到25%左右),SMON进程会3秒⼀次持有LRU
Latch,将主LRU冷端末的块移到辅助LRU。全表扫描也先到辅助LRU中寻可⽤块,但全表扫描的块仍将留在辅助LRU,不会调往主LRU冷端头。因此全表扫描的块将很快被覆盖。全表
扫描操作将只使⽤辅助LRU(也会⽤到主LRU,只会⽤到很少量的主LRU),⼀次⼤的全扫操作,可以将辅助LRU的所有块覆盖⼀遍或多遍。数据库刚启动时,或刚Flush Buffer_cache时,所有块会被放在辅助LRU中。前台进程(服务器进程)扫描主、辅LRU时,会将遇到的TCH为2以下的脏块,移到主LRUW。
将块所在的file#,block#哈希算计算后,相应hash bucket,获得保护这个bucket的cbc latch
将读取到的数据块所对应的buffer header挂到CBC(cache buffer chain)链上,块放到对应链的buffer上,头的地址指向链上的对应的buffer块。
物理/逻辑⼀致性检查。(计算这个数据块的校验和,并和块头的字段相⽐较,如果有差异,oracle就知道这个块有错误,会报出ORA-1578错误,会对数据块做cache recovery,如果不能把数据块恢复到⼀致状态,oracle会把这个数据块标志位software corrupt。如果是其他错误,则需要使⽤dbms_repair包吧数据块标识为“software corrupt”,块头的校验和字段在写回磁盘前会进⾏重新计算。执⾏与否受参数db_block_checksum影响)
Dbv只检查数据块的header/footer,做逻辑验证;
Db_block_checking:替代10210/10211/10212事件,进⾏块完整性检查,如free slot list/⾏位置/锁数量;检查时会复制块,如有错误将块标志为soft corruption;
Db_block_checksum:dbwr和direct loader写数据块时计算checksum并存于cache层chkval,再次读取时重新计算并与已有checksum⽐较;
Dbms_repair修复cache/transaction层的错误,将块标⽰为soft corruption;
对数据块进⾏逻辑⼀致性检查。检查失败会抛出ORA-1578的internal错误。当oracle检查到数据块的逻辑⼀致性时,会对数据块做cache recovery,如果不能把数据块恢复到⼀致状态,oracle会把这个数据块标志位software corrupt,当有查询访问到这个数据块时,也会抛出ora-1578错误。如果不是抛出ORA-1578,则需要先使⽤dbms_repair包。
先进⾏⼀致读,出要修改的⾏
在LRU链的冷端⼀个⼲净的块,将数据块放到其中,如果寻达到⼀定数⽬的块,还未到⼲净块,则将LRU链冷端的脏块,迁移到LRUW链上,?
释放cbc latch
检查要修改的⾏上有没有锁标记
如果有,根据对应的事务槽,到相应的回滚段,看事务表中记录的事务是否提交
如果提交,去处所标记,将对应的修改事务槽事务状态为U,进⾏下⼀步。(C=Commited;U=Commited Upper Bound;T=Active at CSC;---是未提交 )
如果回滚段事务表中事务信息被覆盖,则认为事务已经提交,去处锁标记,将对应的修改事务槽事务状态为U,进⾏下⼀步。
如果未提交,则等待其提交,产⽣buffer busy 等待事件
如果没有,进⾏下⼀步。
将该BH的标记设置为钉住(ping)
检查有没有before类型触发器,如果有执⾏(在此过程中:new的值可能会被重新赋值)。
随后,oracle以当前模式读这个块,如果查这⼀⾏的(where)条件列已经修改过。由于使⽤条件列来定位这条记录,⽽且条件列已经修改,所以数据库会重启查询。
从shared pool中分配的⼀块内存划分给⼀个private stand,并受到redo allocation latch的保护,这个事务⽣成的redo存放在private stand中,当flush private stand 或者commit时,private stand被批量写⼊log⽂件中,对于使⽤Private strand的事务,⽆需先申请Redo Copy Latch,也⽆需申请Shared Strand的redo allocation latch,⽽是flush或commit是批量写⼊磁盘,因此减少了Redo Copy Latch和redo allocation latch申请/释放次数、也减少了这些latch的等待,从⽽降低了CPU的负荷。(redo log 最开始是在pga中的uga 产⽣的(数据库⼀般是专有模式),oracle会把它拷贝到SGA中的log_buffer中去,如果log_buffer过⼩,或者lgwr不能够快速将redo 写⼊到log file中,那么就会产⽣log buffer space等待事件,遇到此类问题,可以增加 log_buffer⼤⼩,调整log file 到裸设备,I/0快的磁盘中)
在PGA中⽣成DataBlock块的后映像(11.9)。
在PGA中⽣成UNDO段头事务表的后映像(5.2)。
在PGA中⽣成UNDO块的后映像(5.1)
将前三个Redo⽮量做为⼀条Redo Recorder写⼊Shared pool中的Private strand。
将DataBlock中的前映像值,写⼊Shared pool中的Imu pool。
修改UNDO段头的事务表。
修改UNDO块,写⼊DataBlock的前映像。
将每条还原改变向量写到对应的IMU池,或者依照旧的机制;
将每条重做改变向量写到私有redo去;
如果新事务申请不到private stand的redo allocation latch,则会继续遵循旧的redo buffer机制,申请写⼊shared strand中。
在PGA中构建change vector并组合成redo record
在PGA中⽣成UNDO段头事务表的后映像(5.2)
在PGA中⽣成UNDO块的后映像(5.1)
在PGA中⽣成DataBlock块的后映像(11.9)
将前三个Redo⽮量做为⼀条Redo Recorder写⼊Log buffer( 重做⽇志记录在pga中复制到redo buffer)
修改UNDO段头的事务表,事务正式开始。
修改UNDO块,写⼊DataBlock的前映像。
改变这个块
取消block 的pin标记
修改DataBlock,将新值写⼊Buffer cache。
调⽤kcrfwr()将record写⼊log buffer:
计算record占⽤的空间⼤⼩;分配SCN;
获取copy latch,验证SCN;
获取allocation latch,检验log buffer/file是否有⾜够空间,有则释放allocation latch将redo写⼊log buffer,否则同时释放
allocation/copy latch并通知LGWR进⾏log flush/switch;(为防⽌多个进程同时通知LGWR刷新redo或切换⽇志⽂件,引⼊write latch(只有1个),只有获取此latch后才能进⾏下⼀步操作;)
将redo record写⾄log buffer⽽后释放copy latch,检查是否达到触发LGWR阈值;
更新BH对应的内存数据块的内容。
更新BH对应的内存数据块的内容。
do块
志先记录到PGA中,再写回undo
commit;
为事务⽣成⼀个scn,lgwr将所有余下的缓存重做⽇志条⽬写⾄磁盘,并把scn记录到在线重做⽇志⽂件中。
事务条⽬从v$transaction中删除
v$lock中记录会话所持有的锁,全部释放,等待排队这些锁的每个事务将会被唤醒,继续完成他们的⼯作
如果事务修改的某些块还在缓冲区缓存中,则会以⼀种快速的模式访问并“清理”(指清除存储在数据库块⾸部与锁相关的信息。
全表扫描时出现单块读:当表中有⼀些块存在时,全表扫描,会跳过这些块,这也是在全表扫描时,出现单块读的原因,还有⼀种情况是,到区的边界,也有可能出现单块读。欲成佛,先成莫
XID 回滚段,段的⽂件编号,块号,⾏号,使⽤次数

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。