好吧,现在做回文艺青年。
我决定把自己对ORACLE MVCC的了解分享出来,毕竟前期折腾了这么长时间,现在算是利用一下剩余价值吧。其实你如果要做一些多用户环境下的数据管理,数据库中的许多技术都值得你去借鉴,尽管你不一定需要ACID。MVCC就是这样一种技术,它可以在保证数据一致性的同时,实现最大的并发。其实我觉得ORACLE中最有价值的就是MVCC和RAC,至于其它的,都属于“强者更强”的自然发展吧。
试图用最直白的自然语言把MVCC说清楚,对我来说可不是一件容易的事情,但我仍然对当年研究DSI时的痛苦记忆尤深,希望我的介绍对大家有所帮助,毕竟DSI是写给SUPPORT看的,更多的时候,它只是告诉你How,而不是Why。
从协议上看,MVCC很简单,就是你总是只能看到数据库在某个时间点的状态(ReadConsistency,RC),而不管其后那些数据是否发生了更新,所以实际上你是看到了数据的某个历史版本,在ORACLE中,这个点就是你(事务)开始的时间,这样我们就实现了所谓的读写并行,也就是你的读总是能成功(而且你每次读到的都是同一个数据),即使数据正在被人更新(因为你们其实操作的是不同的版本)。
然而任何好处都必须有代价,不是吗。你所付出的代价就是所谓的写写串行,没错,写被严格串行了,记住,尽管你可以读取数据的任意历史版本,但写只会作用于数据的当前版本,或者叫最新版本。所谓写写串行,就是说如果在你开始事务和写数据之间,数据已经被其它人改了,或者说得准确点,当你看到的版本不是当前版本时,You Are Killed(事务回滚)。所以你会注意到,ORACLE本质上并不适合于写写冲突剧烈的应用(听到这,DB2笑了)。
为了更准确地描述协议,我们需要引入一个关于时间点的术语,SCN,关于SCN,有人叫它SystemChange Number,也有人叫System Commit Number,好吧,我并不在意这个。SCN机就是一种时间序,系统会维护一个GlobalSCN表示当前的时间,每个事务开始时(严格地说,是你开始发出第一条SQL时),你会获得当前时间,那就是你能够看到的时间,ORACLE中,这个时间叫做快照时间(SnapshotSCN),也就是说,就好像你在这个时候给数据库排了个快照。当事务提交的时候,你会获取当前时间并把GlobalSCN+1,这个时间叫做提交时间(CommitSCN)。当你更新数据时,你会产生一个新的版本,并把你的CommitSCN赋给这个版本(注意这里的语病,实际上这时候你还没有拿到CommitSCN,好吧,这个问题我们以后再说)。
所以事务有快照SCN和提交SCN,数据也有SCN,接下来我们可以准确地定义ORACLE的MVCC:
关于读,你能够看到这个数据,如果:
1)它是你自己的更新,或者
2)它是已经提交的数据,并且SCN <=你的SnapshotSCN
关于写,你能够更新这个数据,如果:
1)它是最新的,并且
2.1)它是你自己的更新,或者
2.2)它是已提交的数据,并且SCN <=你的SnapshotSCN
前面说到,在关于写写串行中,如果你看到的版本不是当前版本,你会被Killed。好吧,我承认这样说对ORACLE不公平,事实上有两点:1)如果当前版本尚未提交,那么你可以等待,如果那个事务最后回滚了,那么你有很大的机会能过关。2)ORACLE实际上定义了两个时间点,或者说两种隔离级别,一种是事务级RC,也就是你只能看到本事务开始时数据库的状态,另一种是语句级RC,你只能看到本条语句开始时数据库的状态,在语句级RC中,在你被Killed之前,你可以重启当前语句,于是你便获得了一个更大的SnapshotSCN(比那个数据的SCN更大),然后再试一次,你过关了。
所以感谢上帝,在ORACLE中,除非你特别指定,否则你总是在语句级RC中幸福地徜徉,因此只要你愿意不断地去重试(如果我们不去考虑重启的代价),事实上我们就实现了一种理论上最佳的并发协议,事务永不Killed。
但是现实总是残酷的,再好的协议,落地的时候总是会.....
好吧,先说到这吧,我得准备下周一和用户交流的PPT去了,这周看来是没什么时间了。
转载自:http://blog.sina.com.cn/s/blog_d3bf72ff0101nrhy.html