oracle的commit详解
它执⾏的时候,你不会有什么感觉。commit在数据库编程的时候很常⽤,当你执⾏DML操作时,数据库并不会⽴刻修改表中数据,这时你需要commit,数据库中的数据就⽴刻修改了,如果在没有commit之前,就算你把整个表中数据都删了,如果rollback的话,数据依然能够还原。听我这么说,你或许感觉commit没什么⽤,其实不然。当你同时执⾏两条或两条以上的sql语句时,问题就出现了。举⼀个例⼦,你去银⾏转账,你转的时候银⾏的数据库会update你银⾏账户⾥⾯的数据,同时对另⼀个⼈得账户也进⾏update操作。这两个程序都必须全部正确执⾏,才能commit,否则rollback。如果只是完成⼀条,要么你郁闷,要么银⾏郁闷,第⼀种情况是,你的账户的钱没少,转账⼈得账户上的钱多了,银⾏郁闷了。第⼆种情况你的银⾏账户的钱少了,他的却没多,你就好郁闷了。Oracle好好学吧!sql不难,plsql努努⼒也能熬过去,等到优化那,哎!DBA不是那么好当的。还有就是commit算是显式提交,还有隐式提交,并不是,不commit的话,你的全部努⼒就都⽩费了。
这个命令是将数据写到数据库中。如果不执⾏COMMIT这个命令,那么在你这个session之外的其他session查询的数据是你修改数据之前的数据。⽽COMMIT之后⼈家查询的是你修改的数据。你可以打开两个sqlplus⽐较做⼀下测试。⼀⽬了然。
commit的提交针对的是:DML
Data Manipulation Language(DML) 需要提交,这部分是对数据管理操作,⽐如Insert(插⼊)、Update(修改)、Delete(删除),Data Definition Language(DDL) 不需要提交,这部分是对数据结构定义,⽐如 Create(创建)、Alter(修改)、Drop(删除)
oracle的commit就是提交数据(这⾥是释放锁不是锁表),在未提交前你前⾯的操作更新的都是内存,没有更新到物理⽂件中。执⾏commit 从⽤户⾓度讲就是更新到物理⽂件了,事实上commit时还没有写date file,⽽是记录了redo log file,要从内存写到data物理⽂件,需要触发检查点,由DBWR这个后台进程来写,这⾥内容有点多的,如果不深究的话你就理解成commit即为从内存更新到物理⽂件。锁有很多种,⼀般我们关注的都是DML操作产⽣的,⽐如insert,delete,update,for update都会同时触发表级锁和⾏级锁
补充:对的,insert以后commit之前是锁表的状态,其他事务⽆法对该表进⾏操作。
如果不提交的话,那么这个表就被锁了
COMMIT通常是⼀个⾮常快的操作,⽽不论事务⼤⼩如何。你可能认为,⼀个事务越⼤(换句话说,它影响的数据越多),COMMIT需要的时间就越长。不是这样的。不论事务有多⼤,COMMIT的响应时间⼀般都很“平”(flat,可以理解为⽆⾼低变化)。这是因为COMMIT并没有太多的⼯作去做,不过它所做的确实⾄关重要。
这⼀点很重要,之所以要了解并掌握这个事实,原因之⼀是:这样你就能⼼⽆芥蒂地让事务有⾜够的⼤⼩。⼀种错误的信念认为分批提交可以节省稀有的系统资源,⽽实际上这只是增加了资源的使⽤。如果只在必要时才提交(即逻辑⼯作单元结束时),不仅能提⾼性能,还能减少对共享资源的竞争(⽇志⽂件、各种内部闩等)。
分批提交COMMIT的开销存在两个因素:
显然会增加与数据库的往返通信。如果每个记录都提交,⽣成的往返通信量就会⼤得多。
每次提交时,必须等待redo写⾄磁盘。这会导致“等待”。在这种情况下,等待称为“⽇志⽂件同步”(log file sync)。
为什么COMMIT的响应时间相当“平”,⽽不论事务⼤⼩呢?在数据库中执⾏COMMIT之前,困难的⼯作都已经做了。我们已经修改了数据库中的数据,所以99.9%的⼯作都已经完成。例如,已经发⽣了以下操作:
提交更改是内存条吗 已经在SGA中⽣成了undo块。
已经在SGA中⽣成了已修改数据块。
已经在SGA中⽣成了对于前两项的缓存redo。
取决于前三项的⼤⼩,以及这些⼯作花费的时间,前⾯的每个数据(或某些数据)可能已经刷新输出到磁盘。
已经得到了所需的全部锁。
执⾏COMMIT时,余下的⼯作只是:
为事务⽣成⼀个SCN。如果你还不熟悉SCN,起码要知道,SCN是Oracle使⽤的⼀种简单的计时机制,⽤于保证事务的顺序,并⽀持失败恢复。SCN 还⽤于保证数据库中的读⼀致性和检查点。可以把SCN看作⼀个钟摆,每次有⼈COMMIT时,SCN都会增1.
LGWR将所有余下的缓存重做⽇志条⽬写到磁盘,并把SCN记录到在线重做⽇志⽂件中。这⼀步就是真正的COMMIT。如果出现了这⼀步,即已经提交。事务条⽬会从V$TRANSACTION中“删除”,这说明我们已经提交。
V$LOCK中记录这我们的会话持有的锁,这些所都将被释放,⽽排队等待这些锁的每⼀个⼈都会被唤醒,可以继续完成他们的⼯作。
如果事务修改的某些块还在缓冲区缓存中,则会以⼀种快速的模式访问并“清理”。块清除(Block cleanout)是指清除存储在数据库块⾸部的与锁相关的信息。实质上讲,我们在清除块上的事务信息,
这样下⼀个访问这个块的⼈就不⽤再这么做了。我们采⽤⼀种⽆需⽣成重做⽇志信息的⽅式来完成块清除,这样可以省去以后的⼤量⼯作(在下⾯的“块清除”⼀节中将更全⾯地讨论这个问题)。
可以看到,处理COMMIT所要做的⼯作很少。其中耗时最长的操作要算LGWR执⾏的活动(⼀般是这样),因为这些磁盘写是物理磁盘
I/O。不过,这⾥LGWR花费的时间并不会太多,之所以能⼤幅减少这个操作的时间,原因是LGWR⼀直在以连续的⽅式刷新输出重做⽇志缓冲区的内容。在你⼯作期间,LGWR并⾮缓存这你做的所有⼯作;实际上,随着你的⼯作的进⾏,LGWR会在后台增量式地刷新输出重做⽇志缓冲区的内容。这样做是为了避免COMMIT等待很长时间来⼀次性刷新输出所有的redo。
因此,即使我们有⼀个长时间运⾏的事务,但在提交之前,它⽣成的许多缓存重做⽇志已经刷新输出到磁盘了(⽽不是全部等到提交时才刷新输出)。这也有不好的⼀⾯,COMMIT时,我们必须等待,直到尚未写出的所有缓存redo都已经安全写到磁盘上才⾏。也就是说,对
LGWR的调⽤是⼀个同步(synchronous)调⽤。尽管LGWR本⾝可以使⽤异步I/O并⾏地写⾄⽇志⽂件,但是我们的事务会⼀直等待LGWR完成所有写操作,并收到数据都已在磁盘上的确认才会返回。
前⾯我提⾼过,由于某种原因,我们⽤的是⼀个Java程序⽽不是PL/SQL,这个原因就是 PL/SQL提
供了提交时优化(commit-time optimization)。我说过,LGWR是⼀个同步调⽤,我们要等待它完成所有写操作。在Oracle 10g Release 1及以前版本中,除PL/SQL以外的所有编程语⾔都是如此。PL/SQL引擎不同,要认识到直到PL/SQL例程完成之前,客户并不知道这个PL /SQL例程中是否发⽣了COMMIT,所以PL/SQL引擎完成的是异步提交。它不会等待LGWR完成;相反,PL/SQL引擎会从COMMIT调⽤⽴即返回。不过,等到PL/SQL例程完成,我们从数据库返回客户时,PL/SQL例程则要等待LGWR完成所有尚未完成的COMMIT。因此,如果在PL /SQL中提交了100次,然后返回客户,会发现由于存在这种优化,你只会等待LGWR⼀次,⽽不是100次。这是不是说可以在PL/SQL中频繁地提交呢?这是⼀个很好或者不错的主意吗?不是,绝对不是,在PL/SQ;中频繁地提交与在其他语⾔中这样做同样糟糕。指导原则是,应该在逻辑⼯作单元完成时才提交,⽽不要在此之前草率地提交。
COMMIT是⼀个“响应时间很平”的操作,虽然不同的操作将⽣成不同⼤⼩的redo,即使⼤⼩相差很⼤或者说⽆论⽣成多少redo,但也并不会影响提交(COMMIT)的时间或者说提交所⽤的时间都基本相同。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论