前言:
首先,这次故障颠覆了我对Oracle的认知,在笔者的一贯认知中,Oracle是完全可靠的,只要数据不损坏,他能从任意的掉电,宕机等异常状况下恢复,而redo和undo的存在,不会让用户出现脏读和错读的情况。
但此次的故障,让我对O事务处理的流程产生了一定的怀疑,事务的原子性、一致性、隔离性、持久性未能得到保障。本次故障开始是同事处理的,后经过大家一起的探讨和研习,终于搞清了事情的本末,也确认了这是一个无法避免的现象,因为总要有个先后,O也无法避免这样问题的发生。但由于Oracle闭源,无法得知其内部的处理流程,以下部分内容基于猜测,进行了验证。
故障说明:(涉及系统名称都用通用性名称代替)
系统分为 2 个数据库: 交易库、核心库
交易路径: ①一笔交易需要进行 ---> ②交易信息插入交易库,设置交易状态为 “初始” ---> ③去核心库查询是否付款情况 ---> ④如果付款成功,修改交易库中交易状态为 “成功” ---> ⑤查询交易库中该交易的状态 ---> ⑥如果状态为成功,则进行发货
当日故障现象: 程序查询交易状态,显示“成功”,于是进行了发货操作。但问题时段交换机出现问题,导致IO无法写入,引发了db的重启。 重启后查看刚才的交易发现状态为“初始”。
我们dba无法了解 应用程序的细节。 但业务说过,他们 查询发货 和 交易修改 两个程序绝对不可能是同一个session,因此不会出现同一session中未提交但可读取的情况,到这里就比较奇怪了。
所以简化一下问题:
session1 update数据并commit ----> session2 看到了session1 update后的数据(说明session1 commit成功,正常道理来讲redo已经写入磁盘,undo事务表已经更新) ----> 由于IO问题db重启 ----> session1 update 并commit成功的操作丢失了
下面我们进行一个测试:
1、创建一张测试表(id name),并插入数据:
2、session1 更新数据 (sid 761)
update t set name = 'LeBron' where id=1; 不提交
3、session2 (sid 760)查询相关数据,应该看不到session1修改的记录。还是1,Kobe
4、另起一个session,模拟交换机故障,IO被卡住的情况,这里我们使用oradebug suspend命令hang住 lgwr进程,不让他写日志。
5、session1 进行提交操作,由于lgwr无法写日志,commit操作会hang住。
6、session2 查看session1 已经发出commit,但是没有提交成功的记录。此时,我们会发现虽然session1 还未提交成功,但是session2 已经能看到session1 修改的数据了。
7、重启数据库,模拟异常重启,因为lgwr被hang住,无法完成chkpoint,这个使用abort模拟异常宕机。
8、再次登录其他session,查看t表中的数据。发现session1中的commit操作,实际上丢失了。
总结一下:
与常规和常识不符的地方: session2 看到了 session1 未提交成功的数据。
原理分析: