数据库运维13 min read次阅读

Oracle 19c 装完能连只是开始:表空间、内存、AWR 和那些半夜弹出来的 ORA 报错

数据库装好、监听起来、业务连上了,很多人以为这事儿就算交差了。我早年也这么想,直到有天晚上被电话叫起来,说「系统卡死登不进去了」。上去一看,报错 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_targetpga_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$sessioneventblocking_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——这几件事做成了习惯,绝大多数半夜电话其实都能消在萌芽里。

至于备份恢复,那是另一门功夫,另有专文。但今天讲的每一条,前提都是你的库还活着、还能连、数据还在。库挂了,这些命令一句都用不上。

分享:

相关文章

把几块盘拼成一块放心盘:mdadm 软 RAID 的创建、换盘与重建监控
Linux运维14 min read

把几块盘拼成一块放心盘:mdadm 软 RAID 的创建、换盘与重建监控

有人觉得有了 ZFS 和 LVM 就不需要 mdadm 了,可真到给一台老服务器加两块盘做镜像、或者把一组 SATA 盘凑成读写盘的时候,mdadm 还是最顺手的那一个。这篇文章不堆概念,直接讲怎么从零建 RAID、盘坏了怎么热替换、重建进度怎么盯,以及那些文档不会告诉你的坑——比如 RAID5 掉盘重建时第二块盘也读不出、bitmap 没开导致重建慢一倍、还有 RAID 永远不是备份这件事。

评论区