问题背景
客户在绩效系统中执行自定义存储过程时,连续两日出现 YAS-02020 snapshot too old 报错。相关存储过程通过 PRO_RUN_JOB 进行主控调度,串行执行其他子过程。
该类问题在测试环境中也有复现,属于典型的 长事务快照超期场景。
什么是 snapshot too old?
这是数据库 MVCC(多版本并发控制)机制下的经典错误。
简单理解:事务执行期间,数据库通过 undo 保留旧版本数据。但如果你在查询时所依赖的数据版本已经因为 undo_retention 设置过短被系统回收,那么数据库就无法“回到过去”了,此时报错 snapshot too old。
问题分析过程
✔ 初步排查
客户环境 UNDO_RETENTION = 600(单位:秒)
临时修改为 3000 后,问题缓解但未根除
✔ 可疑特征定位
1.发现 有报错的存储过程中存在 MERGE 操作,读写同表
2.或出现 INSERT INTO SELECT 语句,源表和目标表重叠
3.存储过程中使用游标 loop 读取后,又对相同表进行更新
这类行为会导致读取的 SCN(系统版本号)不变,但数据已经被更新提交,undo 已被复用,快照还原失败 → 报错!
深入原理:为什么 loop + update 容易踩坑?
当游标遍历某张表时,每次 fetch 都基于最初打开时的快照 SCN。而过程中如果对同一张表进行更新并提交:
对应的数据块被改写
旧数据保存在 undo 中,等待保留时间到期
如果 loop 遍历很久,超过 undo_retention,旧版本无法还原
报 snapshot too old!
这个问题也同样出现在 Oracle、DB2 中,属于典型的 MVCC 陷阱。
解决方案建议
1.分离读写表设计
将读取的配置表(如 RUN_JOB_DETA)和更新的结果表分离,避免在 loop 内更新当前遍历表。
2.优化存储过程逻辑
在 PRO_RUN_JOB 中,将 loop 中的状态更新写入结果历史表,避免对主表反复更新。
3.合理配置 UNDO_RETENTION 与 undo 表空间大小
根据业务数据规模与执行时长,评估 undo 空间是否足够支撑事务存活期。
经验总结
undo_retention 是个容易被低估的参数,不能只“设置”,还需结合实际评估 undo 表空间容量
长事务 + 游标更新本表 是高风险组合,必须优化
在 YashanDB 中,该类问题本质与 Oracle 等通用数据库机制一致,处理思路具备可迁移性