继oracle外键不加索引会死锁之后,又看到oracle一个有名的异常,snapshot too old,情景是这样的
假设有一个表,有一亿行数据,现在第一个任务在执行select *操作,第二个任务修改了第5000万行的数据,并进行了commit,那么等到第一个任务执行到第5000万行数据时,就会报snapshot too old异常,为什么会这样呢?
其实一般情况下,数据库都会有一个类似于innodb的非一致性读功能:在某一时刻开始一个查询操作,那么查到的数据就一定是此刻所有数据的状态,即使有些数据被进行了修改,这个功能是通过undo log保证的。如上边的例子,当第二个任务将第5000万行数据进行修改之后,这个操作会被存储到undo log里面去,用来处理后续的读操作或者回滚操作。但很遗憾的是,oracle的undo log容量有限,如果上边第一个查询任务执行时间过长,而且第二个任务已经commit,那么第二个任务的undo log记录很可能被后边事务的undo log覆盖掉,那么当第一个任务执行到第5000万行查询时,就找不到了本该有的undo 记录,所以snapshot too old就准时出现了。
但是innodb却是可以避免这个问题的,原因在于innodb不会对undo log大小进行限制,只会在后台通过master thread定时清理undo log,而且清理的时候,也会判断当前要清理的undo log会不会被用到,所以就基本避免了这个问题。
那么如何防范这个问题呢:
1.不要对undo log设置太小,或者定期增大undo log容量
2.优化sql,不要进行过长时间的查询,那样的话,undo log就会消耗不小的资源。
感觉oracle确实厉害,但mysql却后来居上,做的很精致,弥补了好多oracle的不足之处,还好用的mysql,没有这些莫名其妙的问题!