数据库装好、监听起来、业务连上了,很多人以为这事儿就算交差了。我早年也这么想,直到有天晚上被电话叫起来,说「系统卡死登不进去了」。上去一看,报错 ORA-00257,归档日志把闪回区撑满,数据库直接挂起写不进任何数据——而归档之所以满,是因为备份脚本那周改坏了,没人发现。从那以后我明白,Oracle 真正费功夫的从来不是安装,是它跑起来之后那些细碎又致命的日常。
RMAN 备份恢复我另外写过专文,这篇不重复那块,只讲日常运维里最高频的几件事。
表空间:第一个半夜找上门的永远是它
应用报「ORA-01653: unable to extend table」是最常见的一类工单。根因就一个——某个表空间满了。但满和满不一样,有的真没空间了,有的只是单个数据文件到了上限、表空间本身还有余地。先别急着加文件,看清楚:
SELECT a.tablespace_name,
ROUND(a.bytes/1024/1024) total_mb,
ROUND((a.bytes-b.bytes)/1024/1024) used_mb,
ROUND(b.bytes/1024/1024) free_mb,
ROUND((a.bytes-b.bytes)/a.bytes*100) pct_used
FROM (SELECT tablespace_name, SUM(bytes) bytes FROM dba_data_files GROUP BY tablespace_name) a,
(SELECT tablespace_name, SUM(bytes) bytes FROM dba_free_space GROUP BY tablespace_name) b
WHERE a.tablespace_name = b.tablespace_name
ORDER BY pct_used DESC;
这张表我基本每天瞄一眼,pct_used 过 85% 的提前处理,别等业务报错。注意它只算永久表空间,临时表空间和 UNDO 要分开看。
确认满了,处理方式两种。小文件表空间直接加数据文件:
ALTER TABLESPACE USERS
ADD DATAFILE '/u01/app/oracle/oradata/ORCL/users02.dbf'
SIZE 2G AUTOEXTEND ON NEXT 512M MAXSIZE 30G;
已经有的数据文件如果没开 AUTOEXTEND 或者到了 MAXSIZE,也可以原地扩:
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/ORCL/users01.dbf' RESIZE 10G;
这里有个坑得提醒:RESIZE 只能往大了扩,或者往小了缩到「当前已用空间」以下一点——你没法把文件缩到比里面已有数据还小,ORA-03297 会教你做人。还有 AUTOEXTEND 的 MAXSIZE 一定要设上限,我有次偷懒没设,结果一个跑飞的业务一天把数据文件撑到 300G 把磁盘干满,连带把别的表空间也拖挂了。
临时表空间是另一种满法,它报错是 ORA-01652 unable to extend temp segment,通常是大排序、大 hash join 撑出来的。处理方式不是删数据,是加临时文件:
ALTER TABLESPACE TEMP ADD TEMPFILE '/u01/app/oracle/oradata/ORCL/temp02.dbf' SIZE 2G;
UNDO 表空间也单独看,它跟 ORA-01555 直接挂钩,后面讲报错时会提。
内存:SGA 和 PGA 到底吃了多少
Oracle 的内存分两块,SGA 是共享的(缓冲池、共享池、日志缓冲等),PGA 是每个会话私有的(排序区、会话内存)。19c 里我用得最多的是自动内存管理那套参数:
SHOW PARAMETER sga_target;
SHOW PARAMETER pga_aggregate_target;
SHOW PARAMETER memory_target; -- 如果非 0,说明开了 AMM 全自动
SELECT name, ROUND(bytes/1024/1024) mb FROM v$sgainfo;
我的经验是服务器只跑一个实例时,开 MEMORY_TARGET 让 Oracle 自己调配 SGA 和 PGA 最省心;但一台物理机上有多个实例、或者还跑着别的服务,就别用 AMM 了,手动给每个实例定死 sga_target 和 pga_aggregate_target,免得它们互相抢。
PGA 够不够,看这个建议视图:
SELECT ROUND(pga_target_for_estimate/1024/1024) mb,
estd_pga_cache_hit_percentage
FROM v$pga_target_advice
ORDER BY mb;
如果当前那档对应的 cache hit 明显低于 100%,且加大一点就能拉满,通常值得加。反过来,如果加到很大命中率也不动,说明瓶颈不在这。
最常撞的 ORA-04031(shared pool 内存不足,无法分配),多半是共享池被大量硬解析撑爆,或者 SGA 整体太小。临时救急可以 ALTER SYSTEM FLUSH SHARED_POOL;,但这只是把现有游标清掉腾地方,根子上还是得治硬解析——看看是不是应用没用绑定变量,每条 SQL 都拼字面量。这问题在 Java 应用里很常见,MyBatis 的 ${} 和 #{} 用错就会退化成硬解析。
AWR:业务喊慢的时候拿它说话
「数据库好慢」是最难接的一句话,慢在哪?AWR 报告是 Oracle 给的最硬的证据。它基于两个快照之间的统计增量,所以前提是 STATISTICS_LEVEL 不是 BASIC(默认 TYPICAL 就行)。
生成报告:
@?/rdbms/admin/awrrpt.sql
-- 交互里选 html,选起始和结束 snap_id,给个文件名
报告几十页,真正常看的不多。我一般直奔这几处:
- Report Summary 里的 DB Time:它代表数据库「忙」的总时长,如果 DB Time ÷ 快照时长 ÷ CPU核数 远大于 1,说明在并发等,远小于 1 说明其实挺闲、慢在别处(网络、应用)。
- Top 10 Foreground Events by Total Wait Time:排第一的如果是 db file sequential read,那是索引单块读多,可能是缺索引或索引不当;如果是 direct path read temp,说明在猛扫磁盘临时区,SQL 该优化了;如果是 latch / buffer busy,那是内存争用。
- SQL ordered by Elapsed Time:直接看哪几条 SQL 吃了最多时间,拿 SQL_ID 去
SELECT sql_text FROM v$sql WHERE sql_fulltext LIKE ...或者SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('sql_id',NULL,'ALLSTATS LAST'));看执行计划。 - Buffer Hit % 和 Parse/Execute %:命中率长期低于 95% 说明缓冲池可能偏小或 SQL 在反复全表扫;硬解析占比高就回到上面那个绑定变量的问题。
AWR 默认保留 8 天、每小时一快照。生产库我一般把保留期拉到 30 天,快照间隔根据业务定,交易高峰时段可以调到 15 分钟一份,便于事后定位那几分钟的抖动:
BEGIN
DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(
retention => 30*24*60, -- 30 天,单位分钟
interval => 60); -- 60 分钟
END;
/
半夜弹出来的几个 ORA,先认脸
有些报错你不一定天天见,但见一次就是事故,脸得先认熟。
ORA-01555 snapshot too old。长查询跑着跑着,它要读的数据块被 UNDO 覆盖重用了,于是报「快照太旧」。本质是 UNDO 表空间不够、或者 UNDO_RETENTION 太短、或者那条查询本身实在跑太久。救急是加大 UNDO 和 retention:
SHOW PARAMETER undo_retention;
ALTER SYSTEM SET undo_retention = 3600 SCOPE=BOTH; -- 保留 1 小时
但治本还是把那条慢查询优化掉——让它别在 undo 上待那么久。
ORA-00257 archiver error, connect internal only。这就是开篇那个坑:归档日志把闪回区(FRA)写满,归档进程 ARCH 卡住,新的 redo 没法归档,数据库进入「只能查、不能写」的半死状态。处理是清归档,但清之前先确认这些归档已经备份过,否则你删了没备份的归档,将来的恢复链就断了:
rman target /
# 只删「已经至少备份过 1 次到磁盘」的归档,最安全
DELETE NOPROMPT ARCHIVELOG ALL BACKED UP 1 TIMES TO DEVICE TYPE DISK;
# 如果备份策略允许按时间删(确认恢复窗口覆盖不到的):
# DELETE NOPROMPT ARCHIVELOG ALL COMPLETED BEFORE 'SYSDATE-1';
闪回区用到多少,平时自己查着点,别等满:
SELECT name, ROUND(space_limit/1024/1024) limit_mb,
ROUND(space_used/1024/1024) used_mb,
ROUND(space_reclaimable/1024/1024) reclaim_mb,
number_of_files
FROM v$recovery_file_dest;
ORA-12514 TNS:listener does not currently know of service requested。业务连不上,监听起来了但「不认识」这个服务名。多数情况是实例没自动注册到监听(动态注册依赖 LOCAL_LISTENER 和服务名匹配),或者 listener.ora 里静态注册漏了。先 lsnrctl status 看服务列表里有没有你的 SID,没有就 ALTER SYSTEM REGISTER; 强制实例向监听注册一次,还不行就检查 tnsnames.ora 的 SERVICE_NAME 是否和实例的 service_names 一致。
ORA-00060 deadlock detected。死锁 Oracle 自己会回滚其中一个事务并抛这个错,应用层捕获重试即可。真要查是谁和谁锁了,看 v$session 的 event 和 blocking_session,或者直接 SELECT * FROM dba_blockers;。
一条 SQL 锁死接口,怎么定位并放开
比死锁更常见的是「某个会话长时间持有行锁不提交,后面一排会话全卡在 enq: TX - row lock contention」。这种不会自动解开,得人去杀。
先找出谁是阻塞者、谁被阻塞:
SELECT s.sid, s.serial#, s.username, s.program, s.sql_id,
s.event, s.blocking_session, s.seconds_in_wait
FROM v$session s
WHERE s.blocking_session IS NOT NULL
OR s.sid IN (SELECT blocking_session FROM v$session WHERE blocking_session IS NOT NULL);
blocking_session 非空的那些是被堵的,顺着它指过去的就是源头。找到源头会话后,先确认它是不是真该杀(别把正在跑的关键批处理杀了),再下手:
ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
IMMEDIATE 比不带参数更果断,直接标记会话终止、回滚未提交事务,不跟客户端商量。如果被杀的会话是死连接(客户端早断了但 Oracle 还以为它活着,卡在 SQL*Net break/reset to client 之类),KILL SESSION 可能迟迟不生效,那种得去操作系统层 kill -9 对应的 PMON 子进程——但这一步要确认 PID 真的是那个会话的,错杀后台进程数据库就崩了。我一般先看 SELECT p.spid FROM v$process p, v$session s WHERE p.addr=s.paddr AND s.sid=xxx;,确认了才在 OS 上 kill。
还有一类长操作,比如大表建索引、收集统计信息,你想知道它还要多久:
SELECT sid, serial#, opname, target,
sofar, totalwork, ROUND(sofar/totalwork*100) pct
FROM v$session_longops
WHERE sofar < totalwork;
这一句能告诉你那个 CREATE INDEX 到底跑到百分之几,免得你干等心里没底。
监听和连通性,别等出事才测
数据库能连,不代表明天还能连。我把这几条放进了例行巡检脚本,每天跑一次:
lsnrctl status | grep -E "Services|Instance"
tnsping ORCL # 测 TNS 解析通不通
# 再用 sqlplus 真连一次,确认不只是监听在、实例也在
sqlplus -L appuser/pass@ORCL <<'EOF'
SELECT INSTANCE_NAME, STATUS, DATABASE_STATUS FROM v$instance;
EXIT;
EOF
tnsping 只证明网络和服务名解析通,不证明实例活着,所以最后那次真连很有必要。
收个尾
Oracle 的日常运维,拼的不是记住多少命令,是「报警弹出来的时候你知道先敲哪行、删东西之前先确认什么」。表空间盯使用率、闪回区盯占用、监听每天探活、慢了就拉 AWR 看 Top Event——这几件事做成了习惯,绝大多数半夜电话其实都能消在萌芽里。
至于备份恢复,那是另一门功夫,另有专文。但今天讲的每一条,前提都是你的库还活着、还能连、数据还在。库挂了,这些命令一句都用不上。

