oracle-1条updata的故事

客户端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,否则,结束软软解析,尝试软解析。

如果不存在,创建一个游标。拿这个值去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

修改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 回滚段,段的文件编号,块号,行号,使用次数

update慢的因素:

经常update的列不宜采用索引(体系结构P237)

尽量不要使用触发器,会使update变慢

触发器中尽量不要使用before for each row,没有after for each row 高效(体系结构P238)

原文地址:https://www.cnblogs.com/priestess-zhao/p/8366111.html

时间: 2024-10-12 12:05:45

oracle-1条updata的故事的相关文章

Oracle一条SQL语句时快时慢

今天碰到一个非常奇怪的问题问题,一条SQL语句在PL/SQL developer中很慢,需要9s,问题SQL: SELECT * FROM GG_function_location f WHERE f.parent_id ='03000000000001';  表GG_function_location有5千万的数据,parent_id上是有索引的. 诊断第一步:就在PL/SQL developer中按F5,看到的执行计划是走索引的,应该不会慢啊. 第二步:在sqlplus中用autotrace

Oracle 一条sql插入多条数据

Oracle一次插入多条数据. 表结构: create table aa ( ID NUMBER(11) PRIMARY KEY, NAME VARCHAR2(20) ) 第一种方式: insert into aa (ID,NAME) select 1,'1' from dual union  allselect 2,'2' from dual 第二种方式: INSERT ALLINTO aa (ID,NAME ) VALUES (3,'3')INTO aa (ID,NAME ) VALUES

oracle一条sql语句统计充值表中今天,昨天,前天三天充值记录

select NVL(sum(case when create_date_time>=to_date('2014-11-24 00:00:00','yyyy-mm-dd hh24:mi:ss') and create_date_time<=to_date('2014-11-24 23:59:59','yyyy-mm-dd hh24:mi:ss') then amount end),0) today ,NVL(sum(case when create_date_time>=to_date(

Java的第20年:Java和我的故事

??Java技术诞生于1995年的5月23日,这样一个在程序设计领域长时间占据统治地位并且拥有最好的生态系统的语言起初只是太阳微系统公司(Sun Microsystems)一个失败的机顶盒项目的附产品.Java的第一个正式版本发布于1996年1月23日,在这个最原始的Java版本中,类和接口加起来总共只有211个,那时候的Java并不完美也不强大,但是由于它紧紧的抓住了当时最时髦的两样东西--互联网和浏览器,并拥有完美的平台可移植性,再加上计算机领域的全能奇才James Gosling(Java

《用户故事与敏捷开发》阅读笔记04

  <用户故事与敏捷开发>阅读笔记04 今天抽出了两个小时读了<用户故事与敏捷开发>的第十二.十三.十四以及十五章并写了这篇阅读笔记.第十二章标题为"故事不是什么".IEEE 830是一本关于如何编写软件需求规格的指南,最突出的特征是使用短语"系统应该.....",但作者认为以这种方式编写系统的所有需求实际是一个不可能的任务.因为用户看到正在开发的软件时总会有有效和重要的反馈循环.他们会改变之前的想法,而且每个需求的成本是不可见的,会造成分析

ORACLE中SID和SERVICE_NAME的区别

先来讲一个小故事,2015年6月份,有个客户迁移了数据库,由单实例数据库变成了RAC.JAVA应用程序出现了无法连接数据库的情况,但是PL/SQL能连接上数据库.由于项目比较庞大,虽然在半夜切换的,但是也不能接受长时间的业务停顿.当时,我对ORACLE技术也只是略知皮毛.在咨询过公司研发后,他们给我的建议是:参考PL/SQL的连接参数,将spring中jdbc连接的url由jdbc:oracle:thin:@10.2.0.2:1521:orcl改为jdbc:oracle:thin:@(DESCR

Oracle知识点(一)—Java读写Blob

1 测试表 create table blob_demo( id VARCHAR2(50), image blob, content blob ) 2 新增一条表数据 2.1 目的 插入一条记录,其中image列存储图片,contet存储正常的文本. 2.2 核心代码 /** * 新增一条数据 * @param conn */ public void addOneData(Connection conn){ try { String sql = "insert into blob_demo(id

蓝的成长记——追逐DBA(9):国庆渐去,追逐DBA,新规划,新启程

***********************************************声明***********************************************************************  原创作品,出自 "深蓝的blog" 博客,欢迎转载,转载时请务必注明出处,否则追究版权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong/article/details/39860137 *******

CRM管理系统(二)下

上一篇写的是WEB部分,接下来就是后台部分了,说实话,框架带来的便捷的确是很大的,基本上的流程就是 mapper->Dao->Service->Controller maper部分还是sql,不过本人偷懒了,增删改查功能就是一条updata语句,原因是数据字典这块肯定是系统管理员使用的,而且一个系统的数据分类也不可能太多,所以博主就偷懒,在系统中一共只能添加10种分类,每个分类对应10条类别信息数据库的设计如下 数据字典分类表 其中SJZDXX_ID为主键,SJZDFL_NAME为分类名