接手一套Oracle生产库,最怕的就是“书到用时方恨少”。平时看过不少命令大全,真到故障或者巡检的时候,脑子一片空白,只能现场翻文档。今天这篇东西,不打算写成教科书式的命令罗列,而是按DBA日常工作的实际场景来梳理——从实例巡检、用户权限、备份恢复,到性能诊断、SQL调优,再到那些让人头疼的迁移和补丁操作。我把这些年实际用下来、频率最高、最容易踩坑的Oracle常用命令整理成一套可以直接照着用的操作清单,每个命令都带上了我自己的用法习惯和注意事项,希望能帮你少走点弯路。
1. 实例与数据库状态巡检:每天上班第一件事
很多DBA的习惯是一上班先看邮件、看监控告警,但我建议你先把下面的命令过一遍,尤其是数据库刚接手或者近期有过变更的时候。这套巡检命令基本覆盖了“数据库活着吗、跑得动吗、会不会马上满”这几个核心问题。
1.1 登录与基本信息确认
不要小看登录这一步,生产环境里登录方式错了,后面全是坑。我一般用操作系统认证登录,避免密码问题:
bash复制sqlplus / as sysdba
登录后第一件事,确认实例状态和版本:
sql复制SELECT STATUS, INSTANCE_NAME, VERSION, STARTUP_TIME, HOST_NAME
FROM V$INSTANCE;
这里重点看STATUS是否是OPEN,STARTUP_TIME是否和你预期的重启时间一致。如果状态是MOUNTED或者STARTED,说明数据库没有完全打开,需要进一步检查。版本信息很重要,很多命令在不同版本下行为有差异,后面讲到的很多特性在11g、12c、19c里表现都不太一样。
还有一个我几乎每天都要查的:
sql复制SELECT NAME, OPEN_MODE, DATABASE_ROLE, LOG_MODE, FORCE_LOGGING
FROM V$DATABASE;
OPEN_MODE显示READ WRITE还是READ ONLY,DATABASE_ROLE是PRIMARY还是PHYSICAL STANDBY,这决定了你到底是主库还是备库,千万别在备库上执行写操作。LOG_MODE如果是NOARCHIVELOG,那就要敲响警钟了——这种模式下基本只能做冷备,出了故障恢复窗口会非常难看。
1.2 会话与等待事件:数据库“卡”在哪
当业务反馈“系统很慢”的时候,不要急着重启,先看会话。我常用的三板斧:
sql复制-- 查看当前所有活跃会话
SELECT SID, SERIAL#, USERNAME, STATUS, MACHINE,
SQL_ID, EVENT, BLOCKING_SESSION, LOGON_TIME
FROM V$SESSION
WHERE STATUS = 'ACTIVE'
AND USERNAME IS NOT NULL
ORDER BY LOGON_TIME;
EVENT字段是重点。如果大量会话卡在enq: TX - row lock contention,说明有人在锁表;如果卡在db file sequential read,大概率是SQL执行计划有问题或者IO慢。BLOCKING_SESSION字段可以直接告诉我们是谁在挡路。
定位到阻塞源之后,确认无误再杀掉会话:
sql复制ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;
注意,IMMEDIATE表示立即中断,否则Oracle会等当前事务结束才断。杀会话一定要确认是哪个业务的会话,我见过有人把应用连接池的会话全杀了,结果应用集体重连,瞬间把库打爆。
如果只看等待事件分布,可以直接查视图:
sql复制SELECT EVENT, TOTAL_WAITS, TIME_WAITED_MICRO
FROM V$SYSTEM_EVENT
WHERE WAIT_CLASS != 'Idle'
ORDER BY TIME_WAITED_MICRO DESC
FETCH FIRST 10 ROWS ONLY;
这里排除Idle类事件(比如空等待),剩下的才是真正消耗时间的等待。这一条命令能快速判断瓶颈在CPU、IO还是锁竞争。
1.3 表空间与数据文件使用率
表空间满导致数据库hang住,这种事故在运维中太常见了。我建议每周至少检查一次,而且要按数据文件级别去看:
sql复制SELECT TABLESPACE_NAME, ROUND(SUM(BYTES)/1024/1024/1024, 2) AS SIZE_GB,
ROUND(SUM(MAXBYTES)/1024/1024/1024, 2) AS MAXSIZE_GB,
ROUND((SUM(BYTES) - SUM(FREE_SPACE))/SUM(BYTES)*100, 2) AS USED_PERCENT
FROM (
SELECT TABLESPACE_NAME, BYTES, MAXBYTES,
DECODE(AUTOEXTENSIBLE, 'YES', 0, BYTES) AS FREE_SPACE
FROM DBA_DATA_FILES
)
GROUP BY TABLESPACE_NAME
ORDER BY USED_PERCENT DESC;
简单版的话也可以直接查DBA_TABLESPACE_USAGE_METRICS:
sql复制SELECT TABLESPACE_NAME, USED_PERCENT, USED_SPACE, TABLESPACE_SIZE
FROM DBA_TABLESPACE_USAGE_METRICS
WHERE USED_PERCENT > 80
ORDER BY USED_PERCENT DESC;
习惯上我会把80%作为预警线,达到85%就开始准备扩容或者清理回收。如果表空间开启了自动扩展,数据文件一般不会满,但要注意MAXSIZE的限制,以及文件系统本身的剩余空间。很多人只看表空间使用率,忽略了磁盘满,这是另一个隐藏雷区。
1.4 归档日志与告警日志:故障的“前兆”
归档日志满是非常经典的问题,一旦归档目录写满,数据库会直接hang住。日常检查归档空间:
sql复制SELECT DEST_ID, ROUND(SPACE_USED/1024/1024/1024, 2) AS USED_GB
FROM V$RECOVERY_AREA_USAGE;
-- 或者直接看归档日志数量和最近切换时间
SELECT THREAD#, SEQUENCE#, FIRST_TIME, NEXT_TIME
FROM V$ARCHIVED_LOG
WHERE FIRST_TIME > SYSDATE - 1
ORDER BY SEQUENCE#;
如果发现归档切得很频繁,说明数据库日志量很大,这时候要看是不是有批量任务在跑,或者是否有应用在频繁提交小事务。另外,每天看一眼告警日志是必须的:
bash复制tail -200 $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace/alert_$ORACLE_SID.log
11g以后告警日志路径统一到了ADR(Automatic Diagnostic Repository)目录下。如果出现ORA-01555、ORA-01688、ORA-00060这些经典错误,赶紧查对应的时间点和SQL。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 用户、权限与口令安全:被遗忘的“等保命令”
用户管理这块,大部分DBA只会CREATE USER和GRANT,但真正要细究起来,坑不少。特别是等保测评来了,检查口令策略、登录失败锁定、密码有效期,一堆命令要会。
2.1 创建用户与授权:最小权限原则
创建用户的完整姿势,不是光建个用户名密码就完事:
sql复制CREATE USER scott IDENTIFIED BY "YourStrongPassword#2024"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users
PROFILE app_profile;
DEFAULT TABLESPACE和TEMPORARY TABLESPACE一定要显式指定,否则会用系统默认表空间,很多人习惯默认,结果把表建到了SYSTEM表空间,这是非常不好的习惯。QUOTA要控制住,除非是开发环境,否则不要给UNLIMITED,不然一个误操作能把表空间填满。
授权也要按需来,不要图省事直接给DBA角色:
sql复制-- 只赋予应用需要的权限
GRANT CONNECT, RESOURCE TO scott;
GRANT CREATE SESSION TO scott;
GRANT SELECT ON hr.employees TO scott;
GRANT EXECUTE ON hr.pkg_emp TO scott;
-- 查看用户拥有的角色和权限
SELECT * FROM DBA_ROLE_PRIVS WHERE GRANTEE = 'SCOTT';
SELECT * FROM DBA_TAB_PRIVS WHERE GRANTEE = 'SCOTT';
这里有个细节:RESOURCE角色在12c以后已经不再自动包含UNLIMITED TABLESPACE权限了,所以在12c以上版本,如果用户需要建表但不需要无限表空间,记得单独授权。Oracle 18c、19c里RESOURCE角色的权限集合也有变化,别拿老经验套新版本。
2.2 密码过期与口令策略:关闭密码有效期
“ORA-28001: the password has expired”这条报错,做过Oracle运维的应该都遇到过。默认的PROFILE有180天的密码有效期,系统上线跑着跑着突然开始批量报错,十有八九就是密码过期了。
查看当前密码有效期设置:
sql复制SELECT RESOURCE_NAME, LIMIT
FROM DBA_PROFILES
WHERE PROFILE = 'DEFAULT'
AND RESOURCE_NAME IN ('PASSWORD_LIFE_TIME', 'PASSWORD_GRACE_TIME', 'FAILED_LOGIN_ATTEMPTS', 'PASSWORD_LOCK_TIME');
如果不想让密码过期,直接把生命周期改成UNLIMITED:
sql复制ALTER PROFILE DEFAULT LIMIT PASSWORD_LIFE_TIME UNLIMITED;
已经过期的用户,即使改完profile也要手动改一次密码才能解锁:
sql复制ALTER USER scott IDENTIFIED BY "NewPassword#2024";
等保测评对密码策略有要求,需要设置复杂度的时候,可以配置PASSWORD_VERIFY_FUNCTION,Oracle自带的VERIFY_FUNCTION_11G(在$ORACLE_HOME/rdbms/admin/utlpwdmg.sql脚本里)可以做基础的复杂度检查:
sql复制@$ORACLE_HOME/rdbms/admin/utlpwdmg.sql
ALTER PROFILE DEFAULT LIMIT
FAILED_LOGIN_ATTEMPTS 5
PASSWORD_LOCK_TIME 1
PASSWORD_LIFE_TIME 90
PASSWORD_GRACE_TIME 7;
这里要注意,开启密码复杂度函数之后,所有新密码必须满足复杂度要求,否则会报ORA-28221。多个系统共用一个密码的话,策略改了之后密码很可能不符合要求,需要在窗口期统一改一遍。
2.3 审计与登录日志:安全事件溯源
安全方面我们需要关注谁在什么时间、从哪里登录了数据库:
sql复制-- 查看当前登录用户和会话信息
SELECT USERNAME, PROGRAM, MACHINE, OSUSER, LOGON_TIME, SID
FROM V$SESSION
WHERE USERNAME IS NOT NULL
ORDER BY LOGON_TIME DESC;
-- 开启登录审计(需要开启审计功能)
AUDIT CREATE SESSION WHENEVER SUCCESSFUL;
AUDIT CREATE SESSION WHENEVER NOT SUCCESSFUL;
-- 查询审计记录
SELECT OS_USERNAME, USERNAME, USERHOST, TIMESTAMP, ACTION_NAME, RETURNCODE
FROM DBA_AUDIT_TRAIL
WHERE TIMESTAMP > SYSDATE - 7
ORDER BY TIMESTAMP DESC;
开启审计会影响一定性能,尤其是审计所有会话的情况下,建议在测试环境评估后再上生产。另外,等保测评时经常要查的还有数据库版本补丁情况、最小权限配置、登录失败记录等,这些都可以用视图查出来,不需要导出大量日志文件再过滤。
3. 备份恢复与数据迁移:最怕用到的命令,却必须最熟练
备份恢复类命令,平时用不上,一旦要用就是救命的。冷迁移、RMAN、数据泵,每一种都要形成肌肉记忆。这里重点说三个场景。
3.1 冷备份冷迁移:最原始但最可靠的方式
涉及到迁移或者升级,很多时候冷迁移反而是最稳妥的方案。Oracle 11g数据库冷迁移的核心思路是:干净关闭数据库,拷贝所有数据文件、控制文件、日志文件、参数文件到新机器,然后启动。
完整步骤大概是:
bash复制# 1. 在源库记录当前路径
sqlplus / as sysdba
SQL> SELECT NAME FROM V$DATAFILE;
SQL> SELECT NAME FROM V$CONTROLFILE;
SQL> SELECT MEMBER FROM V$LOGFILE;
SQL> SHOW PARAMETER pfile;
SQL> SHOW PARAMETER spfile;
这些路径要记录下来,迁移后如果路径变了需要重建控制文件或者修改参数。
bash复制# 2. 干净关闭数据库
sqlplus / as sysdba
SQL> SHUTDOWN IMMEDIATE;
bash复制# 3. 拷贝文件到新机器(需要一致的目录结构,或者改好路径)
scp -r $ORACLE_HOME/dbs/orapw* $ORACLE_HOME/dbs/spfile* newhost:/u01/app/oracle/product/11.2.0/db_1/dbs/
scp -r /oradata/orcl newhost:/oradata/orcl
scp -r /u01/app/oracle/oradata/orcl/control01.ctl newhost:/u01/app/oracle/oradata/orcl/
bash复制# 4. 新机器上启动数据库到mount
sqlplus / as sysdba
SQL> STARTUP MOUNT;
SQL> ALTER DATABASE OPEN;
如果路径没变,基本到这里就完事了。如果路径变了,就需要CREATE CONTROLFILE REUSE DATABASE ... NORESETLOGS重新创建控制文件,或者用ALTER DATABASE RENAME FILE来改数据文件路径。
这里有几个关键点:
SHUTDOWN IMMEDIATE不等于SHUTDOWN ABORT。冷迁移要求干净关闭,也就是需要做检查点并且关闭所有数据文件。ABORT是模拟崩溃,不能用于冷迁移。- 拷贝参数文件时,
spfile如果找不到,数据库会去找pfile,建议两个一起拷。 - 迁移完成后,第一时间检查告警日志,看有没有异常。然后用
SELECT * FROM V$DATABASE;确认数据库状态。
冷迁移看起来简单,但因为涉及文件拷贝,最容易出的问题是漏文件。我习惯在拷贝之前先查询完整文件清单,然后逐个核对目标机器上文件是否齐全。
3.2 RMAN备份与恢复
RMAN是生产环境最常用的备份工具,我来说说最基本的日常用法和恢复思路。
首先是备份命令,可以在线备份而不影响业务:
bash复制rman target /
RMAN> BACKUP DATABASE PLUS ARCHIVELOG;
生产环境我一般会做分级备份策略,比如每周日0级全备,其他天累积增量,配合归档日志:
bash复制RMAN> CONFIGURE RETENTION POLICY TO REDUNDANCY 2;
RMAN> BACKUP INCREMENTAL LEVEL 0 DATABASE PLUS ARCHIVELOG DELETE INPUT;
恢复的场景很多,最常见的误删数据文件后恢复:
bash复制RMAN> STARTUP MOUNT;
RMAN> RESTORE DATAFILE '/oradata/orcl/users01.dbf';
RMAN> RECOVER DATAFILE '/oradata/orcl/users01.dbf';
RMAN> ALTER DATABASE OPEN;
如果是完全恢复,可以这样写:
bash复制RMAN> STARTUP MOUNT;
RMAN> RESTORE DATABASE;
RMAN> RECOVER DATABASE;
RMAN> ALTER DATABASE OPEN;
RMAN恢复最关键的思路是:RESTORE是拿文件回来,RECOVER是应用归档日志和在线日志把数据库推到一致状态。很多人RESTORE完了忘记RECOVER,数据库打开时报ORA-01113,其实是因为文件太旧了,缺少日志应用。
3.3 数据泵expdp/impdp:迁移数据的利器
数据泵比exp/imp强很多,也更常用。导出导入命令本身不复杂,复杂的是参数组合和日志分析。
导出某个用户的数据:
bash复制expdp scott/tiger@orcl DIRECTORY=DATA_PUMP_DIR DUMPFILE=scott_20240101.dmp LOGFILE=exp_scott_20240101.log SCHEMAS=scott
导入到新库:
bash复制impdp scott/tiger@newdb DIRECTORY=DATA_PUMP_DIR DUMPFILE=scott_20240101.dmp LOGFILE=imp_scott_20240101.log REMAP_SCHEMA=scott:scott_new REMAP_TABLESPACE=USERS:USERS_NEW TABLE_EXISTS_ACTION=SKIP
这里有几个常见参数的用法:
REMAP_SCHEMA:源库用户迁移到目标库不同用户时使用REMAP_TABLESPACE:迁移后表空间名变了就用它TABLE_EXISTS_ACTION:目标表已存在时怎么处理,可选SKIP、APPEND、TRUNCATE、REPLACEPARALLEL:并行度,大表并行导出能显著提速,但不要超过CPU核数
在19c里,impdp偶尔会遇到“日志记录不完整”的情况,说白了就是日志文件写一半就停了,但没有报错。这个问题多半和网络、目标库的COMMIT行为有关。我的经验是加一个参数:
bash复制impdp ... COMMIT=Y
加上COMMIT=Y之后,每个数组插入后立即提交,日志输出会及时刷新。另外,导入完成后可以对比源库和目标库的表数量、行数来验证数据完整性。常规的验证命令:
sql复制SELECT COUNT(*) FROM ALL_TABLES WHERE OWNER = 'SCOTT';
很多人只关心expdp导出的dump文件大小,忽略日志文件最后的“Job succeeded”字样。我说实话,这个日志一定要看到最后的成功完成,否则前面一切正常都不算数。
4. 性能诊断与SQL调优:DBA的“破案”工具
性能问题是DBA日常最常被拉去处理的问题。Oracle提供了很多诊断工具,关键是要形成一套排查思路,而不是东一榔头西一棒子。
4.1 AWR/ASH报告:定位瓶颈的第一站
AWR报告是Oracle最核心的性能诊断工具。生成一份AWR报告的命令:
sql复制-- 查看可用的AWR快照
SELECT SNAP_ID, BEGIN_INTERVAL_TIME, END_INTERVAL_TIME
FROM DBA_HIST_SNAPSHOT
ORDER BY SNAP_ID DESC FETCH FIRST 10 ROWS ONLY;
-- 生成AWR报告(需要知道快照ID区间)
@$ORACLE_HOME/rdbms/admin/awrrpt.sql
然后按提示输入你想要的报告类型(html或text)、起始快照ID和结束快照ID。HTML格式在浏览器里看效率更高,尤其看Top Event、SQL Statistics这些部分。
如果问题发生在最近一两个小时,来不及等AWR快照,就用ASH:
sql复制SELECT SQL_ID, COUNT(*), EVENT,
ROUND(COUNT(*)*100/SUM(COUNT(*)) OVER(), 2) AS PERCENT
FROM V$ACTIVE_SESSION_HISTORY
WHERE SAMPLE_TIME > SYSDATE - INTERVAL '1' HOUR
GROUP BY SQL_ID, EVENT
ORDER BY COUNT(*) DESC
FETCH FIRST 20 ROWS ONLY;
通过这条命令,我们能快速知道最近一小时内哪些SQL在消耗数据库资源、卡在什么等待事件上。这是我最常用的“破案”第一步。
4.2 执行计划:看懂SQL怎么跑
执行计划是SQL调优的核心。一条SQL慢,首先要看它的执行计划是否合理。
获取执行计划的方式有很多,我推荐两种:
第一种,直接用DBMS_XPLAN展示最近执行的SQL:
sql复制SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', 0, 'ALLSTATS LAST'));
第二种,在SQLPLUS里开AUTOTRACE:
sql复制SET AUTOTRACE ON;
SELECT * FROM employees WHERE department_id = 60;
要看执行计划里的关键信息:TABLE ACCESS FULL(全表扫)和INDEX RANGE SCAN(索引范围扫)。全表扫并不一定慢,表如果很小,全扫反而比走索引高效;但大表没过滤条件地全扫,基本就是问题。
执行计划经常因为统计信息不准确而变化。陈旧或者缺失的统计信息会导致优化器做出错误选择。刷新统计信息的命令得会用:
sql复制EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMPLOYEES', CASCADE => TRUE);
统计信息收集在生产环境要避开业务高峰,大表收集时间很长,建议用GRANULARITY => 'AUTO'和并行度参数控制:
sql复制EXEC DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMPLOYEES', PARALLEL => 4);
4.3 固定执行计划:让SQL稳定下来
很多时候,SQL昨天跑得飞快,今天突然变慢,而且执行计划确实变了。这种情况在企业ERP系统里特别常见,尤其是字段值分布不均匀的时候,直方图一更新,优化器就“乱来”了。这时候固定执行计划就是保命手段。
方案之一是SQL Plan Baseline。先找到想要固定的SQL_ID,查看其历史执行计划:
sql复制SELECT PLAN_NAME, TIMESTAMP, ENABLED, ACCEPTED
FROM DBA_SQL_PLAN_BASELINES
WHERE SQL_HANDLE IN (
SELECT SQL_HANDLE FROM DBA_SQL_PLAN_BASELINES WHERE SQL_TEXT LIKE '%你的SQL特征片段%'
);
把一个当前表现好的执行计划固化成Baseline:
bash复制exec dbms_output.put_line('test');
VARIABLE v_plan_name VARCHAR2(100);
EXEC :v_plan_name := DBMS_SQLTUNE.CREATE_SQL_PLAN_BASELINE(SQL_ID => '&sql_id', PLAN_NAME => 'SQL_PLAN_FIX_001', FIXED => 'YES');
被标记为FIXED的Baseline会优先被优化器使用。这个方案比改SQL加HINT更平滑,不需要改应用,对生产非常友好。
不过要提醒一句,固定执行计划是权宜之计,不是最终方案。根本原因可能是统计信息不准、索引缺失、或者SQL写得有问题。我曾经遇到过一个SQL被固定了执行计划,结果一个月后数据量翻倍,固定计划反而变成灾难。所以固定完要持续监控,不要一劳永逸。
4.4 分页查询与树形查询:高频SQL写法
作为DBA,自己写SQL查数据的时间也不少。分页查询在Oracle里最标准的写法是ROWNUM或者FETCH FIRST,12c以后更推荐FETCH:
sql复制-- 旧写法,需要嵌套一层
SELECT * FROM (
SELECT A.*, ROWNUM RN
FROM (SELECT * FROM employees ORDER BY salary DESC) A
WHERE ROWNUM <= 40
) WHERE RN > 20;
-- 12c以后的写法
SELECT * FROM employees ORDER BY salary DESC OFFSET 20 ROWS FETCH NEXT 20 ROWS ONLY;
OFFSET和FETCH写法直观,但要注意大页码OFFSET性能会下降,因为数据库要扫描并丢弃前面所有行。如果分页深度很大(比如第10000页),还是用基于ROWNUM的写法或者基于游标的分页更靠谱。
树形查询也是DBA日常查数的常客。比如查组织架构、BOM结构、层级菜单:
sql复制SELECT EMPLOYEE_ID, FIRST_NAME, MANAGER_ID, LEVEL,
SYS_CONNECT_BY_PATH(FIRST_NAME, '/') AS PATH
FROM EMPLOYEES
START WITH MANAGER_ID IS NULL
CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID;
这里的核心是START WITH指定根节点,CONNECT BY PRIOR表示父子关系方向。要注意,CONNECT BY循环会导致ORA-01436,需要在语句里加NOCYCLE。查完顺便用LEVEL字段可以看到层级深度,SYS_CONNECT_BY_PATH可以拼出完整路径。
5. 日期处理与常用函数:trunc(sysdate)背后的细节
热搜里有关trunc(sysdate)的内容,确实,这个函数几乎是Oracle SQL高频词。但很多人只知其然不知其所以然,导致在日期比较时出各种诡异的结果。
TRUNC的核心作用是截断日期,默认截断到当天的零点:
sql复制SELECT TRUNC(SYSDATE) FROM DUAL;
-- 返回结果是今天的00:00:00
但如果我想得到本周的起始日、月初、季度初、年初,需要带格式参数:
sql复制SELECT TRUNC(SYSDATE, 'MM') AS MONTH_START,
TRUNC(SYSDATE, 'Q') AS QUARTER_START,
TRUNC(SYSDATE, 'YEAR') AS YEAR_START,
TRUNC(SYSDATE, 'D') AS WEEK_START
FROM DUAL;
很多人习惯做月度统计的时候用TO_CHAR(SYSDATE,'YYYYMM') = TO_CHAR(COLUMN_DATE,'YYYYMM'),这其实是性能杀手,因为它在列上套了函数,导致索引失效。更好的写法是:
sql复制SELECT * FROM orders
WHERE order_date >= TRUNC(SYSDATE, 'MM')
AND order_date < TRUNC(ADD_MONTHS(SYSDATE, 1), 'MM');
这条语句中的TRUNC(TO_DATE('20240101', 'YYYYMMDD'))也是常用写法,用于忽略时间部分只比日期。
另外,关于dual表,有同学问dual最多能存多大。这个问题的本质是:DUAL是Oracle内置的虚拟表,只有1行1列,不需要也不应该往里插入数据。如果你执意要往DUAL里插数据,会报错,因为优化器假定它最多返回一行。真正需要多行的时候,用CONNECT BY LEVEL <= N来生成序列:
sql复制SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 10;
这在做连续日期补全、报表日期维度填充时非常实用。比如生成最近30天的日期序列:
sql复制SELECT TRUNC(SYSDATE) - LEVEL + 1 AS DAY
FROM DUAL
CONNECT BY LEVEL <= 30;
另外提一句,Oracle里VARCHAR2做等值比较是不区分大小写的?不对,默认是区分大小写的,除非设置NLS_COMP和NLS_SORT。所以经常有人问“Oracle判断字符是不是字母”,可以用正则:
sql复制SELECT REGEXP_LIKE('Abc', '^[[:alpha:]]+$') FROM DUAL;
还有个查询总金额这种聚合需求,很多人会忽略空值的影响。SUM会忽略NULL,但COUNT(*)不会忽略NULL行,COUNT(COLUMN)会忽略NULL。这些细节在做报表对账的时候非常容易出问题。
6. 运维中容易“栽跟头”的命令细节
最后这部分,想聊聊几个不在常规命令清单里、但实际运维中极易踩坑的场景。
6.1 进入ASM实例与ASMCMD操作
RAC环境或者使用了ASM存储的单实例,有时候需要进ASM实例操作。我遇到过不少新手不知道ASM实例是啥、怎么进。
bash复制# 进入ASM实例
sqlplus / as sysasm
注意,不是s as sysdba。ASM实例需要用SYSASM权限登录,和数据库实例的SYSDBA是两套体系。进去以后查看磁盘组:
sql复制SELECT NAME, STATE, TYPE, TOTAL_MB, FREE_MB
FROM V$ASM_DISKGROUP;
如果要做磁盘组层面的文件操作,用ASMCMD命令:
bash复制asmcmd
ASMCMD> ls
ASMCMD> cd +DATA/orcl/datafile
ASMCMD> pwd
ASMCMD> du
ASMCMD> cp +DATA/orcl/datafile/users.257.123456 +BACKUP/orcl/datafile/
ASMCMD里最容易出错的就是路径,ASM路径以加号开头,比如+DATA/ORCL/DATAFILE/USERS.257.123456,大小写和格式必须严格匹配。当你听到“明明文件在那里,就是报错说找不到”的反馈,八成是路径格式写错了。
6.2 12c删除不干净与容器数据库清理
很多人在做Oracle 12c清理或者卸载的时候遇到“删除不干净”的问题,比如重新安装时提示已经有了Oracle主目录。这通常是几个残留位置没清理,包括:
/etc/oratab里的条目/etc/oraInst.loc里的inventory信息- 环境变量里的
ORACLE_HOME指向已删除的路径 - 系统服务里残留的监听、集群进程
如果只是删库不删软件,在12c里要注意PDB和CDB的关系。删除PDB:
sql复制ALTER PLUGGABLE DATABASE pdbname CLOSE IMMEDIATE;
DROP PLUGGABLE DATABASE pdbname INCLUDING DATAFILES;
这里的INCLUDING DATAFILES很容易漏,漏掉之后磁盘上的数据文件就变成孤儿文件,下次创建PDB同名时会冲突。
6.3 打补丁时的opatch命令
Oracle 12c和19c的补丁管理,opatch是核心工具。补丁包下载后,很多人栽在“opatch版本不匹配”上。
查看当前OPatch版本:
bash复制$ORACLE_HOME/OPatch/opatch version
查看已安装补丁:
bash复制$ORACLE_HOME/OPatch/opatch lsinventory
打补丁前对比补丁说明要求的OPatch版本,如果不够就先升级OPatch工具。补丁应用命令:
bash复制cd /u01/app/oracle/product/19.0.0/dbhome_1/OPatch
./opatch apply -oh $ORACLE_HOME -silent
如果打了补丁之后发现问题,回滚:
bash复制./opatch rollback -id 12345678 -oh $ORACLE_HOME
opatch中最容易出现的问题是补丁之间互相冲突。在打补丁之前,我建议先用opatch prereq CheckConflictAgainstOHWithDetail做一次冲突检查。另外,补丁应用期间数据库需要停机(如果是数据库补丁),或者至少实例不相关(如果是GI补丁),千万不要在运行中的生产库上直接打数据库补丁。
6.4 grid补丁与补丁包下载的“匹配”问题
Oracle 12c grid补丁包下载时,很多人会搞混“Database Patch”和“Grid Infrastructure Patch”的区别。简单讲:
- Grid补丁打在GI_HOME下,主要影响集群软件、ASM
- Database补丁打在DB_HOME下,主要影响数据库实例
两者不能混用,而且补丁包有时会区分“Database Bundle Patch”和“Grid Infrastructure Bundle Patch”。下载补丁时建议用My Oracle Support里的“Patch Search”按版本号、平台和补丁类型去筛选,不要随便找个看起来差不多的补丁就拿去装。
另外,云环境(比如Oracle Cloud)下的补丁操作和本地环境会有差异,云上可能有托管的补丁流程,不建议手工操作,除非你是自建VM。
这些命令和思路都是我日常工作中反复使用的,谈不上什么高深技巧,但每一条都实打实地解决过问题。如果你刚接手Oracle数据库的运维,不妨把前面几段巡检命令存成SQL脚本,每天定时跑一遍,输出到日志文件,坚持一个月,你对这个库的“脾气”会了解很多。最后说一句,Oracle命令再多,真正到关键时刻敢下手、敢恢复、敢调优的能力,是靠平时一次次演练堆出来的,光看不练永远只是“知道”,不是“会”。
