上一篇我们初步地认识了Oracle的物理体系结构,并通过两个简单的查询更新语句了解到它是如何工作的。这一篇的话我们继续Oracle物理体系结构的探索之路,我们知道,只要执行了DML语句(新增、删除和修改),最终都必须选择提交Commit或者回滚Rollback,那么这两个东西又是怎么和我们的物理体系结构关联在一起的呢?
先来说提交,提交就是我们确定了这一次的修改结果,可以将数据从数据缓存区刷入到磁盘中了,那么是不是只要我们一提交,Oracle就会进行刷盘呢?答案肯定是否啦。举个栗子,今天包工头让你把一百块砖搬到十楼,但是这个包工头看你不爽,让你只能一块一块往上搬,你就说你干不干吧,所以说把,Oracle它肯定也是这个意思,他也不想干。
所以我们的Oracle最终肯定是选择了批量刷入而不是单条刷,那有人就会问了:那这个commit有啥用呢,我提交了Oracle又不立即帮我写到数据库里去,而且要是碰上了断电,那在内存中的数据不是全没了嘛。别急别急,天生commit必有用,我们不要忘了,在SGA共享内存区中除了数据缓存区,还有一个日志缓存区嘞,这玩意才是Oracle敢这么玩的“法宝”,那么它到底有什么神奇的技能呢,我们知道日志缓存区里的日志数据可以帮助我们在一些异常情况下进行恢复使用,而日志数据写入到日志文件中需要依赖一个很重要的进程,就是LGWR进程,它可以将日志数据写入到日志文件中去。这个进程是采用单进程运转,因为多进程下无法保证顺序,该进程还天生自带了五条“铁律”:
-
每隔三秒,LGWR运行一次;
-
任何commit触发LGWR运行一次;
-
DBWR要将数据从数据缓存区写入到磁盘,触发LGWR运行一次;
-
日志缓存区满三分之一或者记录满1MB,触发LGWR运行一次;
-
联机日志文件切换也将触发运行一次;
看到第二点,我想大家应该恍然大悟了吧,正是由于commit会触发LGWR运行,所以Oracle才敢对数据进行批量刷入,哪怕是断电了,有日志文件在也可以恢复过来,在保证了性能的同时,数据安全也得到了保证,完美 and perfect!
那么数据缓存区的数据何时才会被写入到磁盘中去呢?这里又涉及到了一个新的进程CKPT,该进程会在适当的时机触发DBWR写出,有一个参数可以调整CKPT的触发时间,那就是FAST START MTTR TARGET,该参数的设置在于希望Oracle的SMON在多长的时间做实例恢复,调整该参数,Oracle会调配CKPT在合适的时间触发DBWR。
OK,提交讲完了,下面讲讲回滚,先来看一下更新的一个流程(省略了前面的部分):
-
以update t set object_id=92 where object_id=29; 为例,Oracle将数据从磁盘中读到数据缓存区中,接下来就可以开始更新的操作了;
-
在对数据进行更新之前,需要在回滚表空间的回滚段事务表上分配事务槽,这样才能在回滚表空间分配到空间;
-
接着Oracle会在数据缓存区中创建object_id=27的前镜像,前镜像的数据会被写入到回滚表空间的数据文件内;
-
完成这些步骤后,Oracle才会将object_id=29更改为object_id=92;
-
如果用户执行提交,日志缓存区会立即记录这个提交信息,然后把回滚段事务标记为非激活INACTIVE状态,表示允许重写;
-
如果用户执行回滚,则Oracle会将前镜像从回滚表空间中读取出来,修改数据缓存区,完成回滚;
需要注意的是,上面的一系列更新操作都是需要记录日志的!接下来一起看看回滚段的相关参数:
-
undo_management为auto表示自动进行回滚段管理,回滚表空间不足时自动扩展;
-
undo_retention为900,当提交后,回滚段事务被标记为INACTIVE状态,回滚段的前镜像被打上可以重新覆盖使用的标记,但是要等到900秒后才允许;
-
undo_tablespace就是回滚表空间是UNDOTBS1;
既然讲了更新,那这里再提一下关于一致读的一些东西,一致读指的是查询的记录取决于查询的时间点,即便记录在查询的过程中被更新了,也会从回滚段的前镜像中找到那个时间点的数据,这里涉及到了两个概念:
-
SCN,全称是System Change Number,存在于Oracle最小单位块中,组织是一个只会递增的数字,当块被更新SCN就会增加;
-
当更新某块时,回滚段事务槽会记录事务,如果未提交或者回滚,该块就存在活动事务,Oracle读取该块可以识别到这种情况;
举个例子,假设在一次查询中总共涉及到了四张表A表、B表、C表和D表的查询,该次查询从早上八点查到九点总共一个小时,假设在八点半时还未查到D表,并且此时D表记录被更新了,那么等到查询该表时,Oracle会先获取数据块最新的SCN号也就是SCN 8:30,和开始查询的时间点8:00比较发现查询过程中被更新过,那么就不会取最新的数据而是到回滚段中找8:00的前镜像。还有一种情况,D表是在八点之前被更新的,比如说是七点半,那么就是SCN 7:30,那么Oracle在读取该块时发现8:00大于7:30,但是并不是说此时就可以直接读最新的数据了,Oracle还会识别该块是否存在活动事务,如果有的话还是会去找前镜像的!