首页 > 数据库 > Oracle > 关于快照过旧和无法扩展回滚段
相信很多人都见过这样的错误,通常是一个程序运行了很久很久,突然报一个快照过旧的红色错误。
那么什么叫作快照过旧呢?它是如何产生的呢?我们应当如何避免呢?
快照过旧是指Oracle尝试读取一个过去时间点的表的数据,然而这些数据已经不在回滚段中存在了。
为什么会有这种情况呢?
这要牵涉到Oracle独特的多版本特性,它会通过回滚段来保证读数据的一致性:Oracle读取的数据,总是某一个时间点的表数据,无论后面表有多大改变,Oracle总尝试读到最开始时间点的所有数据。
举例来说:
1. 进程1在A时刻读取T表所有数据 select * from T;
2. 进程1读到T表的一半,此刻为进程2删除了T表的所有数据,并且提交了。
delete from T;
commit;
3. 此刻,进程1会继续尝试读取A时刻,T表中的所有数据。
以上便是Oracle引以为豪的多版本特性,这一特性带来了诸多好处,比如这样做既使得读数据不会阻塞任何写动作,也不会被任何写动作阻塞,同时保证了读数据的一致性。
那么Oracle如何做到这一点呢?为什么在T表数据已经被删掉且已经commit的时候,还可以读到其被删除前的数据呢?
简单的说,就是因为在执行delete from T语句时,Oracle生成了回滚信息在回滚段,倘若这时用户执行Rollback,Oracle可以通过这些回滚信息将删除动作回滚掉。
但是,当用户commit了之后,这些回滚信息仍然未被清除。
Oracle仍然保持这些信息在回滚段中,只不过提交之后,这部分回滚信息状态会变为“可擦除”,即如果回滚段不够用了,那么可以把这些旧的回滚信息擦掉,来存放新的回滚信息。
Oracle就是利用这些被标记为“可擦除”的回滚信息,来实现读取A时刻T表数据的。
现在,可能大家已经想到了,当这些可擦除的回滚信息真的被擦掉了,而进程1还在尝试读取T表被删除前的数据,会发生什么情况。
没错,这个时候Oracle就会报出这个快照过旧的错误!
这是一个很严重的错误,因为它不可预测,并有很大地可能性破坏数据一致性。
那么如何避免这个错误呢?
答案也很简单:使回滚信息尽可能久地留在回滚段中。
如何使回滚信息尽可能久地留在回滚段中呢?答案就是尽量延迟提交,这样回滚信息就不会被打上“可擦除”的标记,也就不会被清除。
可能有些朋友会有疑问,这样做的话,将导致回滚段中必须保留的回滚信息增加,加大了超过了回滚段的容量的可能性,这时候可能会引起另一个错误:无法扩展回滚段。
对这一问题,我的考虑是这样的:
与快照过旧相比,无法扩展回滚段是一个相对可控的错误,只要我们将回滚段设置为一个较大的合理值,在一个相对稳定的系统中,几乎可以一劳永逸地避免无法扩展回滚段错误。
而快照过旧就显得凶险得多,我们除了提前预防,几乎没有什么有操作性的方法来避免它。
所以,之前听有种说法,每次循环结束就执行commit,说这样可以提高效率,并且避免占用过大的回滚段空间导致无法扩展回滚段错误,其实是种自欺欺人的 说法。这样做并不能减少回滚信息的数量,该生成多少回滚信息还是会生成多少回滚信息,而频繁提交不过是赋予数据库清除这些回滚信息的权利("可擦除"标 记)。而这些回滚信息被擦除之后,快照过旧的错误就在遥遥招手了!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/30074459/viewspace-1378297/ ,如需转载,请注明出处,否则将追究法律责任。