ORA-01555是Oracle数据库中非常经典的错误类型,很多运维人员和开发在跑长查询或者批量任务时都可能遇到,下面先给大家放一张示意图,方便理解错误产生的场景。

ORA-01555错误的核心原理
要解决问题首先要明白错误是怎么来的,Oracle的读一致性是通过UNDO数据实现的:当一个查询开始执行时,会记录当前的事务SCN(系统变更号),如果查询过程中需要访问的数据被其他事务修改了,Oracle就会从UNDO表空间中找修改前的数据镜像,来保证查询看到的是查询开始时的数据版本。
如果查询执行时间太长,或者UNDO表空间里的旧版本数据被覆盖了,就会出现无法找到对应镜像的情况,这时候就会抛出ORA-01555快照过旧的报错。简单来说就是查询需要的历史数据已经被清理,无法还原出查询开始时的数据状态。
常见的触发场景
- UNDO表空间大小不足,旧的事务前镜像数据被快速覆盖
- UNDO_RETENTION参数设置过小,Oracle过早清理了UNDO数据
- 存在运行时间非常长的查询,远超UNDO数据的保留周期
- 频繁的大事务操作,大量产生UNDO数据,挤占了旧数据的存储空间
- 查询语句本身效率极低,全表扫描大表导致执行时间被拉长
对应的解决方案
1. 调整UNDO表空间相关配置
首先检查当前UNDO表空间的状态和参数设置,先查看UNDO表空间的使用情况:
-- 查看UNDO表空间使用情况
SELECT
tablespace_name,
SUM(bytes)/1024/1024 AS used_mb,
SUM(maxbytes)/1024/1024 AS max_mb
FROM dba_data_files
WHERE tablespace_name = (SELECT value FROM v$parameter WHERE name = 'undo_tablespace')
GROUP BY tablespace_name;
-- 查看UNDO_RETENTION参数设置(单位:秒)
SHOW PARAMETER undo_retention;如果UNDO表空间使用率过高,可以扩展表空间大小:
-- 给UNDO表空间增加数据文件,路径根据实际环境调整 ALTER TABLESPACE undo_ts ADD DATAFILE '/u01/app/oracle/oradata/orcl/undo02.dbf' SIZE 2G AUTOEXTEND ON NEXT 512M MAXSIZE 10G;
如果UNDO_RETENTION设置过小,可以适当调大,比如设置为1800秒(30分钟):
ALTER SYSTEM SET undo_retention = 1800 SCOPE = BOTH;
2. 优化长查询语句
如果是查询本身执行太慢导致的问题,需要优化SQL。比如给查询涉及的过滤字段加索引,避免全表扫描,减少查询执行时间。可以用Oracle的执行计划工具分析SQL:
-- 生成SQL的执行计划,分析性能瓶颈
EXPLAIN PLAN FOR
SELECT * FROM big_table WHERE create_time >= TO_DATE('2024-01-01','yyyy-mm-dd');
-- 查看执行计划
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);3. 避免大事务和长事务
业务设计中尽量避免单次处理过多数据的大事务,可以把大批量操作拆分成小批次执行,减少单个事务的UNDO产生量,也缩短事务执行时间。比如批量更新100万数据,可以分成每次更新1万条,多次执行:
DECLARE
v_count NUMBER;
BEGIN
LOOP
-- 每次更新1万条数据
UPDATE big_table SET status = 1 WHERE status = 0 AND ROWNUM <= 10000;
v_count := SQL%ROWCOUNT;
COMMIT;
EXIT WHEN v_count = 0;
END LOOP;
END;
/问题排查步骤总结
遇到ORA-01555错误时,可以按照下面的顺序排查:
| 排查步骤 | 操作内容 |
|---|---|
| 1 | 查看alert日志,确认错误发生的具体时间和相关SQL |
| 2 | 检查UNDO表空间使用率和UNDO_RETENTION参数 |
| 3 | 分析报错对应的SQL语句,评估执行时长 |
| 4 | 检查是否存在运行时间超长的事务 |
| 5 | 根据排查结果选择调整参数、扩展表空间或者优化SQL |
只要按照上面的方法针对性处理,大部分ORA-01555快照过旧的问题都可以得到解决,日常运维中也可以定期监控UNDO表空间的使用情况,提前规避这类错误。