1.Oracle读Block到Buffer Cache(如果该Block在Buffer中不存在)
2.在相应回滚段段头的事务表中创建一个undo条目(undo条目指向的一块空间)
3.将要变化的记录(不是整个数据块)的前映象放到undo中。如果这时有别的会话需要读取这条记录,oracle先将这条记录所在的数据块复制一份,然后用undo中那条记录的前映像"旧化"这个数据块,这样一致读的数据块就构建完成了。
4.在redo log buffer中记录此次操作的产生redo 数据和undo数据
5.在Buffer Cache中的相应数据块上修改记录,并且标记相应的数据块为Dirty
提交(COMMIT)
1.Oracle产生一个SCN
2.在回滚段事务表中标记该事务状态为commited
3.LGWR Flush Log Buffer到日志文件
3.如果此时数据块仍然在Buffer Cache中,那么SCN将被记录到Block Header上,这被称为快速提交(fast commit)
4.如果内存不够,dirty block已经被写回到磁盘,那么下一个访问这个block的进程,会定位到数据块上要获取的行,从行头找到数据块头中itl事务表中
对应的事务条目,从该事务条目中判断该事务有没有提交(因为在提交前就写入磁盘了,来不及标记提交标记),如果没有提交,将会根据从该事务条目中找到在回滚段中对应的地址,根据地址进入
回滚段中获取该事务的状态。如果事务被提交,则将提交SCN并写回到Block Header中的itl事务表中的对应的事务条目中。这被称为延迟块清除。
如果回滚段中的事务条目由于时间太久已经被覆盖了,那oracle会从当前回滚段中找到一个最小的commit scn更新这个延迟块清除的块的块头中的itl,
从而完成块清除。(这虽然不准确,但是是安全的,对于数据访问也不构成影响。所以叫 upper bound ,猜测的一个scn的上限。)
如果当前的查询时的scn比这个最小的commit scn还小,说明事务的提交scn可能也大于当前查询的scn。则产生01555错误,由于报错后写回了一个当前回滚段中最小的提交scn,那么下次就不会再触发
01555这个错误了;如果当前的查询时的scn比回滚段中最小的commit scn大,那么当前查询的scn肯定大于已经被覆盖的事务的scn,则可以直接用这个延迟清除的块构建一致读