数据库一致性
一直在将保证数据库的一致性,但是到底什么是一致性,一般的DBMS如何保证数据库的一致性的?对这个问题一直都没有一个很直观、完整的认识,所以专门研究了一下数据库的一致性问题,学习的结果如下:
首先摘一段在百度百科上对于“数据库一致性”的描述:
数据库一致性(Database Consistency)是指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。
保证数据库一致性是指当事务完成时,必须使所有数据都具有一致的状态。在关系型数据库中,所有的规则必须应用到事务的修改上,以便维护所有数据的完整性。
保证数据库的一致性是数据库管理系统的一项功能.比如有两个表(员工/职位),员工表中有员工代码、姓名、职位代码等属性,职位表中有职位代码、职位名称、职位等级等属性。你在其中员工表中进行了插入操作,你插入了一个新员工的信息,而这个新员工的职位是公司新创建的一个职位。如果没有一致性的保证,就会出现有这么一个员工,但是不知道他到底担当什么职责!这个只是它的一个小小方面。
读一致性也是数据库一致性的一个重要方面,在实际中,我们会遇到这种情况:我们对一个表中的某些数据进行了更新操作,但是还没有进行提交,这时另外一个用户读取表中数据。这个时候就出现了读一致性的问题:到底是读什么时候的数据呢?是更新前的还是更新后的?在DBMS中设有临时表,它用来保存修改前的值,在没有进行提交前读取数据,会读取临时表中的数据,这样一来就保证了数据是一致的。(当前用户看到的是更新后的值)
但是还有一种情况:用户user1对表进行了更新操作,用户user2在user1还没有进行提交前读表中数据,而且是大批量的读取(打个比方:耗时3分钟)而在这3分钟内user1进行了提交操作,那又会产生什么影响呢?这个时候怎么保证读写一致性呢?这个时候DBMS就要保证有足够大的临时表来存放修改前的数值,以保证user2读取的数据是修改前的一致数据。然后下次再读取时候就是更新后的数据了。
个人认为:从逻辑上来说:当数据库存在没有结束的事务时,数据库就是不一致的。所以要保持数据库的一致性,就是要确保某一时刻没有事务在数据库上执行即可。例如一般说的数据库一致性备份,就需要在数据库关闭之后再进行。当然从物理存储结构考虑一致性的问题会比较复杂一些,因为涉及到很多文件的修改等问题,例如Oracle中的各类SCN的设置。总的来说,可以简单得认为:所有事务结束后数据库就是一致的。
所以说:数据库的一致性的前提是首先要保证事务的一致性。事务的一致性则需要通过并发控制、锁、隔离性等限制进行保证,具体工作机制可以参见前文,这里就不再研究了。
Oracle的SCN相关问题
下面摘录一些Oracle控制一致性的方法,来直观得了解一下,DBMS是如何来处理一致性的问题的:
1、SCN的介绍
Oracle中的SCN有下面几种:
①系统检查点scn(v$database(checkpoint_change#))
当一个检查点动作完成之后,Oracle就把系统检查点的SCN存储到控制文件中
select checkpoint_change# from v$database;
②
数据文件检查点scn (v$datafile(checkpoint_change#))
当一个检查点动作完成之后,Oracle就把每个数据文件的scn单独存放在控制文件中
select name,checkpoint_change# from v$datafile;
③
数据文件终止scn (v$datafile(last_change#))
每个数据文件的终止scn都存储在控制文件中。在正常的数据库操作过程中,所有正处于联机读写模式下的数据文件的终止scn都为null
select name,last_change# from v$datafile;
④数据文件启动scn (v$datafile_header(checkpoint_change#)
Oracle把这个检查点的scn存储在每个数据文件的文件头中,这个值称为启动scn,因为它用于在数据库实例启动时,检查是否需要执行数据库恢复
select name,checkpoint_change# from v$datafile_header;
2、SCN的工作机制:
①在数据库打开并运行之后,控制文件中的系统检查点scn、控制文件中的数据文件检查点scn和每个数据文件头中的启动scn都是相同的
②控制文件中的每个数据文件的终止scn都为null
③NORMAL或IMMEDIATE
关闭数据库的过程中,系统会执行一个检查点动作,这时所有数据文件的终止scn
都会设置成数据文件头中的那个启动scn的值。
④在数据库重新启动的时,Oracle将执行两次检查
◆ 看数据文件头中的ckpt计数器是否与对应控制文件中的ckpt计数器一致。若相等,进行第二次检查
◆ 比较文件头中的启动scn和对应控制文件中的终止scn进行比较,如果终止scn等于启动scn,则不需要对那个文件进行恢复
⑤数据库打开之后,存储在控制文件中的数据文件终止scn的值再次被更改为null,这表示数据文件已经打开并能够正常使用了
注:当ABORT强制关闭数据库时不进行检查点处理,所以终止scn仍然为无穷大。在下次启动期间,发现启动scn和终止scn不同,需要进行线程恢复。
3、SCN的增加
①SCN(System Change Number)只要数据库被修改,就会+1,而不是一定要进行checkpoint,例如DML的发生即使没有提交也会使SCN+1
注:SCN增加并不代表会在数据文件头中表现出来,而是需要等到checkpoint执行后才写入(当然可能已经增加了很多)
②如果一个DML导致产生事务,则会产生一个SCN。这个意思是说如果一个事务包含多个dml,则只有第一个初始产生事务的dml产生scn,提交的时候又是一个scn,如果一个事务只有一个dml,拿看起来就是dml产生一个scn,提交或者回滚产生一个scn。
③Oracle 10g内部的SCN会默认不管有没有动作,每隔3s自动增加一次。其他需要增加的情况则再加。
④只有ckpt进程才会修改文件头中的checkpoint计数器和SCN,DBWR只会修改数据块,即ckpt通知dbwr写数据文件,写完之后ckpt更新控制文件和数据文件头。此时若DBWR发现数据块的log block还没有被写入日志文件,则在dbwr写块之前通知llgwr把log buffer中的日志写入log文件。
注:总结一下,日志切换必定出发ckpt,但ckpt不一定会出发llgwr,但是一定会触发dbwr
4、其他的SCN
①日志文件头中包含了Low scn、Next scn,表示给日志文件包含有从Low scn到Next scn的redo record
注:当系统运行时,日志文件的Next scn同样为无穷大。而且需要注意:在恢复时不是用日志文件中的Low scn和Next scn来选择恢复的日志文件,而是通过数据文件头中的信息。
②数据块中的SCN
data block里面的SCN是当block被更改的时候的SCN,而数据文件有那么多 block,自然不同的block有不同的SCN,block中存在block SCN和ITL中的commit SCN。block SCN 又在块头和块位都有,若不一致意味着block损坏。而ITL中的commit SCN则跟consistent gets and delay block cleanout有关。
③v$database中的checkpoint_change# 和 dbms_flashback.get_system_change_number 不同。前者是作为数据库的最后一次checkpoint是的SCN,而后者是系统的最新SCN,所以一般后者都会比前者大,而当刚做完checkpoint时两者会差不多。
④当begin backup命令发出后,相关数据文件的checkpoint scn被冻结(以及状态标志被改变),其他一切照旧。例如:日志切换时checkpoint count正常递增/检查点照常写文件,自然文件中的数据块内的各种scn也照常递增。
说明:以上内容均来自IPPUB论坛的一贴讨论,来来回回看了好几遍,又自己实践了一下才稍微对Oracle的SCN有了一点了解。有时间要学习一下理论知识。