第14章 SQL优化
14.1 执行计划分析
14.1.1 执行计划概述
执行计划是CBO(Cost-Based Optimizer)根据SQL语句、统计信息、对象定义等生成的执行步骤。分析执行计划是SQL优化的第一步。
常用执行计划获取方法:
sql
-- 方法1:EXPLAIN PLAN FOR
SQL> explain plan for
select e.ename, e.sal, d.dname
from scott.emp e, scott.dept d
where e.deptno = d.deptno
and e.sal > 3000;
SQL> select * from table(dbms_xplan.display);
-- 方法2:DBMS_XPLAN显示实际执行计划
SQL> select * from table(dbms_xplan.display_cursor(
sql_id => 'abcd1234xyz',
cursor_child_no => 0,
format => 'ALLSTATS LAST'));
-- 方法3:查看最近执行SQL执行计划
SQL> select * from table(dbms_xplan.display_cursor(
format => 'ALLSTATS LAST'));
-- 方法4:查看AWR中的执行计划
SQL> select * from table(dbms_xplan.display_awr(
sql_id => 'abcd1234xyz'));
14.1.2 执行计划解读
关键操作类型:
| 操作 | 说明 | 关注点 |
|---|---|---|
| TABLE ACCESS FULL | 全表扫描 | 大表全表扫描通常是性能问题 |
| TABLE ACCESS BY INDEX ROWID | 通过索引ROWID访问表 | 需要配合索引使用 |
| INDEX UNIQUE SCAN | 唯一索引扫描 | 高效,通常只返回1行 |
| INDEX RANGE SCAN | 索引范围扫描 | 适用于范围查询 |
| INDEX FULL SCAN | 索引全扫描 | 适用于全索引覆盖 |
| INDEX FAST FULL SCAN | 索引快速全扫描 | 多块读,适合全索引扫描 |
| NESTED LOOPS | 嵌套循环连接 | 小表驱动大表 |
| HASH JOIN | 哈希连接 | 大表连接,等值条件 |
| SORT MERGE JOIN | 排序合并连接 | 排序操作,非等值连接 |
| SORT (ORDER BY / GROUP BY) | 排序操作 | 检查是否需要排序 |
| TABLE ACCESS BY LOCAL INDEX | 分区表本地索引扫描 | 分区裁剪 |
14.1.3 执行计划格式
sql
-- 查看详细执行计划(包含统计信息)
SQL> select * from table(dbms_xplan.display_cursor(
format => 'TYPICAL'));
-- 查看带I/O统计的执行计划
SQL> select * from table(dbms_xplan.display_cursor(
format => 'ALLSTATS LAST'));
-- 查看最详细的执行计划
SQL> select * from table(dbms_xplan.display_cursor(
format => 'ADVANCED ALLSTATS LAST'));
Output format example:
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 3 (100)|
| 1 | NESTED LOOPS | | 5 | 190 | 3 (0)|
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 5 | 120 | 2 (0)|
|* 3 | INDEX RANGE SCAN | IDX_SAL | 5 | | 1 (0)|
| 4 | TABLE ACCESS BY INDEX ROWID| DEPT | 1 | 14 | 1 (0)|
|* 5 | INDEX UNIQUE SCAN | PK_DEPT | 1 | | 0 (0)|
--------------------------------------------------------------------------
14.1.4 执行计划分析要点
- Cost(成本):CBO计算的总成本,数值越大表示越昂贵
- Rows(行数):预估返回的行数,与实际行数差异大说明统计信息不准确
- Access Predicate(访问谓词):用于访问数据行的条件
- Filter Predicate(过滤谓词):用于过滤数据行的条件
- Operation(操作):执行步骤,从最内层到最外层执行
分析步骤:
- 从执行计划最内层(缩进最多)开始查看
- 检查是否有全表扫描(TABLE ACCESS FULL)且返回行数很少
- 检查是否有不必要的排序操作
- 检查JOIN顺序是否合理(小表驱动大表)
- 检查预估行数与实际行数的差异
14.2 索引优化策略
14.2.1 索引类型选择
| 索引类型 | 适用场景 | 注意事项 |
|---|---|---|
| B-Tree索引 | 高基数(NDV大)列,等值查询 | 默认索引类型 |
| 唯一索引 | 主键、唯一约束 | 确保列值唯一 |
| 复合索引 | 多条件查询 | 列顺序很重要(最左前缀) |
| 位图索引 | 低基数列,数据仓库环境 | DML操作影响大 |
| 反向键索引 | 序列增长列,减少索引块争用 | 不支持范围查询 |
| 函数索引 | 基于函数/表达式的查询 | 需要手动收集统计信息 |
| 分区索引 | 分区表 | 全局索引 vs 本地索引 |
| 全文索引(Oracle Text) | 全文搜索 | 需要单独的Oracle Text授权 |
14.2.2 索引设计原则
索引选择标准:
sql
-- 查看列的选择性(Selectivity)
SQL> select column_name, num_distinct, num_rows,
round(num_distinct / nullif(num_rows, 0), 4) as selectivity
from dba_tab_col_statistics
where owner = 'SCOTT' and table_name = 'EMP'
order by selectivity desc;
-- 选择性 > 0.1 的列适合建索引
-- 选择性 < 0.01 的列不适合建索引
复合索引列顺序规则:
- 等值条件的列放在前面
- 选择性高的列放在前面
- 范围查询(>、<、BETWEEN)的列放在后面
- 频繁用于排序的列放在最后
sql
-- 示例:查询条件 WHERE dept_no = 10 AND sal > 3000 ORDER BY hire_date
-- 推荐复合索引:CREATE INDEX idx_emp_1 ON emp(dept_no, sal, hire_date);
14.2.3 索引创建与维护
sql
-- 创建B-Tree索引
SQL> create index idx_emp_deptno on scott.emp(deptno)
tablespace users
pctfree 10
initrans 2
maxtrans 255
storage (initial 64k next 64k pctincrease 0);
-- 创建复合索引
SQL> create index idx_emp_dept_sal on scott.emp(deptno, sal desc);
-- 创建函数索引
SQL> create index idx_emp_upper_name on scott.emp(upper(ename));
-- 创建位图索引
SQL> create bitmap index idx_emp_gender on scott.emp(gender);
-- 重建索引(在线重建)
SQL> alter index idx_emp_deptno rebuild online;
-- 重建索引分区
SQL> alter index idx_emp_deptno rebuild partition p1;
-- 合并索引(仅适用于B-tree)
SQL> alter index idx_emp_deptno coalesce;
-- 监控索引使用情况
SQL> alter index idx_emp_deptno monitoring usage;
SQL> select * from v$object_usage;
SQL> alter index idx_emp_deptno nomonitoring usage;
14.2.4 索引使用分析
sql
-- 查看索引使用频率
SQL> select io.name as index_name,
t.name as table_name,
u.monitor, u.used, u.start_monitoring, u.end_monitoring
from sys.object_usage u, sys.obj$ io, sys.obj$ t
where u.obj# = io.obj#
and io.owner# = t.owner#
and io.type# = 1
and t.obj# = u.obj#;
-- 查看未被使用的索引
SQL> select index_name, table_name, uniqueness
from dba_indexes
where owner = 'SCOTT'
and index_name not in (
select index_name from v$object_usage where used = 'YES'
);
-- 查看索引的聚类因子(Clustering Factor)
SQL> select index_name, clustering_factor, num_rows,
round(clustering_factor / nullif(num_rows, 0), 2) as cf_ratio
from dba_ind_statistics
where owner = 'SCOTT'
order by cf_ratio desc;
-- CF接近块数:数据有序,索引效率高
-- CF接近行数:数据无序,索引效率低
14.2.5 索引维护策略
- 定期检查索引碎片:当索引碎片率超过30%时考虑重建
- 监控无用索引:定期清理未被使用的索引
- 在线重建 :使用
REBUILD ONLINE避免影响生产 - 索引压缩:对复合索引的前缀列使用压缩
sql
-- 检查索引碎片
SQL> select name, height, del_lf_rows, lf_rows,
round(del_lf_rows / nullif(lf_rows, 0) * 100, 2) as frag_pct
from index_stats
where name = 'IDX_EMP_DEPTNO';
-- 创建压缩索引
SQL> create index idx_emp_comp on scott.emp(deptno, job, sal) compress 2;
-- 重建索引使用压缩
SQL> alter index idx_emp_comp rebuild compress 2;
14.3 SQL改写与优化
14.3.1 SQL改写基本原则
SQL改写是SQL优化中最直接有效的手段。以下是一些核心原则:
- 减少访问数据量:使用合适的过滤条件
- 减少扫描次数:避免重复扫描
- 优化JOIN顺序:小表驱动大表
- 避免不必要的排序:减少ORDER BY、GROUP BY、DISTINCT
- 使用合适的连接方式:INNER JOIN、LEFT JOIN、SEMI JOIN
14.3.2 常见SQL改写技巧
技巧1:使用EXISTS替代IN
sql
-- 慢:使用IN
SELECT * FROM emp WHERE deptno IN (SELECT deptno FROM dept WHERE loc = 'NEW YORK');
-- 快:使用EXISTS(子查询返回小结果集时)
SELECT * FROM emp e
WHERE EXISTS (SELECT 1 FROM dept d WHERE d.deptno = e.deptno AND d.loc = 'NEW YORK');
技巧2:避免使用NOT IN
sql
-- 慢:NOT IN(可能导致全表扫描,且不处理NULL)
SELECT * FROM emp WHERE deptno NOT IN (SELECT deptno FROM dept);
-- 快:使用NOT EXISTS
SELECT * FROM emp e
WHERE NOT EXISTS (SELECT 1 FROM dept d WHERE d.deptno = e.deptno);
-- 或使用LEFT JOIN
SELECT e.* FROM emp e
LEFT JOIN dept d ON e.deptno = d.deptno
WHERE d.deptno IS NULL;
技巧3:使用UNION ALL替代UNION
sql
-- 慢:UNION(自动去重,需要排序)
SELECT ename FROM emp
UNION
SELECT ename FROM emp_archive;
-- 快:UNION ALL(不需要排序去重,数据不重复时)
SELECT ename FROM emp
UNION ALL
SELECT ename FROM emp_archive;
技巧4:避免函数包裹列
sql
-- 慢:函数包裹列(无法使用索引)
SELECT * FROM emp WHERE UPPER(ename) = 'SMITH';
-- 快:使用函数索引或改写
SELECT * FROM emp WHERE ename = 'SMITH';
-- 或创建函数索引
CREATE INDEX idx_emp_upper_name ON emp(UPPER(ename));
技巧5:使用CASE WHEN减少表扫描
sql
-- 慢:多次扫描同一表
SELECT COUNT(*) FROM emp WHERE deptno = 10;
SELECT COUNT(*) FROM emp WHERE deptno = 20;
SELECT COUNT(*) FROM emp WHERE deptno = 30;
-- 快:一次扫描
SELECT COUNT(CASE WHEN deptno = 10 THEN 1 END) as dept10_cnt,
COUNT(CASE WHEN deptno = 20 THEN 1 END) as dept20_cnt,
COUNT(CASE WHEN deptno = 30 THEN 1 END) as dept30_cnt
FROM emp;
14.3.3 分页查询优化
sql
-- 慢:传统分页(OFFSET会扫描所有行)
SELECT * FROM (
SELECT rownum rn, t.* FROM (
SELECT * FROM emp ORDER BY empno
) t WHERE rownum <= 40
) WHERE rn >= 21;
-- 快:使用游标分页(Key Set Pagination)
SELECT * FROM emp
WHERE empno > 100
ORDER BY empno
FETCH FIRST 20 ROWS ONLY;
-- 或使用ROWID分页
SELECT * FROM emp
WHERE rowid IN (
SELECT rid FROM (
SELECT rowid as rid, rownum as rn FROM (
SELECT * FROM emp ORDER BY empno
) WHERE rownum <= 40
) WHERE rn >= 21
);
14.3.4 多表连接优化
sql
-- 推荐:使用WITH子句(CTE)简化复杂查询
WITH dept_summary AS (
SELECT deptno, COUNT(*) as emp_count, SUM(sal) as total_sal
FROM emp
GROUP BY deptno
)
SELECT d.dname, d.loc, ds.emp_count, ds.total_sal
FROM dept d
JOIN dept_summary ds ON d.deptno = ds.deptno
ORDER BY ds.total_sal DESC;
-- 推荐:使用MERGE替代UPDATE+JOIN
MERGE INTO emp_target t
USING emp_source s
ON (t.empno = s.empno)
WHEN MATCHED THEN UPDATE SET
t.sal = s.sal,
t.comm = s.comm;
14.3.5 SQL改写检查清单
- 是否使用了SELECT *(应只选择需要的列)?
- WHERE条件中是否有函数包裹列导致索引失效?
- 是否可以使用EXISTS替代IN?
- 是否可以使用UNION ALL替代UNION?
- 是否有多表连接时没有使用关联条件?
- 是否有不必要的DISTINCT操作?
- 是否有不必要的ORDER BY操作?
- 是否可以在子查询中先过滤再JOIN?
- 是否可以考虑使用WITH子句简化逻辑?
14.4 Hint使用与绑定变量
14.4.1 Hint概述
Hint是SQL中特殊的注释指令,用于强制CBO使用特定的执行计划。Hint应该谨慎使用,通常在统计信息准确但CBO选择了次优执行计划时使用。
Hint的使用语法:
sql
SELECT /*+ hint_name(parameters) */ columns FROM table;
14.4.2 常用Hint
访问路径Hint:
sql
-- 强制全表扫描
SELECT /*+ FULL(emp) */ * FROM scott.emp WHERE sal > 3000;
-- 强制使用索引
SELECT /*+ INDEX(emp idx_emp_sal) */ * FROM scott.emp WHERE sal > 3000;
-- 强制使用复合索引(指定列)
SELECT /*+ INDEX(emp idx_emp_dept_sal) */ * FROM scott.emp
WHERE deptno = 10 AND sal > 3000;
-- 强制不使用索引
SELECT /*+ NO_INDEX(emp idx_emp_sal) */ * FROM scott.emp WHERE sal > 3000;
连接Hint:
sql
-- 强制使用嵌套循环连接
SELECT /*+ USE_NL(e d) */ * FROM scott.emp e, scott.dept d
WHERE e.deptno = d.deptno;
-- 强制使用哈希连接
SELECT /*+ USE_HASH(e d) */ * FROM scott.emp e, scott.dept d
WHERE e.deptno = d.deptno;
-- 强制使用排序合并连接
SELECT /*+ USE_MERGE(e d) */ * FROM scott.emp e, scott.dept d
WHERE e.deptno = d.deptno;
-- 指定驱动表(小表)
SELECT /*+ LEADING(d) USE_NL(e) */ * FROM scott.emp e, scott.dept d
WHERE e.deptno = d.deptno;
并行Hint:
sql
-- 强制并行执行
SELECT /*+ PARALLEL(emp, 4) */ * FROM scott.emp;
-- 强制使用并行DML
INSERT /*+ PARALLEL(emp, 4) */ INTO scott.emp SELECT * FROM scott.emp_temp;
-- 强制串行执行
SELECT /*+ NO_PARALLEL(emp) */ * FROM scott.emp;
其他常用Hint:
sql
-- 使用物化视图
SELECT /*+ REWRITE(mv_sales) */ * FROM sales;
-- 强制使用查询结果缓存
SELECT /*+ RESULT_CACHE */ * FROM scott.emp;
-- 强制使用星型转换
SELECT /*+ STAR_TRANSFORMATION */ * FROM fact, dim1, dim2
WHERE fact.key1 = dim1.key1 AND fact.key2 = dim2.key2;
-- 指定优化器模式
SELECT /*+ OPTIMIZER_FEATURES_ENABLE('19.1.0') */ * FROM scott.emp;
14.4.3 Hint使用注意事项
- Hint名称拼写错误:Oracle不会报错,但会忽略Hint
- Hint冲突:多个冲突的Hint会导致所有Hint被忽略
- 统计信息变化:Hint绑定后,即使统计信息变化也不会改变执行计划
- 版本兼容性:某些Hint在低版本中不可用
- 优先使用SQL Profile:在不修改SQL的情况下固定执行计划
14.4.4 绑定变量
绑定变量是SQL优化中最重要的实践之一。使用绑定变量可以避免硬解析,减少库缓存竞争。
sql
-- 不使用绑定变量(每次硬解析)
SELECT * FROM scott.emp WHERE empno = 7369;
SELECT * FROM scott.emp WHERE empno = 7499;
SELECT * FROM scott.emp WHERE empno = 7521;
-- 使用绑定变量(一次软解析,多次执行)
SELECT * FROM scott.emp WHERE empno = :emp_no;
-- 在PL/SQL中使用绑定变量
DECLARE
v_empno emp.empno%TYPE := 7369;
v_ename emp.ename%TYPE;
BEGIN
SELECT ename INTO v_ename FROM emp WHERE empno = v_empno;
DBMS_OUTPUT.PUT_LINE(v_ename);
END;
/
14.4.5 绑定变量窥探(Bind Peeking)与自适应游标
sql
-- 查看绑定变量值
SQL> select sql_id, name, datatype_string, value_string
from v$sql_bind_capture
where sql_id = 'abcd1234xyz';
-- 查看游标共享情况
SQL> select sql_id, child_number, executions,
is_bind_sensitive, is_bind_aware, is_shareable,
plan_hash_value
from v$sql
where sql_id = 'abcd1234xyz';
-- 启用自适应游标共享(默认启用)
SQL> alter system set optimizer_adaptive_cursor_sharing=true;
14.4.6 绑定变量使用建议
| 场景 | 建议 |
|---|---|
| OLTP系统 | 必须使用绑定变量,提高并发处理能力 |
| 数据仓库 | 可以考虑使用字面值,便于CBO选择最优计划 |
| 分区表查询 | 使用绑定变量可能导致分区裁剪失效,需谨慎 |
| 范围查询 | 绑定变量可能导致非最优计划,关注适应游标 |
14.5 分区表优化
14.5.1 分区表概述
分区表将大表分割成多个小的、更易管理的分区,可以提高查询性能、管理效率和可用性。
分区的核心优势:
- 分区裁剪:只扫描相关分区,减少I/O
- 分区维护:TRUNCATE/DROP分区操作高效
- 并行执行:可以并行扫描多个分区
- 数据管理:便于数据归档和生命周期管理
14.5.2 分区类型
sql
-- 范围分区(Range Partitioning)
CREATE TABLE sales_range (
sale_id NUMBER,
sale_date DATE,
amount NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2023_q1 VALUES LESS THAN (TO_DATE('2023-04-01','YYYY-MM-DD')),
PARTITION p_2023_q2 VALUES LESS THAN (TO_DATE('2023-07-01','YYYY-MM-DD')),
PARTITION p_2023_q3 VALUES LESS THAN (TO_DATE('2023-10-01','YYYY-MM-DD')),
PARTITION p_2023_q4 VALUES LESS THAN (TO_DATE('2024-01-01','YYYY-MM-DD')),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
-- 列表分区(List Partitioning)
CREATE TABLE sales_list (
sale_id NUMBER,
region VARCHAR2(20),
amount NUMBER
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES ('北京', '天津', '河北'),
PARTITION p_south VALUES ('广东', '深圳', '海南'),
PARTITION p_east VALUES ('上海', '江苏', '浙江'),
PARTITION p_other VALUES (DEFAULT)
);
-- 哈希分区(Hash Partitioning)
CREATE TABLE sales_hash (
sale_id NUMBER,
customer_id NUMBER,
amount NUMBER
)
PARTITION BY HASH (customer_id)
PARTITIONS 8;
-- 复合分区(Composite Partitioning)
CREATE TABLE sales_composite (
sale_id NUMBER,
sale_date DATE,
region VARCHAR2(20),
amount NUMBER
)
PARTITION BY RANGE (sale_date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
SUBPARTITION sp_north VALUES ('北京', '天津'),
SUBPARTITION sp_south VALUES ('广东', '深圳'),
SUBPARTITION sp_other VALUES (DEFAULT)
) (
PARTITION p_2023_q1 VALUES LESS THAN (TO_DATE('2023-04-01','YYYY-MM-DD')),
PARTITION p_2023_q2 VALUES LESS THAN (TO_DATE('2023-07-01','YYYY-MM-DD')),
PARTITION p_2023_q3 VALUES LESS THAN (TO_DATE('2023-10-01','YYYY-MM-DD')),
PARTITION p_2023_q4 VALUES LESS THAN (TO_DATE('2024-01-01','YYYY-MM-DD'))
);
14.5.3 分区索引
sql
-- 本地分区索引(与表分区一致)
SQL> CREATE INDEX idx_sales_date_local ON sales_range(sale_date) LOCAL;
-- 全局分区索引(独立分区策略)
SQL> CREATE INDEX idx_sales_global ON sales_range(sale_id)
GLOBAL PARTITION BY RANGE (sale_id) (
PARTITION p1 VALUES LESS THAN (100000),
PARTITION p2 VALUES LESS THAN (200000),
PARTITION p3 VALUES LESS THAN (MAXVALUE)
);
-- 全局非分区索引
SQL> CREATE INDEX idx_sales_global_np ON sales_range(sale_id);
14.5.4 分区维护操作
sql
-- 添加新分区
SQL> ALTER TABLE sales_range ADD PARTITION
p_2024_q1 VALUES LESS THAN (TO_DATE('2024-04-01','YYYY-MM-DD'));
-- 删除分区
SQL> ALTER TABLE sales_range DROP PARTITION p_2023_q1;
-- TRUNCATE分区
SQL> ALTER TABLE sales_range TRUNCATE PARTITION p_2023_q2;
-- 合并分区
SQL> ALTER TABLE sales_range MERGE PARTITIONS p_2023_q1, p_2023_q2 INTO PARTITION p_2023_h1;
-- 拆分分区
SQL> ALTER TABLE sales_range SPLIT PARTITION p_future AT
(TO_DATE('2024-04-01','YYYY-MM-DD'))
INTO (PARTITION p_2024_q1, PARTITION p_future);
-- 移动分区到其他表空间
SQL> ALTER TABLE sales_range MOVE PARTITION p_2023_q1 TABLESPACE ts_archive;
-- 交换分区(将分区转换为独立表)
SQL> ALTER TABLE sales_range EXCHANGE PARTITION p_2023_q1
WITH TABLE sales_2023_q1 INCLUDING INDEXES;
14.5.5 分区裁剪验证
sql
-- 查看执行计划的分区信息
SQL> EXPLAIN PLAN FOR
SELECT * FROM sales_range WHERE sale_date BETWEEN TO_DATE('2023-03-01','YYYY-MM-DD')
AND TO_DATE('2023-03-31','YYYY-MM-DD');
SQL> SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 输出中查看:
-- Partition access: RANGE: 1-2 (表示只扫描了第1和第2个分区)
14.5.6 分区表最佳实践
- 分区键选择:选择查询中频繁使用的过滤条件列
- 分区数量:每个分区数据量建议在2-10GB之间
- 分区索引:OLTP环境使用全局索引,DSS环境使用本地索引
- 定期维护:定期添加新分区,清理历史分区
- 监控分区裁剪:验证执行计划是否实现了分区裁剪
14.6 并行执行优化
14.6.1 并行执行概述
并行执行是Oracle将单个SQL操作分解为多个子任务,由多个进程并行处理的技术。适用于数据仓库、报表查询、批量加载等场景。
14.6.2 并行度配置
sql
-- 设置表级并行度
SQL> ALTER TABLE scott.emp PARALLEL 4;
-- 设置索引级并行度
SQL> ALTER INDEX idx_emp_deptno PARALLEL 2;
-- 设置会话级并行度
SQL> ALTER SESSION SET parallel_degree_policy = AUTO;
SQL> ALTER SESSION SET parallel_degree_limit = 8;
-- 设置系统级并行度参数
SQL> ALTER SYSTEM SET parallel_max_servers = 64 SCOPE=BOTH;
SQL> ALTER SYSTEM SET parallel_min_servers = 8 SCOPE=BOTH;
SQL> ALTER SYSTEM SET parallel_degree_policy = AUTO SCOPE=BOTH;
-- 查看并行度设置
SQL> SELECT name, value FROM v$parameter
WHERE name LIKE 'parallel%';
14.6.3 并行执行Hint
sql
-- 强制并行查询
SELECT /*+ PARALLEL(emp, 4) */ * FROM scott.emp;
-- 自动并行度
SELECT /*+ PARALLEL(emp, DEFAULT) */ * FROM scott.emp;
-- 并行DML
INSERT /*+ PARALLEL(emp, 4) */ INTO scott.emp SELECT * FROM scott.emp_temp;
-- 并行DDL
CREATE INDEX /*+ PARALLEL(4) */ idx_emp_sal ON scott.emp(sal);
-- 并行CTAS
CREATE TABLE /*+ PARALLEL(4) */ emp_copy AS SELECT * FROM emp;
14.6.4 并行执行监控
sql
-- 查看并行执行进程
SQL> SELECT slave_name, status, sessions, sid, serial#
FROM v$pq_slave;
-- 查看当前并行查询
SQL> SELECT qcsid, qcserial#, sid, serial#, degree, req_degree
FROM v$px_session
ORDER BY qcsid;
-- 查看并行执行统计
SQL> SELECT sql_id, degree, server_set, num_servers, num_queries
FROM v$pq_sysstat;
-- 查看并行SQL的执行计划
SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor(
format => 'PARALLEL'));
14.6.5 并行执行最佳实践
- OLTP系统慎用并行:OLTP系统使用并行可能导致资源争用
- 并行度设置:建议设置为CPU核心数的1-2倍
- I/O子系统:确保I/O子系统能支撑并行负载
- 数据仓库场景:全表扫描、排序、聚合操作适合并行
- 控制并行度 :使用
parallel_degree_limit限制最大并行度
14.7 物化视图优化
14.7.1 物化视图概述
物化视图(Materialized View)是预先计算并存储查询结果的数据对象,适用于数据仓库中的报表查询、汇总统计等场景。物化视图可以显著提高查询性能,但需要消耗存储空间和维护成本。
14.7.2 创建物化视图
sql
-- 创建物化视图(手动刷新)
CREATE MATERIALIZED VIEW mv_dept_sales
BUILD IMMEDIATE
REFRESH COMPLETE ON DEMAND
ENABLE QUERY REWRITE
AS
SELECT d.dname, SUM(s.amount) as total_sales,
COUNT(*) as order_count,
AVG(s.amount) as avg_amount
FROM dept d, sales s
WHERE d.deptno = s.deptno
GROUP BY d.dname;
-- 创建物化视图(快速刷新)
CREATE MATERIALIZED VIEW mv_emp_summary
BUILD IMMEDIATE
REFRESH FAST ON COMMIT
ENABLE QUERY REWRITE
AS
SELECT deptno, COUNT(*) as emp_count, SUM(sal) as total_sal
FROM emp
GROUP BY deptno;
-- 创建物化视图日志(支持快速刷新)
CREATE MATERIALIZED VIEW LOG ON emp
WITH ROWID, SEQUENCE (deptno, sal)
INCLUDING NEW VALUES;
CREATE MATERIALIZED VIEW LOG ON dept
WITH ROWID, SEQUENCE (dname)
INCLUDING NEW VALUES;
14.7.3 物化视图刷新方式
| 刷新方式 | 说明 | 适用场景 |
|---|---|---|
| COMPLETE | 完全刷新,删除全部数据重新插入 | 数据量小或不允许增量刷新 |
| FAST | 快速刷新,仅应用变更 | 有物化视图日志,变更量小 |
| FORCE | 优先快速刷新,失败则完全刷新 | 默认方式 |
| ON COMMIT | 基表提交时自动刷新 | 实时性要求高 |
| ON DEMAND | 手动刷新 | 对实时性要求不高的场景 |
| ON STATEMENT | 语句级自动刷新 | 实时性要求极高的场景 |
14.7.4 物化视图管理
sql
-- 手动完全刷新
SQL> EXEC dbms_mview.refresh('mv_dept_sales', 'C');
-- 手动快速刷新
SQL> EXEC dbms_mview.refresh('mv_emp_summary', 'F');
-- 批量刷新多个物化视图
SQL> EXEC dbms_mview.refresh('mv1, mv2, mv3', 'C');
-- 使用DBMS_SCHEDULER定时刷新
BEGIN
dbms_scheduler.create_job(
job_name => 'refresh_mv_job',
job_type => 'PLSQL_BLOCK',
job_action => 'begin dbms_mview.refresh(''mv_dept_sales'', ''C''); end;',
start_date => systimestamp,
repeat_interval => 'freq=daily; byhour=2; byminute=0',
enabled => true);
END;
/
-- 查看物化视图状态
SQL> SELECT mview_name, refresh_mode, refresh_method, last_refresh_type,
last_refresh_date, staleness
FROM dba_mviews
WHERE owner = 'SCOTT';
-- 查看物化视图是否可用
SQL> SELECT mview_name, compile_state
FROM dba_mviews
WHERE owner = 'SCOTT' AND compile_state != 'VALID';
14.7.5 查询重写(Query Rewrite)
sql
-- 启用查询重写(会话级)
SQL> ALTER SESSION SET query_rewrite_enabled = TRUE;
-- 启用查询重写(系统级)
SQL> ALTER SYSTEM SET query_rewrite_enabled = TRUE SCOPE=BOTH;
-- 查看物化视图是否用于查询重写
SQL> SELECT owner, name, rewrite_enabled
FROM dba_mviews
WHERE owner = 'SCOTT';
-- 验证查询重写是否生效(查看执行计划)
SQL> EXPLAIN PLAN FOR
SELECT d.dname, SUM(s.amount) FROM dept d, sales s
WHERE d.deptno = s.deptno
GROUP BY d.dname;
SQL> SELECT * FROM TABLE(dbms_xplan.display);
-- 如果执行计划中显示"MATERIALIZED VIEW"或"TABLE ACCESS FULL MV_DEPT_SALES"
-- 说明查询重写已生效
14.7.6 物化视图最佳实践
- 选择合适的刷新策略:根据实时性要求选择ON COMMIT或ON DEMAND
- 查询重写:启用查询重写可以让优化器自动选择使用物化视图
- 监控刷新时间:确保物化视图在维护窗口内能完成刷新
- 索引物化视图:为物化视图的查询列创建索引,提高查询性能
- 分区物化视图:大表物化视图建议使用分区,便于维护
第七部分:安全管理
第15章 数据库安全
数据库安全是DBA的重要职责,涵盖用户认证、权限管理、审计监控、数据加密等多个层面。本章将系统介绍Oracle数据库的安全防护体系,帮助DBA构建全方位的安全防护方案。
15.1 安全策略与最佳实践
15.1.1 安全策略概述
数据库安全策略应覆盖以下三个层面:
| 安全层面 | 说明 | 关键技术 |
|---|---|---|
| 访问控制 | 控制谁能访问数据库 | 认证、授权、密码策略 |
| 数据保护 | 保护数据在存储和传输中的安全 | 加密、脱敏、安全标记 |
| 审计监控 | 记录和监控所有操作 | 审计、告警、日志分析 |
15.1.2 安全最佳实践清单
系统配置安全:
sql
-- 1. 关闭不必要的数据库功能
SQL> alter system set _enable_secure_view_merge = true scope=both;
SQL> alter system set enable_ddl_logging = true scope=both;
-- 2. 设置安全参数
SQL> alter system set sec_case_sensitive_logon = true scope=both;
SQL> alter system set sec_max_failed_login_attempts = 5 scope=both;
SQL> alter system set sec_protocol_error_further_action = 'DROP' scope=both;
SQL> alter system set sec_protocol_error_trace_action = 'LOG' scope=both;
-- 3. 限制远程登录权限
SQL> alter system set remote_login_passwordfile = 'EXCLUSIVE' scope=spfile;
-- 4. 启用密码验证函数
SQL> alter profile default limit
password_life_time 90
password_grace_time 7
password_reuse_time 365
password_reuse_max 5
password_lock_time 1
failed_login_attempts 5;
日常安全操作:
- 定期检查默认密码是否已修改
- 移除不再需要的用户和角色
- 限制公共角色(PUBLIC)的权限
- 定期审查用户权限
- 启用统一审计
- 配置密码策略并强制执行
- 监控失败登录尝试
- 定期备份安全配置
15.1.3 安全配置基线
sql
-- 创建安全基线检查脚本
SQL> select name, value, isdefault
from v$parameter
where name in ('sec_case_sensitive_logon',
'sec_max_failed_login_attempts',
'audit_trail',
'password_encryption',
'remote_login_passwordfile',
'os_authent_prefix',
'sql92_security',
'utl_file_dir',
'db_domain')
order by name;
15.2 用户认证机制
15.2.1 认证方式
Oracle支持多种用户认证方式,根据安全级别选择合适的方式:
| 认证方式 | 说明 | 适用场景 |
|---|---|---|
| 密码认证 | 用户名+密码,最常用 | 大多数场景 |
| 操作系统认证 | 通过操作系统用户认证 | 本地管理 |
| 外部认证 | 使用外部服务(如LDAP、Kerberos) | 企业统一认证 |
| 全局认证 | 使用Oracle Internet Directory | 大规模企业环境 |
| 多因子认证 | 密码+其他因素(如Token) | 高安全环境 |
15.2.2 密码策略管理
sql
-- 创建密码验证函数
CREATE OR REPLACE FUNCTION verify_password(
username VARCHAR2,
password VARCHAR2,
old_password VARCHAR2
) RETURN BOOLEAN AS
BEGIN
-- 密码长度至少8位
IF LENGTH(password) < 8 THEN
raise_application_error(-20001, '密码长度至少8位');
END IF;
-- 必须包含数字
IF NOT REGEXP_LIKE(password, '[0-9]') THEN
raise_application_error(-20002, '密码必须包含数字');
END IF;
-- 必须包含字母
IF NOT REGEXP_LIKE(password, '[a-zA-Z]') THEN
raise_application_error(-20003, '密码必须包含字母');
END IF;
-- 不能包含用户名
IF INSTR(LOWER(password), LOWER(username)) > 0 THEN
raise_application_error(-20004, '密码不能包含用户名');
END IF;
RETURN TRUE;
END;
/
-- 应用密码验证函数
SQL> alter profile default limit
password_verify_function verify_password;
-- 创建自定义Profile
CREATE PROFILE app_user_profile LIMIT
sessions_per_user 5
idle_time 30
connect_time 480
failed_login_attempts 5
password_life_time 90
password_reuse_time 365
password_reuse_max 3
password_lock_time 1
password_grace_time 7
password_verify_function verify_password;
15.2.3 用户创建与管理
sql
-- 创建用户时指定Profile
CREATE USER app_user IDENTIFIED BY "Str0ng!Passw0rd"
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
PROFILE app_user_profile
ACCOUNT UNLOCK
PASSWORD EXPIRE;
-- 修改用户密码
SQL> alter user app_user identified by "NewStr0ng!Pass";
-- 锁定用户
SQL> alter user app_user account lock;
-- 解锁用户
SQL> alter user app_user account unlock;
-- 过期密码
SQL> alter user app_user password expire;
-- 查看用户信息
SQL> select username, account_status, lock_date, expiry_date,
profile, authentication_type
from dba_users
where username = 'APP_USER';
15.2.4 操作系统认证
sql
-- 查看操作系统认证前缀
SQL> show parameter os_authent_prefix
-- 默认值:OPS$
-- 创建操作系统认证用户
SQL> create user ops$oracle identified externally;
-- 授权
SQL> grant connect, resource to ops$oracle;
-- 使用操作系统认证登录
$ sqlplus /
15.2.5 密码文件管理
sql
-- 创建密码文件
$ orapwd file=$ORACLE_HOME/dbs/orapwORCL
password=Admin123 entries=10 force=y
-- 查看密码文件用户
SQL> select * from v$pwfile_users;
-- 添加sysdba用户
SQL> grant sysdba to backup_admin;
-- 移除sysdba权限
SQL> revoke sysdba from backup_admin;
15.3 权限最小化原则
15.3.1 权限管理概述
权限最小化原则是指只授予用户完成工作所需的最小权限。Oracle的权限分为系统权限和对象权限两类。
权限分类:
| 权限类型 | 说明 | 示例 |
|---|---|---|
| 系统权限 | 执行特定数据库操作的权限 | CREATE TABLE, ALTER DATABASE |
| 对象权限 | 操作特定数据库对象的权限 | SELECT ON scott.emp, INSERT ON hr.employees |
| 角色权限 | 通过角色授予的权限集合 | DBA, CONNECT, RESOURCE |
15.3.2 权限分配原则
sql
-- 原则1:不直接将系统权限授予用户,通过角色管理
CREATE ROLE app_read_role;
CREATE ROLE app_write_role;
CREATE ROLE app_admin_role;
-- 原则2:授予最小权限
GRANT CREATE SESSION TO app_read_role;
GRANT SELECT ANY TABLE TO app_read_role;
GRANT INSERT, UPDATE, DELETE ON app_schema.orders TO app_write_role;
-- 原则3:将角色授予用户
GRANT app_read_role TO app_user1;
GRANT app_write_role TO app_user2;
-- 原则4:启用角色时限制
ALTER USER app_user1 DEFAULT ROLE app_read_role;
ALTER USER app_user2 DEFAULT ROLE app_write_role;
15.3.3 权限审计与审查
sql
-- 查看用户拥有的系统权限
SQL> select grantee, privilege, admin_option
from dba_sys_privs
where grantee = 'APP_USER'
order by privilege;
-- 查看用户拥有的对象权限
SQL> select grantee, owner, table_name, privilege, grantable
from dba_tab_privs
where grantee = 'APP_USER'
order by owner, table_name;
-- 查看用户拥有的角色
SQL> select grantee, granted_role, admin_option, default_role
from dba_role_privs
where grantee = 'APP_USER';
-- 查看DBA权限用户
SQL> select grantee, privilege
from dba_sys_privs
where privilege like '%ANY%'
or grantee in (select role from dba_roles)
order by grantee;
-- 查看拥有DBA角色的用户
SQL> select grantee from dba_role_privs
where granted_role = 'DBA';
15.3.4 权限回收
sql
-- 回收系统权限
SQL> revoke create table from app_user;
-- 回收对象权限
SQL> revoke select on scott.emp from app_user;
-- 回收角色
SQL> revoke app_read_role from app_user;
-- 回收所有权限(删除用户前)
SQL> revoke all privileges from app_user;
SQL> revoke all roles from app_user;
15.3.5 最小权限检查清单
- 是否每个用户都有自己独立的账号?
- 是否使用角色管理权限,而非直接授予?
- 是否定期审查DBA权限用户?
- PUBLIC角色是否只被授予了最小权限?
- 应用程序用户是否只拥有必要的对象权限?
- 是否移除了默认的示范用户(SCOTT等)?
- 是否限制了任何权限(WITH ADMIN OPTION)的传播?
15.4 数据库审计
15.4.1 审计概述
数据库审计是记录数据库操作的重要安全机制。通过审计,DBA可以追踪用户的操作行为,发现安全问题,满足合规要求。
审计类型:
| 审计类型 | 说明 | 适用场景 |
|---|---|---|
| 语句审计 | 审计特定类型的SQL语句 | 审计DDL操作 |
| 权限审计 | 审计特定系统权限的使用 | 审计DBA操作 |
| 对象审计 | 审计对特定对象的操作 | 审计敏感表访问 |
| 细粒度审计(FGA) | 基于内容的审计 | 审计特定数据访问 |
15.4.2 传统审计配置
sql
-- 启用审计(重启数据库生效)
SQL> alter system set audit_trail = DB,EXTENDED scope=spfile;
-- 重启数据库后生效
SQL> shutdown immediate;
SQL> startup;
-- 审计登录事件
SQL> audit session;
-- 审计DDL操作
SQL> audit create any table, drop any table, alter any table by access;
-- 审计DML操作
SQL> audit select table, insert table, update table, delete table by access;
-- 审计特定用户
SQL> audit all on hr.employees by app_user;
-- 审计系统权限使用
SQL> audit alter system, create user, drop user, alter user by access;
-- 审计特定表
SQL> audit select on hr.salary by access;
-- 查看审计设置
SQL> select * from dba_audit_policies;
SQL> select * from dba_audit_trail;
15.4.3 细粒度审计(FGA)
细粒度审计允许基于数据内容进行审计,例如只审计访问特定列或特定值的操作。
sql
-- 创建FGA策略:审计访问salary列的所有操作
BEGIN
dbms_fga.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'audit_salary_access',
audit_column => 'SALARY',
enable => true,
statement_types => 'SELECT, UPDATE');
END;
/
-- 创建FGA策略:审计访问高薪员工数据
BEGIN
dbms_fga.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'audit_high_salary',
audit_condition => 'SALARY > 100000',
enable => true,
statement_types => 'SELECT');
END;
/
-- 查看FGA审计记录
SQL> select timestamp, db_user, object_schema, object_name,
sql_text, policy_name
from dba_fga_audit_trail
order by timestamp desc;
-- 禁用FGA策略
SQL> exec dbms_fga.disable_policy('HR', 'EMPLOYEES', 'audit_salary_access');
-- 启用FGA策略
SQL> exec dbms_fga.enable_policy('HR', 'EMPLOYEES', 'audit_salary_access');
-- 删除FGA策略
SQL> exec dbms_fga.drop_policy('HR', 'EMPLOYEES', 'audit_salary_access');
15.4.4 审计记录管理
sql
-- 查看审计记录空间使用
SQL> select segment_name, bytes/1024/1024 as size_mb
from dba_segments
where segment_name like 'AUD$%' or segment_name like 'FGA_LOG$%';
-- 清理审计记录(删除30天前的记录)
SQL> delete from sys.aud$ where timestamp# < sysdate - 30;
-- 清理FGA审计记录
SQL> delete from sys.fga_log$ where ltimestamp# < sysdate - 30;
-- 使用DBMS_AUDIT_MGMT清理审计记录
BEGIN
dbms_audit_mgmt.clean_audit_trail(
audit_trail_type => dbms_audit_mgmt.audit_trail_aud_std,
use_last_arch_timestamp => false);
END;
/
-- 设置审计记录保留策略(保留30天)
BEGIN
dbms_audit_mgmt.set_last_archive_timestamp(
audit_trail_type => dbms_audit_mgmt.audit_trail_aud_std,
last_archive_time => systimestamp - 30);
END;
/
15.4.5 审计最佳实践
- 选择性审计:只审计高风险操作,避免过度审计
- 审计日志保护:将审计日志存储在安全的表空间中,定期归档
- 监控审计空间:确保审计表空间不会满导致数据库挂起
- 定期审查:定期审查审计记录,识别异常行为
- 合规要求:根据行业合规要求(如SOX、PCI DSS)配置审计策略
15.5 统一审计(Unified Auditing)
15.5.1 统一审计概述
统一审计(Unified Auditing)是Oracle 12c引入的下一代审计框架,将所有审计记录统一存储在AUDSYS schema的只读审计表中,提供更高效、更安全的审计能力。
统一审计的优势:
- 统一存储所有审计记录,无需分离传统审计和FGA
- 审计记录存储在只读表中,防止篡改
- 支持在数据库关闭时记录审计(如启动审计)
- 更高的性能和更小的存储开销
- 支持细粒度的审计策略配置
15.5.2 启用统一审计
bash
# 检查当前是否已启用统一审计
SQL> select value from v$option where parameter = 'Unified Auditing';
-- 如果返回FALSE,需要重新链接Oracle二进制文件
# 启用统一审计(需要停止数据库实例)
$ cd $ORACLE_HOME/rdbms/lib
$ make -f ins_rdbms.mk uniaud_on
$ echo $ORACLE_SID
# 重启数据库
SQL> shutdown immediate
SQL> startup
# 确认启用
SQL> select value from v$option where parameter = 'Unified Auditing';
-- 应返回TRUE
15.5.3 统一审计策略
sql
-- 创建统一审计策略:审计所有DBA操作
CREATE AUDIT POLICY dba_audit_policy
PRIVILEGES ALTER ANY TABLE, CREATE USER, DROP USER,
ALTER USER, ALTER DATABASE, ALTER SYSTEM,
DROP ANY TABLE, DROP ANY TABLESPACE
CONTAINER = ALL;
-- 启用审计策略
AUDIT POLICY dba_audit_policy;
-- 创建审计策略:审计对敏感表的操作
CREATE AUDIT POLICY sensitive_table_policy
ACTIONS SELECT ON hr.employees,
UPDATE ON hr.employees,
SELECT ON hr.salary,
UPDATE ON hr.salary
CONTAINER = CURRENT;
AUDIT POLICY sensitive_table_policy;
-- 创建审计策略:审计特定用户的操作
CREATE AUDIT POLICY user_audit_policy
ACTIONS ALL ON SCHEMA
WHEN 'SYS_CONTEXT(''USERENV'',''SESSION_USER'') = ''APP_ADMIN'''
EVALUATE PER SESSION;
AUDIT POLICY user_audit_policy;
15.5.4 统一审计策略管理
sql
-- 查看审计策略
SQL> select policy_name, audit_option_type, audited_role, condition_eval_opt
from audit_unified_policies
order by policy_name;
-- 查看已启用的审计策略
SQL> select policy_name, enabled_option, entity_name
from audit_unified_enabled_policies
order by policy_name;
-- 修改审计策略
CREATE OR REPLACE AUDIT POLICY dba_audit_policy
PRIVILEGES ALTER ANY TABLE, CREATE USER, DROP USER,
ALTER USER, ALTER DATABASE, ALTER SYSTEM,
DROP ANY TABLE, DROP ANY TABLESPACE,
GRANT ANY PRIVILEGE
CONTAINER = ALL;
-- 禁用审计策略
NOAUDIT POLICY dba_audit_policy;
-- 删除审计策略
DROP AUDIT POLICY dba_audit_policy;
15.5.5 统一审计记录查询
sql
-- 查看审计记录
SQL> select event_timestamp, dbusername, action_name, object_schema,
object_name, sql_text, unified_audit_policies
from unified_audit_trail
order by event_timestamp desc
fetch first 100 rows only;
-- 查看特定用户的审计记录
SQL> select event_timestamp, action_name, object_name, sql_text
from unified_audit_trail
where dbusername = 'APP_USER'
order by event_timestamp desc;
-- 查看失败的登录尝试
SQL> select event_timestamp, dbusername, os_username,
userhost, return_code
from unified_audit_trail
where action_name = 'LOGON'
and return_code != 0
order by event_timestamp desc;
-- 查看DDL操作审计
SQL> select event_timestamp, dbusername, action_name,
object_schema, object_name, sql_text
from unified_audit_trail
where action_name in ('CREATE TABLE', 'DROP TABLE', 'ALTER TABLE',
'CREATE USER', 'DROP USER', 'ALTER USER')
order by event_timestamp desc;
15.5.6 统一审计维护
sql
-- 归档审计记录
BEGIN
dbms_audit_mgmt.set_last_archive_timestamp(
audit_trail_type => dbms_audit_mgmt.audit_trail_unified,
last_archive_time => systimestamp - 30);
END;
/
-- 清理审计记录
BEGIN
dbms_audit_mgmt.clean_audit_trail(
audit_trail_type => dbms_audit_mgmt.audit_trail_unified,
use_last_arch_timestamp => true);
END;
/
-- 查看审计记录空间使用
SQL> select segment_name, bytes/1024/1024 as size_mb
from dba_segments
where segment_name like 'AUDSYS%' or segment_name like 'CLI_SWP%';
-- 设置审计记录保留天数
BEGIN
dbms_audit_mgmt.update_cleanup(
audit_trail_type => dbms_audit_mgmt.audit_trail_unified,
audit_trail_property => dbms_audit_mgmt.audit_trail_cleanup_interval,
audit_trail_property_value => 168); -- 每小时清理一次
END;
/
15.6 透明数据加密(TDE)
15.6.1 TDE概述
透明数据加密(Transparent Data Encryption, TDE)是Oracle企业版提供的数据加密功能,能够自动对存储到磁盘的数据进行加密,并在数据被读取时自动解密,对应用程序完全透明。
TDE的加密层级:
┌──────────────────────────────────────────────┐
│ 应用程序层 │
│ (无需修改,透明访问加密数据) │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ TDE 表空间加密 │
│ 对整个表空间的所有数据进行加密 │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ TDE 列加密 │
│ 对指定列的数据进行加密 │
└──────────────────────┬───────────────────────┘
│
┌──────────────────────▼───────────────────────┐
│ 加密密钥管理 │
│ 主加密密钥 → 表空间/列加密密钥 │
└──────────────────────────────────────────────┘
15.6.2 配置TDE表空间加密
sql
-- 步骤1:创建Wallet目录
-- 在操作系统创建目录
$ mkdir -p $ORACLE_BASE/admin/$ORACLE_SID/wallet
-- 步骤2:配置Wallet位置
-- 在sqlnet.ora中添加
-- ENCRYPTION_WALLET_LOCATION = (SOURCE = (METHOD = FILE)(METHOD_DATA = (DIRECTORY = /u01/app/oracle/admin/orcl/wallet)))
-- 步骤3:创建并打开主密钥
SQL> administer key management create keystore
'/u01/app/oracle/admin/orcl/wallet'
identified by "WalletPass123";
SQL> administer key management set keystore open
identified by "WalletPass123";
-- 设置自动登录Wallet(避免重启后需要手动打开)
SQL> administer key management create auto_login keystore
from keystore '/u01/app/oracle/admin/orcl/wallet'
identified by "WalletPass123";
-- 步骤4:创建主加密密钥
SQL> administer key management set key
identified by "WalletPass123" with backup;
-- 步骤5:创建加密表空间
SQL> create tablespace tde_data
datafile '/u01/app/oracle/oradata/orcl/tde_data01.dbf'
size 1g
encryption using 'AES256'
default storage (encrypt);
-- 步骤6:创建加密表(放在加密表空间中)
SQL> create table hr.employee_sensitive
tablespace tde_data
as select * from hr.employees;
15.6.3 配置TDE列加密
sql
-- 列加密(对单列数据加密)
SQL> create table hr.employee_pii (
emp_id number primary key,
name varchar2(100),
ssn varchar2(11) encrypt using 'AES128',
salary number(10,2) encrypt using 'AES256',
hire_date date
);
-- 查看加密列
SQL> select owner, table_name, column_name, encryption_alg
from dba_encrypted_columns
where owner = 'HR';
-- 在现有表上添加列加密
SQL> alter table hr.employees modify (salary encrypt);
15.6.4 TDE管理操作
sql
-- 重新打开Wallet
SQL> administer key management set keystore open
identified by "WalletPass123";
-- 关闭Wallet
SQL> administer key management set keystore close
identified by "WalletPass123";
-- 轮换主加密密钥
SQL> administer key management set key
identified by "WalletPass123" with backup;
-- 查看密钥信息
SQL> select key_id, creation_time, keystore_type
from v$encryption_keys
order by creation_time desc;
-- 查看加密表空间
SQL> select tablespace_name, encryptionalg
from dba_tablespaces
where encryptionalg is not null;
-- 查看加密状态
SQL> select name, value, description
from v$parameter
where name like '%wallet%' or name like '%encrypt%';
15.6.5 TDE性能影响
| 加密类型 | 性能影响 | 说明 |
|---|---|---|
| 表空间加密(AES128) | 3-5% | 对整个表空间加密,影响最小 |
| 表空间加密(AES256) | 5-10% | 更高级别加密,略有性能开销 |
| 列加密 | 10-20% | 对单列加密,索引无法使用 |
| 大量数据批量加载 | 10-15% | 加密操作需要额外CPU |
15.7 数据库防火墙
15.7.1 数据库防火墙概述
Oracle数据库防火墙(Database Firewall)是一种网络安全设备或软件,用于监控和拦截对数据库的访问请求,防止SQL注入、未授权访问等安全威胁。
数据库防火墙的主要功能:
- SQL注入防护:检测并阻止SQL注入攻击
- 访问控制:基于源IP、用户、SQL语句等条件控制访问
- 异常检测:识别异常SQL模式和访问行为
- 合规审计:记录所有数据库操作,满足合规要求
15.7.2 数据库配置级安全加固
sql
-- 1. 限制IP地址访问
-- 使用sqlnet.ora配置
-- tcp.validnode_checking = yes
-- tcp.invited_nodes = (192.168.1.0/24, 10.0.0.0/8)
-- tcp.excluded_nodes = (192.168.2.100)
-- 2. 限制数据库连接
SQL> alter system set logon_timeout = 60 scope=both;
SQL> alter system set idle_time = 15 scope=both;
-- 3. 使用数据库触发器限制登录
CREATE OR REPLACE TRIGGER restrict_login_trigger
AFTER LOGON ON DATABASE
BEGIN
IF SYS_CONTEXT('USERENV', 'IP_ADDRESS') NOT IN
('192.168.1.100', '192.168.1.101') THEN
raise_application_error(-20001, '来源IP地址未被授权');
END IF;
END;
/
-- 4. 限制特定时间的登录
CREATE OR REPLACE TRIGGER limit_login_time
AFTER LOGON ON DATABASE
BEGIN
IF TO_CHAR(SYSDATE, 'HH24') NOT BETWEEN 6 AND 22 THEN
raise_application_error(-20002, '当前时间不允许登录');
END IF;
END;
/
15.7.3 SQL注入防护
sql
-- 1. 使用绑定变量(最有效的SQL注入防护)
-- 不安全的方式
SQL> select * from users where username = 'admin' OR '1'='1';
-- 安全的方式
SQL> select * from users where username = :username;
-- 2. 使用DBMS_ASSERT验证输入
CREATE OR REPLACE FUNCTION safe_query(p_username VARCHAR2) RETURN SYS_REFCURSOR AS
v_cursor SYS_REFCURSOR;
v_safe_username VARCHAR2(100);
BEGIN
v_safe_username := DBMS_ASSERT.ENQUOTE_NAME(
DBMS_ASSERT.SIMPLE_SQL_NAME(p_username), false);
OPEN v_cursor FOR 'SELECT * FROM users WHERE username = :1'
USING p_username;
RETURN v_cursor;
END;
/
-- 3. 使用PL/SQL代码执行(避免动态SQL)
CREATE OR REPLACE PROCEDURE get_user_info(p_user_id NUMBER) AS
v_name VARCHAR2(100);
BEGIN
-- 安全:使用静态SQL
SELECT name INTO v_name FROM users WHERE user_id = p_user_id;
DBMS_OUTPUT.PUT_LINE(v_name);
END;
/
15.7.4 连接级安全
sql
-- 配置sqlnet.ora安全参数
-- SQLNET.ALLOWED_LOGON_VERSION_SERVER = 12 (限制最低客户端版本)
-- SQLNET.ALLOWED_LOGON_VERSION_CLIENT = 12
-- SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER = (SHA512, SHA384, SHA256)
-- SQLNET.CRYPTO_CHECKSUM_SERVER = REQUIRED
-- SQLNET.ENCRYPTION_TYPES_SERVER = (AES256, AES192, AES128)
-- SQLNET.ENCRYPTION_SERVER = REQUIRED
-- SQLNET.IGNORE_ANO_ENCRYPTION_FOR_TCPS = TRUE
-- 验证网络加密配置
SQL> select network_service_banner
from v$session_connect_info
where sid = (select sid from v$mystat where rownum = 1);
15.8 数据脱敏
15.8.1 数据脱敏概述
数据脱敏(Data Masking)是对敏感数据进行转换,使其在非生产环境中保持可用性但无法识别真实身份的技术。Oracle提供了Data Masking Pack和多种脱敏方法。
脱敏类型:
| 脱敏类型 | 说明 | 示例 |
|---|---|---|
| 静态脱敏 | 在非生产环境中永久脱敏 | 测试数据库脱敏 |
| 动态脱敏 | 实时脱敏,原始数据不变 | 生产环境查询脱敏 |
| 确定性脱敏 | 相同的输入产生相同的输出 | 一致性脱敏 |
15.8.2 使用SQL函数脱敏
sql
-- 示例1:手机号脱敏(保留前3后4位)
CREATE OR REPLACE FUNCTION mask_phone(p_phone VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN REGEXP_REPLACE(p_phone,
'(\d{3})\d{4}(\d{4})', '\1****\2');
END;
/
-- 测试
SQL> select mask_phone('13812345678') from dual;
-- 输出:138****5678
-- 示例2:身份证号脱敏
CREATE OR REPLACE FUNCTION mask_id_card(p_id VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN REGEXP_REPLACE(p_id,
'(\d{6})\d{8}(\d{4})', '\1********\2');
END;
/
-- 示例3:电子邮件脱敏
CREATE OR REPLACE FUNCTION mask_email(p_email VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN REGEXP_REPLACE(p_email,
'(.{1,3}).*@', '\1****@');
END;
/
-- 测试
SQL> select mask_email('zhangsan@example.com') from dual;
-- 输出:zha****@example.com
15.8.3 使用Oracle Data Redaction(动态脱敏)
Oracle Data Redaction是Oracle企业版安全特性,提供动态数据脱敏能力,无需修改原始数据。
sql
-- 创建Redaction策略:对salary列执行完全脱敏
BEGIN
dbms_redact.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'redact_salary',
policy_description => 'Redact salary column',
column_name => 'SALARY',
function_type => dbms_redact.full,
expression => '1=1');
END;
/
-- 创建Redaction策略:对非管理员用户部分脱敏
BEGIN
dbms_redact.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'redact_phone',
policy_description => 'Partial redact phone',
column_name => 'PHONE_NUMBER',
function_type => dbms_redact.partial,
function_parameters => 'VVVFVVVVVVVV',
regexp_pattern => '(\d{3})\d{4}(\d{4})',
regexp_replace_string => '\1****\2',
expression => 'sys_context(''USERENV'',''SESSION_USER'') != ''HR_ADMIN''');
END;
/
-- 查看Redaction策略
SQL> select object_owner, object_name, policy_name, column_name,
function_type_desc, expression
from redaction_policies
order by object_owner, object_name;
-- 禁用Redaction策略
SQL> exec dbms_redact.disable_policy('HR', 'EMPLOYEES', 'redact_salary');
-- 启用Redaction策略
SQL> exec dbms_redact.enable_policy('HR', 'EMPLOYEES', 'redact_salary');
-- 删除Redaction策略
SQL> exec dbms_redact.drop_policy('HR', 'EMPLOYEES', 'redact_salary');
15.8.4 使用视图脱敏
sql
-- 创建脱敏视图(替代直接访问原始表)
CREATE OR REPLACE VIEW hr.employees_masked AS
SELECT employee_id,
first_name,
last_name,
email,
mask_phone(phone_number) as phone_number,
hire_date,
job_id,
CASE WHEN sys_context('USERENV','SESSION_USER') = 'HR_ADMIN'
THEN salary
ELSE round(salary, -3)
END as salary,
commission_pct,
manager_id,
department_id
FROM hr.employees;
-- 授权用户访问脱敏视图
GRANT SELECT ON hr.employees_masked TO app_user;
15.8.5 数据脱敏最佳实践
- 识别敏感数据:扫描数据库识别PII(个人身份信息)、财务数据等敏感列
- 分级脱敏:根据用户角色实施不同级别的脱敏策略
- 保持一致性:对同一数据在不同表中使用一致的脱敏方法
- 测试验证:脱敏后验证数据格式和业务逻辑的正确性
- 合规要求:满足GDPR、PCI DSS、等保等合规要求
第八部分:运维管理
运维管理是DBA的日常工作核心,涵盖数据库巡检、监控告警、补丁升级、故障处理等多个方面。本部分将系统介绍数据库运维管理的完整流程,帮助DBA建立规范化的运维体系。
第16章 日常运维与监控
16.1 数据库巡检清单
16.1.1 巡检概述
数据库巡检是DBA日常工作的核心内容,通过定期巡检可以及时发现潜在问题,预防故障发生。巡检应覆盖数据库的各个层面,包括可用性、性能、空间、安全等。
16.1.2 巡检清单
每日巡检(耗时约15分钟):
sql
-- 1. 检查数据库是否正常运行
SQL> select name, open_mode, database_role, log_mode from v$database;
-- 2. 检查实例状态
SQL> select instance_name, status, host_name, version from v$instance;
-- 3. 检查监听器状态
$ lsnrctl status LISTENER
-- 4. 检查表空间使用率
SQL> select tablespace_name,
round(total_space / 1024 / 1024, 2) as total_mb,
round(free_space / 1024 / 1024, 2) as free_mb,
round((1 - free_space / total_space) * 100, 2) as used_pct
from (select tablespace_name,
sum(bytes) as total_space,
sum(decode(maxbytes, 0, bytes, maxbytes)) as max_space,
sum(decode(autoextensible, 'YES', 0, bytes)) as free_space
from dba_data_files
group by tablespace_name)
where (1 - free_space / total_space) * 100 > 85;
-- 5. 检查归档日志空间
SQL> select * from v$flash_recovery_area_usage;
-- 6. 查看最近的告警日志错误
SQL> select originating_timestamp, message_text
from v$diag_alert_ext
where originating_timestamp > sysdate - 1
and message_text like '%ORA-%'
and message_text not like '%ORA-00000%'
order by originating_timestamp desc;
每周巡检(耗时约30分钟):
sql
-- 1. 检查无效对象
SQL> select owner, object_type, count(*) as invalid_count
from dba_objects
where status = 'INVALID'
group by owner, object_type
order by owner, object_type;
-- 2. 检查碎片化程度高的索引
SQL> select owner, index_name, blevel,
round((del_lf_rows / nullif(lf_rows, 0)) * 100, 2) as frag_pct
from dba_indexes ind
where exists (select 1 from index_stats ist
where ist.name = ind.index_name
and ist.owner = ind.owner
and ist.lf_rows > 0
and ist.del_lf_rows / ist.lf_rows > 0.3);
-- 3. 检查AWR报告中的Top SQL有性能退化
@?/rdbms/admin/awrrpt.sql
-- 4. 检查Data Guard同步状态
SQL> select name, value, unit from v$dataguard_stats
where name in ('transport lag', 'apply lag');
-- 5. 检查RMAN备份是否成功
SQL> select session_key, session_recid, start_time, end_time,
status, input_type, output_device_type
from v$rman_backup_job_details
where start_time > sysdate - 7
order by start_time desc;
每月巡检(耗时约1小时):
sql
-- 1. 检查表空间增长趋势
SQL> select tablespace_name, trunc(creation_time, 'MM') as month,
sum(bytes)/1024/1024 as growth_mb
from dba_data_files
group by tablespace_name, trunc(creation_time, 'MM')
order by tablespace_name, month;
-- 2. 检查用户权限变更
SQL> select grantee, privilege, admin_option, count(*) as grant_count
from dba_sys_privs
where grantee not in ('SYS', 'SYSTEM', 'DBA')
group by grantee, privilege, admin_option
order by grantee;
-- 3. 检查审计记录空间
SQL> select segment_name, bytes/1024/1024 as size_mb
from dba_segments
where segment_name like 'AUD$%' or segment_name like 'FGA_LOG$%'
or segment_name like 'AUDSYS%';
-- 4. 检查数据库版本和补丁信息
SQL> select * from v$version;
SQL> select * from dba_registry_sqlpatch;
-- 5. 生成性能基线报告
@?/rdbms/admin/awrrpt.sql
16.1.3 巡检报告模板
建议使用以下格式记录巡检结果:
巡检日期:2024-06-15
巡检人:张三
数据库:ORCL(19.19.0.0)
1. 数据库状态:正常
2. 实例状态:ORCL1正常,ORCL2正常
3. 表空间使用率:SYSTEM 75%,SYSAUX 68%,USERS 82%,TEMP 45%
4. 归档日志:正常归档,保留3天
5. 备份状态:RMAN备份成功(2024-06-14 02:00:00)
6. Data Guard:传输延迟0秒,应用延迟0秒
7. 告警日志:无ORA错误
8. 无效对象:0个
9. 性能概况:平均DB Time 5.2秒,CPU使用率45%
10. 待处理问题:USERS表空间使用率82%,建议下周扩容
异常事项:无
处理建议:无
16.2 自动化监控方案
16.2.1 监控架构
一个完整的数据库监控架构应包括以下层次:
┌──────────────────────────────────────────────────┐
│ 展示层 │
│ Grafana / Zabbix Web / 自定义Dashboard │
└──────────────────────┬───────────────────────────┘
│
┌──────────────────────▼───────────────────────────┐
│ 数据采集层 │
│ Prometheus / Zabbix Agent / EM Agent / 自定义脚本 │
└──────────────────────┬───────────────────────────┘
│
┌──────────────────────▼───────────────────────────┐
│ 数据存储层 │
│ Prometheus TSDB / Zabbix DB / InfluxDB │
└──────────────────────┬───────────────────────────┘
│
┌──────────────────────▼───────────────────────────┐
│ 告警通知层 │
│ 邮件 / 短信 / 钉钉 / 企业微信 / PagerDuty │
└──────────────────────────────────────────────────┘
16.2.2 使用Shell脚本监控
bash
#!/bin/bash
# 数据库监控脚本:check_db_health.sh
# 功能:检查数据库关键指标,异常时发送告警
# 配置
ORACLE_SID=ORCL
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
ALERT_EMAIL=dba@company.com
THRESHOLD_TABLESPACE=85
# 检查表空间使用率
sqlplus -s / as sysdba <<EOF > /tmp/ts_usage.txt
set pages 0 lines 200 feedback off
select tablespace_name || ':' || round(used_pct, 2)
from (
select df.tablespace_name,
(1 - nvl(fs.free_bytes,0) / df.total_bytes) * 100 as used_pct
from (select tablespace_name, sum(bytes) as total_bytes
from dba_data_files group by tablespace_name) df,
(select tablespace_name, sum(bytes) as free_bytes
from dba_free_space group by tablespace_name) fs
where df.tablespace_name = fs.tablespace_name(+)
)
where used_pct > $THRESHOLD_TABLESPACE;
EOF
# 检查并发送告警
if [ -s /tmp/ts_usage.txt ]; then
mail -s "ALERT: 表空间使用率超过${THRESHOLD_TABLESPACE}% - $ORACLE_SID" \
$ALERT_EMAIL < /tmp/ts_usage.txt
fi
# 检查归档日志空间
sqlplus -s / as sysdba <<EOF > /tmp/arch_usage.txt
set pages 0 lines 200 feedback off
select 'FRA使用率: ' || round(used_pct, 2) || '%'
from (select sum(percent_space_used) as used_pct
from v\$flash_recovery_area_usage)
where used_pct > 85;
EOF
# 检查实例状态
sqlplus -s / as sysdba <<EOF > /tmp/instance_status.txt
set pages 0 lines 200 feedback off
select instance_name || ':' || status
from v\$instance;
EOF
# 配置crontab(每5分钟执行)
# */5 * * * * /u01/scripts/check_db_health.sh
16.2.3 使用Prometheus + Grafana监控
配置Oracle Exporter:
bash
# 1. 下载并配置Oracle Exporter
wget https://github.com/iamseth/oracledb_exporter/releases/download/0.6.0/oracledb_exporter-0.6.0.linux-amd64.tar.gz
tar -xzf oracledb_exporter-0.6.0.linux-amd64.tar.gz
# 2. 创建监控用户
SQL> create user monitor identified by monitor_pass;
SQL> grant connect, select_catalog_role to monitor;
SQL> alter user monitor quota unlimited on users;
# 3. 配置环境变量
export DATA_SOURCE_NAME=monitor/monitor_pass@localhost:1521/ORCL
# 4. 启动Exporter
./oracledb_exporter --default.metrics default-metrics.toml &
# 5. Prometheus配置
# prometheus.yml
# scrape_configs:
# - job_name: 'oracle_db'
# static_configs:
# - targets: ['db_host:9161']
16.2.4 使用Zabbix监控
bash
# Zabbix Agent配置
# /etc/zabbix/zabbix_agentd.conf
# UserParameter=oracle.instance,/usr/bin/sqlplus -s / as sysdba "select status from v\$instance;"
# UserParameter=oracle.tablespace[*],/usr/bin/sqlplus -s / as sysdba "select round((1 - nvl(fs.free_bytes,0) / df.total_bytes) * 100, 2) from (select sum(bytes) as total_bytes from dba_data_files where tablespace_name='$1') df, (select sum(bytes) as free_bytes from dba_free_space where tablespace_name='$1') fs;"
# 重启Agent
systemctl restart zabbix-agent
16.2.5 监控指标清单
| 监控类别 | 监控指标 | 告警阈值 | 检查频率 |
|---|---|---|---|
| 可用性 | 实例状态 | 状态非OPEN | 1分钟 |
| 可用性 | 监听器状态 | 未运行 | 1分钟 |
| 可用性 | Data Guard延迟 | >30秒 | 1分钟 |
| 空间 | 表空间使用率 | >85% | 5分钟 |
| 空间 | 归档空间使用率 | >85% | 5分钟 |
| 空间 | 临时表空间使用率 | >90% | 5分钟 |
| 性能 | DB Time | >30秒 | 5分钟 |
| 性能 | 活跃会话数 | >50 | 5分钟 |
| 性能 | 锁等待 | >5秒 | 1分钟 |
| 备份 | RMAN备份状态 | 失败 | 每天 |
| 安全 | 失败登录尝试 | >5次/分钟 | 1分钟 |
| 资源 | CPU使用率 | >90% | 5分钟 |
| 资源 | 内存使用率 | >90% | 5分钟 |
| 资源 | 磁盘I/O等待 | >50ms | 5分钟 |
16.3 告警配置与管理
16.3.1 告警级别定义
| 告警级别 | 颜色 | 说明 | 响应时间 | 通知方式 |
|---|---|---|---|---|
| P0 - 紧急 | 红色 | 数据库不可用 | 立即响应 | 电话+短信+邮件 |
| P1 - 严重 | 橙色 | 性能严重下降 | 15分钟 | 短信+邮件 |
| P2 - 警告 | 黄色 | 资源接近阈值 | 30分钟 | 邮件 |
| P3 - 通知 | 蓝色 | 日常信息 | 24小时 | 邮件 |
16.3.2 配置Oracle内置告警
sql
-- 设置表空间空间告警阈值(85%警告,97%严重)
SQL> exec dbms_server_space.set_threshold(
tablespace_name => 'USERS',
warning_threshold => 85,
critical_threshold => 97);
-- 设置归档空间告警阈值
SQL> alter system set db_recovery_file_dest_size=500g scope=both;
-- 查看当前告警设置
SQL> select tablespace_name, warning_value, critical_value
from dba_tablespace_usage_metrics;
-- 查看告警日志中的告警
SQL> select originating_timestamp, message_type, message_text
from v$diag_alert_ext
where message_type = 'ALERT'
order by originating_timestamp desc;
-- 创建自定义告警(使用DBMS_SCHEDULER)
BEGIN
dbms_scheduler.create_job(
job_name => 'check_tablespace_alert',
job_type => 'PLSQL_BLOCK',
job_action => 'begin
for rec in (
select tablespace_name,
round(used_percent, 2) as used_pct
from dba_tablespace_usage_metrics
where used_percent > 90
) loop
-- 发送告警(使用UTL_MAIL或自定义函数)
send_alert(rec.tablespace_name || '使用率已达' || rec.used_pct || '%');
end loop;
end;',
start_date => systimestamp,
repeat_interval => 'freq=minutely; interval=5',
enabled => true);
END;
/
16.3.3 告警通知脚本
bash
#!/bin/bash
# 告警通知脚本:send_alert.sh
# 支持邮件、钉钉、企业微信通知
# 邮件通知
send_email() {
local subject="$1"
local body="$2"
echo "$body" | mail -s "$subject" dba@company.com
}
# 钉钉通知
send_dingtalk() {
local webhook_url="https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
local message="{\"msgtype\":\"text\",\"text\":{\"content\":\"$1\"}}"
curl -s -X POST -H "Content-Type: application/json" \
-d "$message" "$webhook_url"
}
# 企业微信通知
send_wechat() {
local webhook_url="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"
local message="{\"msgtype\":\"text\",\"text\":{\"content\":\"$1\"}}"
curl -s -X POST -H "Content-Type: application/json" \
-d "$message" "$webhook_url"
}
# 主函数
main() {
local alert_level="$1"
local alert_message="$2"
case $alert_level in
P0)
send_email "[P0-紧急] $alert_message" "$alert_message"
send_dingtalk "[P0-紧急] $alert_message"
send_wechat "[P0-紧急] $alert_message"
;;
P1)
send_email "[P1-严重] $alert_message" "$alert_message"
send_dingtalk "[P1-严重] $alert_message"
;;
P2)
send_email "[P2-警告] $alert_message" "$alert_message"
;;
P3)
send_email "[P3-通知] $alert_message" "$alert_message"
;;
esac
}
# 使用示例
# main "P0" "数据库ORCL实例1不可用,请立即处理!"
16.3.4 告警归并和抑制
为避免告警风暴,需要配置告警归并和抑制策略:
bash
# 告警归并规则
# 1. 相同告警在10分钟内只发送一次
# 2. 批量告警合并为一条摘要
# 3. 已知维护窗口内不发送告警
# 告警抑制脚本
#!/bin/bash
ALERT_CACHE=/tmp/alert_cache.txt
SUPPRESS_INTERVAL=600 # 10分钟
send_suppressed() {
local alert_key="$1"
local alert_message="$2"
local last_sent=$(grep "^$alert_key:" $ALERT_CACHE 2>/dev/null | cut -d: -f2)
local now=$(date +%s)
if [ -z "$last_sent" ] || [ $((now - last_sent)) -gt $SUPPRESS_INTERVAL ]; then
# 发送告警
send_email "ALERT" "$alert_message"
# 更新缓存
sed -i "/^$alert_key:/d" $ALERT_CACHE 2>/dev/null
echo "$alert_key:$now" >> $ALERT_CACHE
fi
}
16.4 定期维护任务
16.4.1 维护任务清单
| 频率 | 任务 | 说明 | 执行时间 |
|---|---|---|---|
| 每日 | 统计信息收集 | 自动任务,收集过期统计信息 | 凌晨2:00-4:00 |
| 每日 | 备份 | RMAN全量或增量备份 | 凌晨1:00-3:00 |
| 每日 | 归档日志清理 | 删除已备份的归档日志 | 凌晨3:00-4:00 |
| 每周 | 索引重建 | 重建碎片率超过30%的索引 | 周末凌晨 |
| 每周 | 审计记录清理 | 清理30天前的审计记录 | 周末凌晨 |
| 每月 | 表空间扩容 | 扩容使用率超过80%的表空间 | 月初 |
| 每月 | 密码轮换 | 轮换DBA和应用程序密码 | 月初 |
| 每季度 | 补丁更新 | 应用最新的RU/RUR补丁 | 维护窗口 |
| 每半年 | 灾难恢复演练 | 执行DR切换演练 | 非业务高峰期 |
16.4.2 自动化维护脚本
bash
#!/bin/bash
# 维护脚本:db_maintenance.sh
# 功能:自动执行日常维护任务
ORACLE_SID=ORCL
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
LOG_FILE=/u01/logs/maintenance_$(date +%Y%m%d_%H%M%S).log
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> $LOG_FILE
}
# 1. 收集统计信息
log "开始收集统计信息"
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
exec dbms_stats.gather_database_stats(
estimate_percent => dbms_stats.auto_sample_size,
options => 'GATHER STALE',
cascade => true);
EOF
log "统计信息收集完成"
# 2. 清理已备份的归档日志
log "开始清理归档日志"
rman target / >> $LOG_FILE <<EOF
delete archivelog all backed up 1 times to device type disk;
EOF
log "归档日志清理完成"
# 3. 重建碎片索引
log "开始重建碎片索引"
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
declare
cursor c_idx is
select 'alter index ' || owner || '.' || index_name || ' rebuild online' as cmd
from dba_indexes
where owner not in ('SYS', 'SYSTEM', 'OUTLN', 'XDB')
and (select count(*) from index_stats ist
where ist.name = index_name and ist.owner = owner
and ist.lf_rows > 0
and ist.del_lf_rows / ist.lf_rows > 0.3) > 0;
begin
for rec in c_idx loop
begin
execute immediate rec.cmd;
exception when others then
dbms_output.put_line('FAILED: ' || rec.cmd || ' - ' || sqlerrm);
end;
end loop;
end;
/
EOF
log "索引重建完成"
# 4. 清理审计记录
log "开始清理审计记录"
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
BEGIN
dbms_audit_mgmt.set_last_archive_timestamp(
audit_trail_type => dbms_audit_mgmt.audit_trail_unified,
last_archive_time => systimestamp - 30);
dbms_audit_mgmt.clean_audit_trail(
audit_trail_type => dbms_audit_mgmt.audit_trail_unified,
use_last_arch_timestamp => true);
END;
/
EOF
log "审计记录清理完成"
log "维护任务全部完成"
16.4.3 维护窗口操作规范
- 提前通知:维护操作前至少提前3天通知业务方
- 变更申请:提交变更申请,包含操作步骤和回退方案
- 操作前检查 :
- 确认数据库当前状态正常
- 确认备份已完成并可恢复
- 确认操作窗口时间充足
- 操作记录:所有操作步骤和输出记录到日志文件
- 操作后验证 :
- 验证数据库正常启动
- 验证应用程序可正常连接
- 验证Data Guard同步正常
- 执行基本功能测试
- 回退准备:操作前准备好回退方案,确保能在30分钟内回退
16.5 空间管理与清理
16.5.1 空间管理策略
数据库空间管理是DBA最频繁的日常任务之一。合理的空间管理策略可以避免因空间不足导致的数据库故障。
空间管理最佳实践:
- 监控预警:表空间使用率超过85%时发出警告,超过95%时立即处理
- 自动扩展:数据文件启用AUTOEXTEND,但设置最大大小限制
- 定期清理:定期清理不再需要的数据和日志
- 增长规划:根据历史增长趋势,提前规划存储容量
16.5.2 表空间使用率监控
sql
-- 详细表空间使用率查询
SQL> select df.tablespace_name,
round(df.total_bytes / 1024 / 1024, 2) as total_mb,
round(nvl(fs.free_bytes, 0) / 1024 / 1024, 2) as free_mb,
round((df.total_bytes - nvl(fs.free_bytes, 0)) / 1024 / 1024, 2) as used_mb,
round((1 - nvl(fs.free_bytes, 0) / nullif(df.total_bytes, 0)) * 100, 2) as used_pct,
round(nvl(df.max_bytes, df.total_bytes) / 1024 / 1024, 2) as max_mb,
df.autoextensible
from (select tablespace_name,
sum(bytes) as total_bytes,
sum(decode(autoextensible, 'YES', maxbytes, bytes)) as max_bytes,
max(autoextensible) as autoextensible
from dba_data_files
group by tablespace_name) df,
(select tablespace_name, sum(bytes) as free_bytes
from dba_free_space
group by tablespace_name) fs
where df.tablespace_name = fs.tablespace_name(+)
order by used_pct desc;
-- 查看表空间文件详细信息
SQL> select file_name, tablespace_name, bytes/1024/1024 as size_mb,
maxbytes/1024/1024 as max_mb, autoextensible, increment_by,
status, online_status
from dba_data_files
order by tablespace_name, file_name;
16.5.3 表空间扩容
sql
-- 方式1:增加数据文件大小
SQL> alter database datafile '/u01/oradata/users01.dbf' resize 32g;
-- 方式2:启用自动扩展
SQL> alter database datafile '/u01/oradata/users01.dbf'
autoextend on next 1g maxsize 32g;
-- 方式3:添加新的数据文件
SQL> alter tablespace users
add datafile '/u01/oradata/users02.dbf' size 10g
autoextend on next 1g maxsize 32g;
-- 方式4:添加临时表空间文件
SQL> alter tablespace temp
add tempfile '/u01/oradata/temp02.dbf' size 10g
autoextend on next 512m maxsize 32g;
16.5.4 空间回收
sql
-- 1. 回收表空间(高水位线压缩)
-- 需要额外的空间,适合大表
SQL> alter table scott.emp enable row movement;
SQL> alter table scott.emp shrink space cascade;
SQL> alter table scott.emp disable row movement;
-- 2. 重建表(降低高水位线)
SQL> alter table scott.emp move;
-- 重建索引(move后索引失效)
SQL> alter index pk_emp rebuild;
-- 3. 回收临时表空间
SQL> alter tablespace temp shrink space keep 2g;
-- 4. 清理回收站
SQL> purge dba_recyclebin;
-- 5. 清理SYSAUX表空间(AWR/审计数据)
BEGIN
-- 减少AWR保留天数
dbms_workload_repository.modify_snapshot_settings(retention => 43200);
-- 清理AWR数据
dbms_workload_repository.drop_snapshot_range(
low_snap_id => 1,
high_snap_id => 100);
END;
/
16.5.5 空间增长预测
sql
-- 创建空间增长表
SQL> create table space_growth_history as
select sysdate as capture_time, tablespace_name,
round(sum(bytes)/1024/1024, 2) as total_mb
from dba_data_files
group by tablespace_name;
-- 定期采集(每周执行)
SQL> insert into space_growth_history
select sysdate, tablespace_name, round(sum(bytes)/1024/1024, 2)
from dba_data_files
group by tablespace_name;
-- 查看增长趋势
SQL> select tablespace_name,
trunc(capture_time, 'MM') as month,
avg(total_mb) as avg_mb,
max(total_mb) as max_mb,
max(total_mb) - min(total_mb) as growth_mb
from space_growth_history
where capture_time > add_months(sysdate, -6)
group by tablespace_name, trunc(capture_time, 'MM')
order by tablespace_name, month;
-- 预测未来3个月空间需求
SQL> select tablespace_name,
avg_mb as current_mb,
round(avg_mb * 1.1, 2) as predict_1m,
round(avg_mb * 1.2, 2) as predict_3m
from (select tablespace_name, avg(total_mb) as avg_mb
from space_growth_history
where capture_time > sysdate - 30
group by tablespace_name);
16.6 作业调度(DBMS_SCHEDULER)
16.6.1 DBMS_SCHEDULER概述
DBMS_SCHEDULER是Oracle内置的作业调度器,用于自动执行定时任务。相比传统的DBMS_JOB,DBMS_SCHEDULER提供了更强大的功能:
- 支持复杂的调度计划(日历表达式)
- 支持作业链(Job Chain)
- 支持作业优先级和资源管理
- 支持作业窗口和资源计划
- 支持作业事件通知
16.6.2 创建作业
sql
-- 创建简单的定时作业
BEGIN
dbms_scheduler.create_job(
job_name => 'daily_stats_job',
job_type => 'PLSQL_BLOCK',
job_action => 'begin
dbms_stats.gather_database_stats(
estimate_percent => dbms_stats.auto_sample_size,
options => ''GATHER STALE'',
cascade => true);
end;',
start_date => systimestamp,
repeat_interval => 'freq=daily; byhour=2; byminute=0; bysecond=0',
enabled => true,
comments => '每日凌晨2点收集统计信息');
END;
/
-- 创建执行存储过程的作业
BEGIN
dbms_scheduler.create_job(
job_name => 'archive_cleanup_job',
job_type => 'STORED_PROCEDURE',
job_action => 'cleanup_archivelogs',
number_of_arguments => 0,
start_date => systimestamp,
repeat_interval => 'freq=daily; byhour=3; byminute=0',
enabled => true,
comments => '每日凌晨3点清理归档日志');
END;
/
16.6.3 调度计划(Schedule)
sql
-- 创建重复调度计划
BEGIN
dbms_scheduler.create_schedule(
schedule_name => 'weekday_night_schedule',
start_date => systimestamp,
repeat_interval => 'freq=daily; byhour=22; byminute=0; byday=mon,tue,wed,thu,fri',
comments => '工作日晚上10点执行');
END;
/
-- 创建一次性调度计划
BEGIN
dbms_scheduler.create_schedule(
schedule_name => 'maintenance_window_schedule',
start_date => to_timestamp('2024-06-30 01:00:00', 'yyyy-mm-dd hh24:mi:ss'),
end_date => to_timestamp('2024-06-30 06:00:00', 'yyyy-mm-dd hh24:mi:ss'),
repeat_interval => 'freq=monthly; bymonthday=1',
comments => '每月1日凌晨1-6点维护窗口');
END;
/
-- 使用调度计划创建作业
BEGIN
dbms_scheduler.create_job(
job_name => 'weekly_index_rebuild',
job_type => 'PLSQL_BLOCK',
job_action => 'rebuild_fragmented_indexes;',
schedule_name => 'weekday_night_schedule',
enabled => true);
END;
/
16.6.4 作业链(Job Chain)
sql
-- 创建作业链步骤
BEGIN
-- 步骤1:创建作业链
dbms_scheduler.create_chain(
chain_name => 'backup_chain',
comments => '数据库备份链');
-- 步骤2:定义链步骤
dbms_scheduler.define_chain_step(
chain_name => 'backup_chain',
step_name => 'step_1_precheck',
program_name => 'precheck_program');
dbms_scheduler.define_chain_step(
chain_name => 'backup_chain',
step_name => 'step_2_backup',
program_name => 'rman_backup_program');
dbms_scheduler.define_chain_step(
chain_name => 'backup_chain',
step_name => 'step_3_verify',
program_name => 'verify_backup_program');
-- 步骤3:定义链规则
dbms_scheduler.define_chain_rule(
chain_name => 'backup_chain',
condition => 'TRUE',
action => 'START step_1_precheck',
rule_name => 'precheck_rule');
dbms_scheduler.define_chain_rule(
chain_name => 'backup_chain',
condition => 'step_1_precheck SUCCEEDED',
action => 'START step_2_backup',
rule_name => 'backup_rule');
dbms_scheduler.define_chain_rule(
chain_name => 'backup_chain',
condition => 'step_2_backup SUCCEEDED',
action => 'START step_3_verify',
rule_name => 'verify_rule');
-- 步骤4:启用链并创建作业
dbms_scheduler.enable('backup_chain');
dbms_scheduler.create_job(
job_name => 'backup_chain_job',
job_type => 'CHAIN',
job_action => 'backup_chain',
repeat_interval => 'freq=daily; byhour=1',
enabled => true);
END;
/
16.6.5 作业管理
sql
-- 查看作业状态
SQL> select job_name, status, last_start_date, last_run_duration,
next_run_date, failure_count
from dba_scheduler_jobs
where owner = 'SYS'
order by job_name;
-- 查看作业运行历史
SQL> select job_name, status, actual_start_date, run_duration,
cpu_used, additional_info
from dba_scheduler_job_run_details
where job_name = 'DAILY_STATS_JOB'
order by actual_start_date desc;
-- 启用/禁用作业
SQL> exec dbms_scheduler.enable('daily_stats_job');
SQL> exec dbms_scheduler.disable('daily_stats_job');
-- 停止正在运行的作业
SQL> exec dbms_scheduler.stop_job('daily_stats_job');
-- 立即运行作业
SQL> exec dbms_scheduler.run_job('daily_stats_job');
-- 删除作业
SQL> exec dbms_scheduler.drop_job('daily_stats_job');
-- 查看作业日志
SQL> select log_id, log_date, job_name, status, additional_info
from dba_scheduler_job_log
where job_name = 'DAILY_STATS_JOB'
order by log_date desc;
第17章 补丁与升级管理
17.1 Oracle补丁类型
17.1.1 补丁类型概述
Oracle提供了多种类型的补丁,DBA需要了解不同补丁的用途和适用场景:
| 补丁类型 | 说明 | 发布频率 | 适用范围 |
|---|---|---|---|
| RU(Release Update) | 季度安全更新,包含安全修复和选定的Bug修复 | 每季度 | 所有版本 |
| RUR(Release Update Revision) | RU的修订版,在RU基础上修复紧急Bug | 按需 | 特定RU |
| CPU(Critical Patch Update) | 安全补丁更新(已替换为RU) | 每季度 | 旧版本 |
| PSU(Patch Set Update) | 累积补丁集(已替换为RU) | 每季度 | 旧版本 |
| BP(Bundle Patch) | 特定平台的补丁包 | 不定期 | 特定平台 |
| One-Off Patch | 单次Bug修复补丁 | 按需 | 特定问题 |
| Interim Patch | 临时补丁(Hotfix) | 按需 | 紧急修复 |
| Patch Set | 大版本补丁集(如19.3.0→19.19.0) | 每年 | 所有版本 |
17.1.2 补丁版本命名规则
Oracle 19c及之后的补丁命名规则:
19.19.0.0.240115
├┘├┘├┘├┘ ├──────┘
│ │ │ │ └── 发布日期(2024年1月15日)
│ │ │ └───── 平台特定版本
│ │ └─────── 数据库版本
│ └───────── RU版本号(第19个RU)
└─────────── 主版本号
17.1.3 查看已安装的补丁
sql
-- 查看当前数据库版本
SQL> select * from v$version;
-- 查看已安装的RU补丁
SQL> select patch_id, patch_name, action, status, action_time,
version, description
from dba_registry_sqlpatch
order by action_time desc;
-- 查看OPatch安装的补丁
$ opatch lsinventory
-- 查看特定补丁的详细信息
$ opatch lsinventory -detail -oh $ORACLE_HOME
17.1.4 补丁选择策略
| 场景 | 推荐策略 | 说明 |
|---|---|---|
| 生产环境 | 应用最新的RU | 包含安全修复和Bug修复 |
| 测试环境 | 与生产环境一致的RU | 确保兼容性 |
| 开发环境 | 最新RU或RUR | 提前验证新补丁 |
| 紧急修复 | One-Off Patch | 针对特定问题,后续RU会包含 |
17.2 OPatch工具使用
17.2.1 OPatch概述
OPatch是Oracle提供的补丁管理工具,用于安装、查看和卸载Oracle补丁。
bash
# 查看OPatch版本
$ $ORACLE_HOME/OPatch/opatch version
# 查看OPatch帮助
$ $ORACLE_HOME/OPatch/opatch -help
17.2.2 补丁安装流程
bash
# 1. 准备工作
# 1.1 备份Oracle Home
$ tar -czf $ORACLE_BASE/backup/oh_backup_$(date +%Y%m%d).tar.gz $ORACLE_HOME
# 1.2 检查补丁冲突
$ $ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail \
-phBaseDir /u01/patches/12345678
# 1.3 检查补丁空间要求
$ $ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace \
-phBaseDir /u01/patches/12345678
# 2. 停止相关服务
$ srvctl stop database -d $ORACLE_SID
$ srvctl stop listener
$ lsnrctl stop
# 3. 安装补丁
$ cd /u01/patches/12345678
$ $ORACLE_HOME/OPatch/opatch apply
# 4. 执行SQL脚本(如果补丁包含SQL变更)
$ cd $ORACLE_HOME/rdbms/admin
$ sqlplus / as sysdba
SQL> startup upgrade
SQL> @catcon.pl -n 1 -l /tmp -b patch_catalog catupgrd.sql
SQL> shutdown immediate
SQL> startup
SQL> @utlrp.sql
# 5. 验证安装
$ $ORACLE_HOME/OPatch/opatch lsinventory
SQL> select patch_id, patch_name, status from dba_registry_sqlpatch;
17.2.3 OPatch常用命令
bash
# 查看已安装的补丁
$ opatch lsinventory
# 查看补丁信息
$ opatch lsinventory -detail -oh $ORACLE_HOME
# 检查补丁冲突
$ opatch prereq CheckConflictAgainstOHWithDetail -phBaseDir /patch_dir
# 检查补丁空间
$ opatch prereq CheckSystemSpace -phBaseDir /patch_dir
# 检查补丁是否可安装
$ opatch prereq CheckApplicable -phBaseDir /patch_dir
# 安装补丁
$ opatch apply
# 安装补丁(不执行SQL脚本)
$ opatch apply -no_inventory
# 回滚补丁
$ opatch rollback -id 12345678
# 回滚最后一个补丁
$ opatch rollback -local
# 查看补丁的依赖关系
$ opatch lsinventory -detail -oh $ORACLE_HOME | grep -i "interim"
17.2.4 补丁安装注意事项
- 备份Oracle Home:安装前必须备份
- 测试环境验证:先在测试环境验证补丁
- 补丁顺序:按照Oracle文档指定的顺序安装
- RAC环境:需要使用滚动补丁方式,减少停机时间
- 回退方案:准备好补丁回退方案
- 补丁文档:阅读补丁Readme中的特殊说明
17.2.5 RAC滚动补丁
bash
# RAC环境滚动补丁步骤
# 1. 在节点1上停止数据库实例
$ srvctl stop instance -d $ORACLE_SID -i $ORACLE_SID1
# 2. 在节点1上安装补丁
$ $ORACLE_HOME/OPatch/opatch apply
# 3. 在节点1上执行SQL脚本
$ sqlplus / as sysdba
SQL> startup upgrade
SQL> @catcon.pl -n 1 -l /tmp -b patch_catalog catupgrd.sql
SQL> shutdown immediate
SQL> startup
SQL> @utlrp.sql
# 4. 启动节点1的实例
$ srvctl start instance -d $ORACLE_SID -i $ORACLE_SID1
# 5. 重复步骤1-4,在节点2上执行
17.3 数据库升级策略
17.3.1 升级路径
Oracle升级的推荐路径:
旧版本 → 中间版本 → 目标版本
11.2.0.4 → 12.1.0.2 → 12.2.0.1 → 18.0.0.0 → 19.0.0.0
↓
19.0.0.0 (推荐长期支持版本)
升级策略对比:
| 策略 | 说明 | 停机时间 | 风险 |
|---|---|---|---|
| 原地升级 | 直接升级现有数据库 | 较长 | 中等 |
| 逻辑迁移 | 使用Data Pump导出导入 | 较长 | 较低 |
| 物理迁移 | 使用RMAN或Goldengate | 较短 | 较低 |
| DBUA | 使用Database Upgrade Assistant | 中等 | 较低 |
| Autoupgrade | 使用自动升级工具 | 中等 | 低 |
17.3.2 升级前检查
bash
# 1. 运行预升级检查脚本
$ $ORACLE_HOME/rdbms/admin/preupgrd.sql
# 2. 查看预升级报告
# 根据报告中的建议修复问题
# 3. 检查兼容性
SQL> select name, value from v$parameter where name = 'compatible';
# 4. 检查是否有无效对象
SQL> select count(*) from dba_objects where status = 'INVALID';
# 5. 检查统计信息
SQL> select count(*) from dba_tab_statistics where last_analyzed is null;
# 6. 检查空间需求
SQL> select tablespace_name, sum(bytes)/1024/1024/1024 as size_gb
from dba_data_files
group by tablespace_name;
17.3.3 升级到19c示例
bash
# 1. 准备新Oracle Home
# 安装19c软件到新的ORACLE_HOME(不在原目录)
# 2. 运行预升级脚本
$ cd $ORACLE_HOME/rdbms/admin
$ sqlplus / as sysdba @preupgrd.sql
# 3. 修复预升级报告中的问题
# 查看 $ORACLE_BASE/cfgtoollogs/$ORACLE_SID/preupgrade/preupgrade.log
# 4. 使用DBUA升级
# 图形界面方式
$ dbua
# 或使用Autoupgrade(推荐)
$ java -jar $ORACLE_HOME/rdbms/admin/autoupgrade.jar \
-config /home/oracle/autoupgrade_config.cfg -mode deploy
# 5. 升级后检查
SQL> @utlrp.sql -- 重新编译无效对象
SQL> select count(*) from dba_objects where status = 'INVALID';
SQL> select * from v$version;
17.3.4 升级后检查清单
- 数据库版本是否正确
- 所有对象是否编译成功(无无效对象)
- 应用程序是否能正常连接
- 统计信息是否已更新
- SPFILE是否已更新
- 监听器配置是否正确
- Data Guard是否重新配置
- RMAN备份是否正常
- 作业调度是否正常
- 性能基线是否已建立
17.4 自动升级工具(Autoupgrade)
17.4.1 Autoupgrade概述
Autoupgrade是Oracle 12.2开始提供的自动升级工具,可以自动执行数据库升级的各个阶段,包括预检查、升级、编译、验证等,大幅简化升级过程。
Autoupgrade的优势:
- 自动执行升级的各个阶段
- 支持并行升级多个数据库
- 支持滚动升级(RAC环境)
- 失败时自动回退
- 生成详细的升级报告
17.4.2 配置Autoupgrade
bash
# 创建配置文件
$ cat > /home/oracle/autoupgrade_config.cfg <<EOF
# 全局配置
global.log_dir=/u01/autoupgrade/logs
global.upgrade_dir=/u01/autoupgrade/upgrade
global.backup_dir=/u01/autoupgrade/backup
global.keep_utlrp_dir=/u01/autoupgrade/utlrp
# 数据库配置
# SID:ORCL
upgrade1.source_home=/u01/app/oracle/product/12.2.0/dbhome_1
upgrade1.target_home=/u01/app/oracle/product/19.0.0/dbhome_1
upgrade1.sid=ORCL
upgrade1.node=localhost
upgrade1.target_version=19
upgrade1.start_time=NOW
upgrade1.upgrade_node=localhost
upgrade1.run_utlrp=yes
upgrade1.timezone_upg=yes
upgrade1.restoration=yes
EOF
17.4.3 运行Autoupgrade
bash
# 分析模式(只检查,不升级)
$ java -jar $ORACLE_HOME/rdbms/admin/autoupgrade.jar \
-config /home/oracle/autoupgrade_config.cfg \
-mode analyze
# 部署模式(执行升级)
$ java -jar $ORACLE_HOME/rdbms/admin/autoupgrade.jar \
-config /home/oracle/autoupgrade_config.cfg \
-mode deploy
# 查看升级状态
$ java -jar $ORACLE_HOME/rdbms/admin/autoupgrade.jar \
-config /home/oracle/autoupgrade_config.cfg \
-mode status
# 恢复失败的升级
$ java -jar $ORACLE_HOME/rdbms/admin/autoupgrade.jar \
-config /home/oracle/autoupgrade_config.cfg \
-mode restore
17.4.4 Autoupgrade升级报告
bash
# 查看升级日志
$ ls -la /u01/autoupgrade/logs/ORCL/
# 升级日志文件:
# preupgrade.log - 预升级检查
# upgrade.log - 升级过程
# postupgrade.log - 升级后操作
# summary.log - 升级总结
# 查看升级总结
$ cat /u01/autoupgrade/logs/ORCL/summary.log
17.5 跨版本迁移
17.5.1 迁移方式对比
| 迁移方式 | 适用场景 | 停机时间 | 数据量 | 复杂度 |
|---|---|---|---|---|
| Data Pump | 逻辑迁移,版本跨度大 | 较长 | 中 | 低 |
| RMAN Convert | 跨平台迁移 | 中等 | 大 | 中 |
| GoldenGate | 零停机迁移 | 极短 | 任意 | 高 |
| Transportable Tablespace | 表空间级迁移 | 短 | 大 | 中 |
| Full Transportable Export | 全库可传输导出 | 中等 | 大 | 中 |
17.5.2 使用Data Pump迁移
bash
# 源库导出
$ expdp system/password@source_db \
full=y \
directory=exp_dir \
dumpfile=fullexp_%U.dmp \
logfile=fullexp.log \
parallel=4 \
compression=all \
exclude=STATISTICS
# 目标库导入
$ impdp system/password@target_db \
full=y \
directory=exp_dir \
dumpfile=fullexp_%U.dmp \
logfile=fullimp.log \
parallel=4 \
transform=segment_attributes:n \
remap_tablespace=source_ts:target_ts
17.5.3 使用RMAN实现跨平台迁移
bash
# 查看源平台的endian格式
$ rman target /
RMAN> select platform_name, endian_format from v$transportable_platform;
# 转换表空间
$ rman target /
RMAN> convert tablespace users
to platform 'Linux x86 64-bit'
format '/u01/transport/%U';
# 在目标库上转换数据文件
RMAN> convert datafile '/u01/transport/users01.dbf'
from platform 'Linux x86 64-bit'
db_file_name_convert '/u01/transport/', '/u01/oradata/';
17.5.4 迁移前检查清单
- 源库和目标库字符集是否兼容
- 源库和目标库的时区版本是否一致
- 目标库的兼容性参数是否设置正确
- 是否有足够的存储空间
- 网络带宽是否满足数据传输要求
- 应用程序是否兼容目标版本
- 是否有第三方组件需要单独迁移
- 迁移后是否需要进行性能调优
17.6 回退方案
17.6.1 回退方案的重要性
每次升级或补丁操作都必须准备回退方案。回退方案应包含详细的步骤、预期时间、验证方法等。
17.6.2 使用RMAN备份回退
bash
# 升级前必须执行全库备份
$ rman target /
RMAN> backup database plus archivelog format '/backup/preupgrade_%U';
# 回退步骤
# 1. 关闭数据库
SQL> shutdown immediate;
# 2. 恢复数据库
$ rman target /
RMAN> startup nomount;
RMAN> restore controlfile from autobackup;
RMAN> alter database mount;
RMAN> restore database;
RMAN> recover database;
# 3. 打开数据库
SQL> alter database open resetlogs;
# 4. 验证
SQL> select * from v$version;
SQL> select count(*) from dba_objects where status = 'INVALID';
17.6.3 使用Flashback Database回退
sql
-- 升级前创建还原点
SQL> create restore point before_upgrade guarantee flashback database;
-- 回退到还原点
SQL> shutdown immediate;
SQL> startup mount;
SQL> flashback database to restore point before_upgrade;
SQL> alter database open resetlogs;
-- 删除还原点
SQL> drop restore point before_upgrade;
17.6.4 回退OPatch补丁
bash
# 回退补丁
$ $ORACLE_HOME/OPatch/opatch rollback -id 12345678
# 回退最新的补丁
$ $ORACLE_HOME/OPatch/opatch rollback -local
# 回退后验证
$ $ORACLE_HOME/OPatch/opatch lsinventory
SQL> select patch_id, status from dba_registry_sqlpatch;
17.6.5 升级回退检查清单
- 升级前是否已执行全库备份
- 升级前是否已创建Guaranteed Restore Point
- 回退方案是否已在测试环境验证
- 回退所需时间是否在维护窗口内
- 回退后是否需要重新配置Data Guard
- 回退后是否需要重建索引
- 回退后是否需要重新收集统计信息
- 回退后是否通知了所有相关业务方
第18章 故障诊断与处理
18.1 故障诊断方法论
18.1.1 故障诊断的基本原则
- 确认问题:明确故障现象,收集相关日志
- 影响评估:评估故障影响范围和严重程度
- 根因分析:从现象追溯到根本原因
- 制定方案:制定恢复方案,评估风险
- 执行恢复:按照方案执行恢复操作
- 验证确认:验证恢复结果,确认问题已解决
- 复盘改进:分析根因,制定预防措施
18.1.2 故障诊断流程
故障发生
│
▼
[第1步] 收集信息
├── 告警日志(alert.log)
├── 监听器日志(listener.log)
├── 跟踪文件(trace files)
├── AWR/ASH报告
├── 系统负载(CPU/内存/I/O)
└── 用户反馈信息
│
▼
[第2步] 初步判断
├── 数据库是否可用
├── 性能是否下降
├── 是否有错误信息
└── 影响范围大小
│
▼
[第3步] 深入分析
├── 分析告警日志
├── 分析跟踪文件
├── 使用诊断工具(ADDM/ASH)
└── 查询MOS文档
│
▼
[第4步] 制定方案
├── 临时方案(快速恢复服务)
├── 永久方案(修复根因)
└── 回退方案(方案失败时)
│
▼
[第5步] 执行恢复
├── 按方案执行
├── 记录操作日志
└── 监控恢复过程
│
▼
[第6步] 验证确认
├── 数据库功能正常
├── 性能指标正常
├── 应用程序可连接
└── 用户确认
│
▼
[第7步] 复盘改进
├── 编写故障报告
├── 分析根因
├── 制定预防措施
└── 更新应急预案
18.1.3 故障诊断工具
| 工具 | 用途 | 使用场景 |
|---|---|---|
| alert.log | 数据库告警日志 | 查看错误信息和事件时间线 |
| trc/trace文件 | 进程跟踪文件 | 分析特定进程的错误 |
| AWR报告 | 性能分析 | 分析性能问题 |
| ASH报告 | 活动会话分析 | 定位短时性能问题 |
| ADDM | 自动诊断 | 自动识别性能瓶颈 |
| SQL Trace | SQL跟踪 | 分析单条SQL性能 |
| OSWatcher | 操作系统监控 | 分析系统资源问题 |
| TFA | 日志收集工具 | 一键收集诊断信息 |
| MOS | Oracle支持网站 | 查找已知问题和解决方案 |
18.2 常见故障案例分析
18.2.1 案例1:表空间不足导致数据库挂起
现象: 应用程序无法执行DML操作,数据库响应缓慢
诊断过程:
sql
-- 检查表空间使用率
SQL> select tablespace_name, used_pct
from dba_tablespace_usage_metrics
where used_pct > 99;
-- 发现USERS表空间使用率达到100%
-- 检查是否启用了自动扩展
SQL> select file_name, autoextensible, maxbytes, bytes
from dba_data_files
where tablespace_name = 'USERS';
-- 如果未启用自动扩展或达到最大限制
解决方案:
sql
-- 立即扩容
SQL> alter database datafile '/u01/oradata/users01.dbf' resize 32g;
-- 或添加新数据文件
SQL> alter tablespace users add datafile '/u01/oradata/users02.dbf' size 10g
autoextend on next 1g maxsize 32g;
预防措施:
- 设置表空间使用率告警(85%警告,95%严重)
- 启用数据文件自动扩展
- 定期监控表空间使用趋势
18.2.2 案例2:归档日志空间满导致数据库挂起
现象: 数据库无法执行DML操作,报错ORA-00257
诊断过程:
sql
-- 检查FRA空间使用
SQL> select * from v$flash_recovery_area_usage;
-- 检查归档日志序列
SQL> select thread#, sequence#, name, blocks, block_size,
archived, deleted, status
from v$archived_log
where deleted = 'NO'
order by sequence#;
解决方案:
bash
# 1. 立即释放空间(删除已备份的归档日志)
$ rman target /
RMAN> delete archivelog all backed up 1 times to device type disk;
# 2. 或增加FRA大小
SQL> alter system set db_recovery_file_dest_size=500g scope=both;
# 3. 配置归档日志删除策略
RMAN> configure archivelog deletion policy to backed up 1 times to device type disk;
预防措施:
- 配置归档日志自动删除策略
- 设置FRA空间告警阈值
- 定期检查归档日志空间
18.2.3 案例3:行锁竞争导致系统阻塞
现象: 多个会话等待,系统吞吐量下降
诊断过程:
sql
-- 查看锁等待
SQL> select l1.sid as blocking_sid,
l2.sid as blocked_sid,
l1.type as lock_type,
l1.ctime as seconds_held,
s1.sql_id as blocking_sql,
s2.sql_id as blocked_sql
from v$lock l1, v$lock l2, v$session s1, v$session s2
where l1.block = 1
and l2.request > 0
and l1.id1 = l2.id1
and l1.sid = s1.sid
and l2.sid = s2.sid;
-- 查找阻塞会话正在执行的SQL
SQL> select sql_id, sql_text
from v$sql
where sql_id = 'blocking_sql_id';
解决方案:
sql
-- 1. 通知业务方提交或回滚事务
-- 2. 如果无法联系业务方,强制终止阻塞会话
SQL> alter system kill session 'sid, serial#' immediate;
-- 3. 分析根因,优化应用事务逻辑
预防措施:
- 应用层确保事务及时提交
- 使用合理的锁超时设置
- 监控锁等待事件,及时预警
18.2.4 案例4:ORA-600内部错误
现象: 数据库报ORA-600内部错误,可能伴随实例崩溃
诊断过程:
bash
# 1. 收集诊断信息
$ $ORACLE_HOME/bin/tfactl diag collect -database $ORACLE_SID
# 2. 查看告警日志中的详细错误
$ tail -100 $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace/alert_$ORACLE_SID.log
# 3. 查找跟踪文件
$ ls -la $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace/*.trc
# 4. 在MOS上搜索ORA-600错误参数
# 搜索:ORA-600 [参数1] [参数2] [版本]
解决方案:
- 根据MOS文档中的建议执行相应的修复操作
- 通常需要应用补丁或执行特定的恢复操作
- 联系Oracle技术支持获取帮助
18.2.5 案例5:数据库无法启动
现象: 数据库启动过程中报错,无法正常打开
诊断过程:
sql
-- 尝试启动并查看错误
SQL> startup
-- ORA-01157: cannot identify/lock data file
-- ORA-01110: data file 4: '/u01/oradata/users01.dbf'
-- 检查数据文件状态
SQL> select file#, name, status, error
from v$datafile;
-- 查看控制文件和数据文件的一致性
SQL> select file#, name, checkpoint_change# from v$datafile;
SQL> select file#, name, checkpoint_change# from v$datafile_header;
解决方案:
sql
-- 如果数据文件丢失但有备份,恢复数据文件
SQL> alter database datafile '/u01/oradata/users01.dbf' offline;
RMAN> restore datafile '/u01/oradata/users01.dbf';
RMAN> recover datafile '/u01/oradata/users01.dbf';
SQL> alter database datafile '/u01/oradata/users01.dbf' online;
-- 如果数据文件不需要恢复,可以删除
SQL> alter database datafile '/u01/oradata/users01.dbf' offline drop;
SQL> alter database open;
18.3 ORA错误处理
18.3.1 常见ORA错误及处理
| ORA错误 | 说明 | 常见原因 | 处理建议 |
|---|---|---|---|
| ORA-00001 | 唯一约束违反 | 插入重复数据 | 检查数据,使用MERGE语句 |
| ORA-00060 | 死锁检测 | 两个会话互相等待 | 终止阻塞会话,优化应用 |
| ORA-00257 | 归档日志满 | FRA空间不足 | 清理归档日志,增加FRA大小 |
| ORA-00312 | 在线日志损坏 | 磁盘故障 | 恢复日志文件,可能需要恢复数据库 |
| ORA-00313 | 日志组无法打开 | 日志文件损坏 | 重建日志组 |
| ORA-00600 | 内部错误 | 各种原因 | 收集诊断信息,联系Oracle支持 |
| ORA-01017 | 用户名/密码无效 | 认证失败 | 检查密码,重置密码 |
| ORA-01157 | 无法识别数据文件 | 数据文件丢失或损坏 | 恢复数据文件 |
| ORA-01555 | 快照过旧 | UNDO空间不足 | 增大UNDO表空间,优化SQL |
| ORA-01652 | 临时表空间不足 | 排序空间不足 | 增大临时表空间 |
| ORA-01653 | 表空间无法扩展 | 表空间满 | 添加数据文件,启用自动扩展 |
| ORA-01688 | 无法扩展归档日志 | 归档空间不足 | 清理归档日志 |
| ORA-02049 | 分布式事务超时 | 分布式事务等待 | 检查分布式事务状态 |
| ORA-04031 | 共享内存不足 | SGA内存不足 | 增大SGA,减少内存碎片 |
| ORA-12514 | 监听器无法识别服务 | 监听器配置错误 | 检查listener.ora和tnsnames.ora |
| ORA-12560 | 协议适配器错误 | 数据库未启动 | 检查数据库实例状态 |
18.3.2 ORA错误分析流程
sql
-- 1. 查看错误详情
SQL> select * from v$diag_alert_ext
where message_text like '%ORA-00600%'
order by originating_timestamp desc;
-- 2. 查看相关的跟踪文件
-- 跟踪文件位置:
-- $ORACLE_BASE/diag/rdbms/$ORACLE_SID/$ORACLE_SID/trace/
-- 3. 查询相关数据字典
SQL> select * from v$sql where sql_id = '&sql_id';
SQL> select * from v$session where sid = &sid;
-- 4. 在MOS上搜索
-- 使用ORA错误号和参数搜索
-- 搜索格式:ORA-00600 [参数1] [参数2]
18.3.3 ORA错误处理原则
- 不要恐慌:大多数ORA错误都有已知的解决方案
- 收集信息:记录完整的错误信息、时间、操作
- 搜索MOS:Oracle Support上有大量已知问题的解决方案
- 测试方案:在测试环境验证方案再执行
- 升级支持:复杂问题及时联系Oracle技术支持
18.4 数据库启动故障处理
18.4.1 启动阶段与故障
| 启动阶段 | 说明 | 常见故障 |
|---|---|---|
| NOMOUNT | 启动实例,读取参数文件 | 参数文件损坏、内存分配失败 |
| MOUNT | 加载控制文件 | 控制文件丢失、控制文件损坏 |
| OPEN | 打开数据文件 | 数据文件损坏、数据文件丢失 |
18.4.2 NOMOUNT阶段故障
sql
-- 故障:参数文件丢失
SQL> startup;
-- ORA-01078: failure in processing system parameters
-- LRM-00109: could not open parameter file
-- 解决方案1:从备份恢复spfile
RMAN> restore spfile from autobackup;
-- 解决方案2:创建新的pfile
SQL> create pfile='?/dbs/initORCL.ora' from spfile;
-- 如果spfile也丢失,手动创建pfile
-- 解决方案3:使用默认参数启动
SQL> startup nomount pfile='?/dbs/init.ora';
18.4.3 MOUNT阶段故障
sql
-- 故障:控制文件丢失
SQL> startup mount;
-- ORA-00205: error in identifying control file
-- 查看控制文件位置
SQL> show parameter control_files;
-- 解决方案1:从备份恢复控制文件
RMAN> restore controlfile from autobackup;
-- 解决方案2:创建控制文件脚本
SQL> alter database backup controlfile to trace as '/tmp/cf_create.sql';
-- 解决方案3:重新创建控制文件
SQL> startup nomount;
SQL> create controlfile reuse database "ORCL" resetlogs noarchivelog
maxlogfiles 5
maxlogmembers 5
maxdatafiles 100
maxinstances 2
logfile
group 1 ('/u01/oradata/redo01.log') size 500m,
group 2 ('/u01/oradata/redo02.log') size 500m
datafile
'/u01/oradata/system01.dbf',
'/u01/oradata/sysaux01.dbf',
'/u01/oradata/undo01.dbf',
'/u01/oradata/users01.dbf';
18.4.4 OPEN阶段故障
sql
-- 故障:数据文件丢失或损坏
SQL> startup;
-- ORA-01157: cannot identify/lock data file 4
-- ORA-01110: data file 4: '/u01/oradata/users01.dbf'
-- 查看数据文件状态
SQL> select file#, name, status, error, recovery
from v$datafile;
-- 解决方案1:恢复数据文件(有备份)
SQL> alter database datafile 4 offline;
RMAN> restore datafile 4;
RMAN> recover datafile 4;
SQL> alter database datafile 4 online;
-- 解决方案2:使用备份的控制文件恢复
RMAN> startup nomount;
RMAN> restore controlfile from autobackup;
RMAN> alter database mount;
RMAN> restore database;
RMAN> recover database;
SQL> alter database open resetlogs;
-- 解决方案3:数据文件不需要时,删除并打开
SQL> alter database datafile 4 offline drop;
SQL> alter database open;
18.4.5 UNDO表空间故障
sql
-- 故障:UNDO表空间损坏
SQL> startup;
-- ORA-00604: error occurred at recursive SQL level 1
-- ORA-01650: unable to extend rollback segment
-- 解决方案:重建UNDO表空间
SQL> startup mount;
SQL> alter system set undo_tablespace='UNDOTBS2' scope=spfile;
SQL> alter system set undo_management='MANUAL' scope=spfile;
SQL> startup mount;
-- 创建新的UNDO表空间
SQL> create undo tablespace undotbs2
datafile '/u01/oradata/undotbs02.dbf' size 10g;
SQL> alter system set undo_tablespace='UNDOTBS2' scope=both;
SQL> alter system set undo_management='AUTO' scope=spfile;
SQL> shutdown immediate;
SQL> startup;
-- 删除旧的UNDO表空间
SQL> drop tablespace undotbs1 including contents and datafiles;
18.5 性能故障排查
18.5.1 性能故障排查流程
性能问题报告
│
▼
[第1步] 确认问题范围
├── 全局性能问题(所有操作慢)
├── 局部性能问题(特定操作慢)
└── 间歇性性能问题(时好时坏)
│
▼
[第2步] 收集性能数据
├── 当前系统状态(v$session, v$system_event)
├── AWR报告(最近1小时)
├── ASH报告(问题时间段)
└── 系统资源(CPU/内存/I/O/网络)
│
▼
[第3步] 分析瓶颈
├── CPU瓶颈(CPU Time > 50%)
├── I/O瓶颈(I/O Wait > 30%)
├── 内存瓶颈(Buffer Cache命中率低)
└── 并发瓶颈(锁等待严重)
│
▼
[第4步] 执行优化
├── SQL优化
├── 索引优化
├── 参数调整
└── 硬件升级
│
▼
[第5步] 验证效果
└── 确认性能指标恢复到正常范围
18.5.2 实时性能监控
sql
-- 查看当前活动会话
SQL> select s.sid, s.serial#, s.username, s.status,
s.sql_id, s.event, s.wait_class,
s.seconds_in_wait, s.state,
q.sql_text
from v$session s, v$sql q
where s.sql_id = q.sql_id
and s.status = 'ACTIVE'
and s.type = 'USER'
order by s.seconds_in_wait desc;
-- 查看Top等待事件
SQL> select event, total_waits, time_waited_micro/1000000 as time_sec,
average_wait/1000 as avg_wait_ms,
wait_class, round(time_waited_micro * 100 /
sum(time_waited_micro) over(), 2) as pct
from v$system_event
where wait_class != 'Idle'
and total_waits > 0
order by time_waited_micro desc;
-- 查看系统资源使用
SQL> select name, value
from v$sysstat
where name in ('DB CPU', 'DB time', 'physical reads',
'physical writes', 'redo size', 'user commits');
-- 查看I/O最繁忙的数据文件
SQL> select df.name, fs.phyrds, fs.phywrts,
fs.singleblkrdtim/1000 as read_time_ms,
fs.writetim/1000 as write_time_ms
from v$datafile df, v$filestat fs
where df.file# = fs.file#
order by fs.phyrds desc
fetch first 10 rows only;
18.5.3 性能问题快速诊断脚本
sql
-- 性能诊断脚本:perf_diag.sql
-- 收集性能问题相关的关键信息
-- 1. 查看当前活动会话数
select count(*) as active_sessions from v$session
where status = 'ACTIVE' and type = 'USER';
-- 2. 查看Top 5等待事件
select * from (
select event, total_waits, time_waited_micro/1000000 as time_sec,
average_wait/1000 as avg_wait_ms
from v$system_event
where wait_class != 'Idle'
order by time_waited_micro desc
) where rownum <= 5;
-- 3. 查看Top 5 SQL(按消耗时间)
select * from (
select sql_id, executions,
round(elapsed_time/1000000, 2) as elapsed_sec,
round(cpu_time/1000000, 2) as cpu_sec,
buffer_gets, disk_reads
from v$sql
where parsing_user_id > 0 and executions > 0
order by elapsed_time desc
) where rownum <= 5;
-- 4. 查看CPU使用率
select value/1000000 as cpu_sec
from v$sysstat where name = 'DB CPU';
-- 5. 查看锁等待
select count(*) as lock_waits
from v$session
where event like 'enq%' or event like 'latch%';
-- 6. 查看I/O等待
select count(*) as io_waits
from v$session
where event like 'db file%' or event like 'direct path%';
18.6 数据损坏处理
18.6.1 数据损坏类型
| 损坏类型 | 说明 | 原因 | 可恢复性 |
|---|---|---|---|
| 物理损坏 | 数据块物理损坏(坏块) | 磁盘故障、存储问题 | 可恢复(有备份) |
| 逻辑损坏 | 数据内容逻辑错误 | Bug、误操作 | 可恢复(有备份) |
| 索引损坏 | 索引结构损坏 | 存储问题、软件Bug | 可重建 |
| 控制文件损坏 | 控制文件损坏 | 磁盘故障 | 可从备份恢复 |
| Redo日志损坏 | 在线重做日志损坏 | 磁盘故障 | 可能丢失数据 |
18.6.2 检测数据损坏
sql
-- 1. 使用DBV(DB Verify)检查数据文件
$ dbv file=/u01/oradata/users01.dbf blocksize=8192
-- 2. 使用RMAN检测坏块
RMAN> backup validate check logical database;
RMAN> backup validate check logical tablespace users;
-- 3. 查看坏块信息
SQL> select * from v$database_block_corruption;
-- 4. 使用ANALYZE检测表损坏
SQL> analyze table scott.emp validate structure cascade;
-- 5. 检测索引损坏
SQL> analyze index pk_emp validate structure;
18.6.3 处理数据块损坏
sql
-- 场景1:有备份的坏块恢复
RMAN> blockrecover datafile 4 block 123, 124;
-- 场景2:表级坏块恢复(使用RMAN)
RMAN> recover datafile 4 block 123;
-- 场景3:使用表迁移绕过坏块
-- 跳过坏块,导出可用数据
SQL> alter system set events '10231 trace name context forever, level 10';
SQL> create table scott.emp_backup as select * from scott.emp;
SQL> alter system set events '10231 trace name context off';
-- 场景4:使用DBMS_REPAIR修复
BEGIN
dbms_repair.admin_tables(
table_name => 'REPAIR_TABLE',
table_type => dbms_repair.repair_table,
action => dbms_repair.create_action);
dbms_repair.check_object(
schema_name => 'SCOTT',
object_name => 'EMP',
repair_table_name => 'REPAIR_TABLE');
dbms_repair.fix_corrupt_blocks(
schema_name => 'SCOTT',
object_name => 'EMP',
repair_table_name => 'REPAIR_TABLE');
END;
/
18.6.4 处理索引损坏
sql
-- 检测索引损坏
SQL> analyze index scott.pk_emp validate structure;
-- 如果报错,说明索引损坏
-- 重建索引
SQL> alter index scott.pk_emp rebuild online;
-- 或使用RMAN重建索引
RMAN> recover index scott.pk_emp;
18.6.5 数据损坏预防措施
- 启用块检查 :设置
db_block_checking和db_block_checksum参数 - 使用ASM:ASM的镜像功能可以防止坏块
- 定期备份:确保有可用的备份
- 监控存储:监控存储设备的健康状态
- 定期校验 :使用RMAN定期执行
backup validate database
sql
-- 启用块完整性检查
SQL> alter system set db_block_checking=MEDIUM scope=both;
SQL> alter system set db_block_checksum=FULL scope=both;
-- 定期验证备份的可恢复性
RMAN> validate backupset all;
18.7 故障报告编写规范
18.7.1 故障报告模板
markdown
# 故障报告
## 基本信息
- **报告编号**:FCR-2024-001
- **报告日期**:2024-06-15
- **报告人**:张三
- **数据库版本**:Oracle 19.19.0.0
- **环境**:生产环境
## 1. 故障概述
(简要描述故障现象和影响范围)
## 2. 故障时间线
- 14:00 - 故障发生
- 14:05 - 收到告警通知
- 14:10 - 开始诊断
- 14:25 - 定位到根因
- 14:30 - 开始恢复
- 14:45 - 恢复完成
- 15:00 - 业务验证通过
## 3. 故障现象
(详细描述故障的临床表现)
- 错误信息:ORA-01653: unable to extend table SCOTT.EMP by 128 in tablespace USERS
- 影响范围:USERS表空间下的所有表无法插入数据
- 影响时间:约45分钟
## 4. 诊断过程
(详细描述诊断步骤和发现)
1. 检查表空间使用率,发现USERS表空间使用率达到100%
2. 检查数据文件,发现数据文件已到达最大大小限制
3. 检查历史增长趋势,发现过去3个月数据增长了30%
## 5. 根因分析
(分析故障的根本原因和直接原因)
- **直接原因**:USERS表空间数据文件达到32GB最大限制
- **根本原因**:未及时监控表空间增长趋势,未在达到阈值前扩容
- **触发条件**:业务批量导入数据触发
## 6. 解决方案
(详细描述恢复操作步骤)
1. 执行扩容操作:ALTER TABLESPACE USERS ADD DATAFILE ...
2. 验证数据可正常插入
## 7. 预防措施
(制定预防类似故障的措施)
1. 设置表空间使用率告警阈值(85%警告,95%严重)
2. 设置数据文件自动扩展(最大限制提高至64GB)
3. 每周检查表空间增长趋势
4. 制定表空间扩容标准流程
## 8. 经验总结
(总结本次故障的经验教训)
- 监控告警阈值需要根据业务增长定期调整
- 自动扩展功能应在所有表空间上启用
- 定期Review监控指标和阈值配置
## 9. 附件
- 告警日志片段
- AWR报告
- 操作日志
18.7.2 故障报告编写原则
- 客观准确:记录事实,不含主观判断
- 完整详细:包含所有关键信息,不遗漏
- 时间线清晰:精确记录每个操作的时间点
- 根因明确:分析根本原因,而非表面现象
- 措施可执行:预防措施具体可执行
- 经验可复用:总结的经验可供其他DBA参考
18.8 事故复盘与改进
18.8.1 复盘流程
事故处理完成
│
▼
[第1步] 收集数据
├── 事故时间线
├── 操作记录
├── 监控数据
└── 相关人员反馈
│
▼
[第2步] 分析根因
├── 5 Whys分析法
├── 故障树分析
└── 根因确定
│
▼
[第3步] 制定改进措施
├── 技术改进
├── 流程改进
└── 人员培训
│
▼
[第4步] 跟踪落实
├── 分配责任人
├── 设定截止日期
└── 验证效果
│
▼
[第5步] 知识沉淀
├── 更新运维手册
├── 更新应急预案
└── 分享经验教训
18.8.2 5 Whys分析法示例
问题: USERS表空间满了导致数据库挂起
Why 1: 为什么数据库挂起?
→ 因为USERS表空间满了,无法扩展
Why 2: 为什么USERS表空间满了?
→ 因为数据文件达到32GB最大限制,且未启用自动扩展
Why 3: 为什么未启用自动扩展?
→ 因为创建表空间时未配置AUTOEXTEND ON
Why 4: 为什么创建时未配置?
→ 因为没有标准表空间创建规范
Why 5: 为什么没有标准规范?
→ 因为团队缺乏数据库空间管理规范
根因:缺乏数据库空间管理规范和标准配置
18.8.3 改进措施跟踪
| 改进措施 | 责任人 | 截止日期 | 状态 |
|---|---|---|---|
| 制定表空间创建规范 | 张三 | 2024-06-30 | 进行中 |
| 配置所有表空间的自动扩展 | 李四 | 2024-06-25 | 已完成 |
| 设置表空间使用率告警 | 王五 | 2024-06-20 | 已完成 |
| 建立空间增长趋势分析机制 | 张三 | 2024-07-15 | 待开始 |
| 更新运维手册 | 李四 | 2024-07-30 | 待开始 |
18.8.4 复盘会议议程
- 事故回顾:简要回顾事故处理过程(5分钟)
- 时间线确认:确认事故时间线的准确性(10分钟)
- 根因分析:讨论根因分析结果(15分钟)
- 改进措施:讨论并确定改进措施(15分钟)
- 责任分配:分配改进措施的责任人(10分钟)
- 总结:总结经验和教训(5分钟)
18.8.5 复盘后的改进方向
- 技术层面:完善监控告警机制、自动化运维流程
- 流程层面:建立变更管理、配置管理、容量管理规范
- 人员层面:加强DBA技能培训、建立知识库
第九部分:高级主题
第19章 自动化运维
自动化运维是现代DBA的核心能力之一。通过自动化工具和脚本,DBA可以减少重复性工作、降低人为错误、提高运维效率。本章将介绍Oracle自动化框架、脚本化运维、Ansible和Terraform等主流自动化工具的使用方法。
19.1 Oracle自动化框架
19.1.1 Oracle自动化框架概述
Oracle提供了多种内置的自动化框架,帮助DBA简化日常运维任务:
| 框架 | 用途 | 版本 |
|---|---|---|
| DBMS_SCHEDULER | 作业调度、定时任务 | 10g+ |
| Oracle Enterprise Manager(EM) | 统一监控、管理平台 | 10g+ |
| Database Lifecycle Management(DBLM) | 数据库生命周期管理 | 12c+ |
| Automatic Maintenance Tasks | 自动维护任务(统计信息、SQL优化等) | 10g+ |
| Autonomous Health Framework(AHF) | 自动健康检查、问题诊断 | 12c+ |
| Database Fleet Management | 大规模数据库集群管理 | 18c+ |
| Performance Hub | 统一性能管理平台 | 19c+ |
19.1.2 自动维护任务管理
sql
-- 查看自动维护任务配置
SQL> select client_name, status, description
from dba_autotask_client;
-- 查看自动维护窗口
SQL> select window_name, repeat_interval, duration, enabled
from dba_scheduler_windows
where enabled = 'TRUE';
-- 禁用某个自动维护任务
SQL> exec dbms_auto_task_admin.disable(
client_name => 'sql tuning advisor',
operation => NULL,
window_name => NULL);
-- 启用自动维护任务
SQL> exec dbms_auto_task_admin.enable(
client_name => 'sql tuning advisor');
19.1.3 使用EM CLI实现自动化
bash
# EM CLI(Enterprise Manager Command Line Interface)
# 登录EM CLI
$ emcli login -username=sysman -password=password
# 同步状态
$ emcli sync
# 批量执行作业
$ emcli create_job \
-name="Daily_RMAN_Backup" \
-type="OSCommand" \
-targets="oracle_database:ORCL" \
-schedule="FREQ=DAILY;BYHOUR=1" \
-command="/u01/scripts/rman_backup.sh"
# 查看作业状态
$ emcli get_jobs -name="Daily_RMAN_Backup"
# 获取数据库配置
$ emcli get_targets -targets="oracle_database"
19.1.4 使用REST API自动化
Oracle Enterprise Manager 13.5+支持REST API,可以集成到自动化流水线中。
bash
# 使用curl调用EM REST API
# 获取数据库列表
$ curl -k -u sysman:password \
-H "Content-Type: application/json" \
https://em_host:7803/em/websvcs/restful/emws/databases
# 获取数据库性能指标
$ curl -k -u sysman:password \
-H "Content-Type: application/json" \
https://em_host:7803/em/websvcs/restful/emws/databases/ORCL/performance
# 触发数据库备份
$ curl -k -u sysman:password \
-X POST \
-H "Content-Type: application/json" \
-d '{"backup_type":"FULL","target":"ORCL"}' \
https://em_host:7803/em/websvcs/restful/emws/databases/ORCL/backup
19.1.5 Autonomous Health Framework(AHF)
bash
# 安装AHF
$ wget https://<oracle_support>/ahf.zip
$ unzip ahf.zip -d /u01/app/oracle
$ cd /u01/app/oracle/ahf
$ ./ahf_setup
# 使用TFA(Trace File Analyzer)收集诊断信息
$ tfactl diag collect -database ORCL
# 使用ORACHK进行健康检查
$ orachk -database ORCL
# 使用EXAChk进行Exadata检查
$ exachk
# 查看AHF配置
$ tfactl status
$ tfactl printconfig
19.2 脚本化运维
19.2.1 脚本化运维的重要性
脚本化是自动化运维的基础。通过编写脚本,DBA可以将重复性工作自动化,并确保操作的一致性和可追溯性。
脚本化运维的优势:
- 减少人为错误,操作一致性高
- 批量处理,提高效率
- 可审计,所有操作记录在日志中
- 可复用,一处编写,多处使用
- 可集成,易于集成到CI/CD流水线
19.2.2 Shell脚本库
数据库健康检查脚本:
bash
#!/bin/bash
# 文件名:db_health_check.sh
# 功能:综合数据库健康检查
ORACLE_SID=$1
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
export ORACLE_SID ORACLE_HOME
LOG_FILE=/u01/logs/health_check_${ORACLE_SID}_$(date +%Y%m%d).log
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
log "开始健康检查 - $ORACLE_SID"
# 1. 检查实例状态
INST_STATUS=$(sqlplus -s / as sysdba <<EOF
set pages 0 lines 200 feedback off
select status from v\$instance;
EOF
)
if [ "$INST_STATUS" = "OPEN" ]; then
log "[OK] 实例状态: OPEN"
else
log "[FAIL] 实例状态: $INST_STATUS"
fi
# 2. 检查表空间使用率
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
set pages 0 lines 200 feedback off
select tablespace_name || ':' || round(used_pct, 2) || '%' as alert
from (
select df.tablespace_name,
(1 - nvl(fs.free_bytes,0) / df.total_bytes) * 100 as used_pct
from (select tablespace_name, sum(bytes) as total_bytes
from dba_data_files group by tablespace_name) df,
(select tablespace_name, sum(bytes) as free_bytes
from dba_free_space group by tablespace_name) fs
where df.tablespace_name = fs.tablespace_name(+)
)
where used_pct > 85;
EOF
# 3. 检查归档日志空间
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
set pages 0 lines 200 feedback off
select 'FRA:' || round(percent_space_used, 2) || '%'
from v\$flash_recovery_area_usage
where percent_space_used > 85;
EOF
# 4. 检查无效对象
INVALID_CNT=$(sqlplus -s / as sysdba <<EOF
set pages 0 lines 200 feedback off
select count(*) from dba_objects where status = 'INVALID';
EOF
)
if [ "$INVALID_CNT" -gt 0 ]; then
log "[WARN] 无效对象数量: $INVALID_CNT"
else
log "[OK] 无无效对象"
fi
# 5. 检查RMAN备份
sqlplus -s / as sysdba <<EOF >> $LOG_FILE
set pages 0 lines 200 feedback off
select '备份:' || status || ' - ' || to_char(end_time, 'YYYY-MM-DD HH24:MI')
from v\$rman_backup_job_details
where rownum = 1
order by end_time desc;
EOF
log "健康检查完成"
数据库备份脚本:
bash
#!/bin/bash
# 文件名:rman_backup.sh
# 功能:RMAN增量备份,备份后清理归档日志
ORACLE_SID=$1
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
BACKUP_DIR=/backup/${ORACLE_SID}
LOG_FILE=/u01/logs/rman_backup_${ORACLE_SID}_$(date +%Y%m%d_%H%M%S).log
BACKUP_TAG="${ORACLE_SID}_$(date +%Y%m%d_%H%M%S)"
export ORACLE_SID ORACLE_HOME
mkdir -p $BACKUP_DIR
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a $LOG_FILE
}
log "开始备份 - $ORACLE_SID"
# 检查是否应该执行全量备份(周日全量,其他增量)
DOW=$(date +%u)
if [ "$DOW" -eq 7 ]; then
BACKUP_TYPE="FULL DATABASE"
BACKUP_LEVEL=""
log "执行全量备份"
else
INCREMENTAL_LEVEL=1
BACKUP_TYPE="INCREMENTAL LEVEL 1 DATABASE"
BACKUP_LEVEL="INCREMENTAL LEVEL 1"
log "执行增量备份 (Level 1)"
fi
# 执行RMAN备份
rman target / log=$LOG_FILE append <<EOF
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
CONFIGURE CONTROLFILE AUTOBACKUP ON;
CONFIGURE CONTROLFILE AUTOBACKUP FORMAT FOR DEVICE TYPE DISK TO '${BACKUP_DIR}/%F';
RUN {
ALLOCATE CHANNEL ch1 DEVICE TYPE DISK FORMAT '${BACKUP_DIR}/%U_%T';
SQL 'ALTER SYSTEM ARCHIVE LOG CURRENT';
BACKUP $BACKUP_TYPE
TAG '$BACKUP_TAG'
PLUS ARCHIVELOG DELETE INPUT
FORMAT '${BACKUP_DIR}/%U_%T';
RELEASE CHANNEL ch1;
}
DELETE NOPROMPT OBSOLETE;
CROSSCHECK BACKUP;
DELETE NOPROMPT EXPIRED BACKUP;
LIST BACKUP SUMMARY;
EOF
if [ $? -eq 0 ]; then
log "备份完成: $BACKUP_TAG"
else
log "[ERROR] 备份失败,请检查日志: $LOG_FILE"
exit 1
fi
19.2.3 Python脚本库
使用Python cx_Oracle管理数据库:
python
#!/usr/bin/env python3
# 文件名:db_monitor.py
# 功能:Python数据库监控脚本
import cx_Oracle
import json
import datetime
class OracleMonitor:
def __init__(self, dsn, user, password):
self.dsn = dsn
self.user = user
self.password = password
self.conn = None
def connect(self):
"""连接数据库"""
self.conn = cx_Oracle.connect(
self.user, self.password, self.dsn)
return self.conn
def close(self):
"""关闭连接"""
if self.conn:
self.conn.close()
def get_tablespace_usage(self):
"""获取表空间使用率"""
cursor = self.conn.cursor()
cursor.execute("""
SELECT df.tablespace_name,
ROUND(df.total_bytes / 1024 / 1024, 2) AS total_mb,
ROUND(NVL(fs.free_bytes, 0) / 1024 / 1024, 2) AS free_mb,
ROUND((1 - NVL(fs.free_bytes, 0) /
NULLIF(df.total_bytes, 0)) * 100, 2) AS used_pct
FROM (SELECT tablespace_name, SUM(bytes) AS total_bytes
FROM dba_data_files GROUP BY tablespace_name) df,
(SELECT tablespace_name, SUM(bytes) AS free_bytes
FROM dba_free_space GROUP BY tablespace_name) fs
WHERE df.tablespace_name = fs.tablespace_name(+)
ORDER BY used_pct DESC
""")
result = []
for row in cursor:
result.append({
'tablespace': row[0],
'total_mb': row[1],
'free_mb': row[2],
'used_pct': row[3]
})
cursor.close()
return result
def get_top_sql(self, top_n=10):
"""获取Top SQL"""
cursor = self.conn.cursor()
cursor.execute(f"""
SELECT sql_id, executions,
ROUND(elapsed_time / 1000000, 2) AS elapsed_sec,
ROUND(cpu_time / 1000000, 2) AS cpu_sec,
buffer_gets, disk_reads,
SUBSTR(sql_text, 1, 200) AS sql_text
FROM v$sql
WHERE parsing_user_id > 0 AND executions > 0
ORDER BY elapsed_time DESC
FETCH FIRST {top_n} ROWS ONLY
""")
result = []
for row in cursor:
result.append({
'sql_id': row[0],
'executions': row[1],
'elapsed_sec': row[2],
'cpu_sec': row[3],
'buffer_gets': row[4],
'disk_reads': row[5],
'sql_text': row[6]
})
cursor.close()
return result
def get_session_info(self):
"""获取活动会话信息"""
cursor = self.conn.cursor()
cursor.execute("""
SELECT sid, serial#, username, status, sql_id,
event, wait_class, seconds_in_wait
FROM v$session
WHERE status = 'ACTIVE' AND type = 'USER'
ORDER BY seconds_in_wait DESC
""")
result = []
for row in cursor:
result.append({
'sid': row[0],
'serial': row[1],
'username': row[2],
'status': row[3],
'sql_id': row[4],
'event': row[5],
'wait_class': row[6],
'seconds_in_wait': row[7]
})
cursor.close()
return result
def generate_report(self):
"""生成监控报告(JSON格式)"""
report = {
'timestamp': datetime.datetime.now().isoformat(),
'tablespace_usage': self.get_tablespace_usage(),
'top_sql': self.get_top_sql(),
'active_sessions': self.get_session_info()
}
return json.dumps(report, indent=2, ensure_ascii=False)
# 使用示例
if __name__ == '__main__':
monitor = OracleMonitor('localhost:1521/ORCL', 'system', 'password')
monitor.connect()
report = monitor.generate_report()
print(report)
monitor.close()
19.2.4 脚本化运维最佳实践
- 模块化设计:将常用功能封装成独立函数或模块
- 参数化:通过参数控制脚本行为,避免硬编码
- 日志记录:详细记录操作过程和结果
- 错误处理:完善的异常捕获和错误处理
- 幂等性:多次执行脚本结果一致
- 版本管理:脚本纳入Git版本管理
- 测试验证:在测试环境验证后再在生产环境执行
19.3 Ansible与Oracle集成
19.3.1 Ansible概述
Ansible是一款开源的自动化运维工具,通过SSH协议管理远程主机,无需在目标主机上安装Agent。Ansible使用YAML格式的Playbook定义自动化任务,简单易用。
Ansible在Oracle运维中的典型应用场景:
- Oracle软件安装和配置
- 数据库创建和参数配置
- 补丁安装和升级
- 日常运维任务自动化
- 批量配置管理
19.3.2 Ansible环境配置
bash
# 安装Ansible
$ pip install ansible
# 验证安装
$ ansible --version
# 创建Ansible目录结构
$ mkdir -p oracle_ansible/{inventory,playbooks,roles,group_vars,host_vars}
# 配置inventory文件
$ cat > oracle_ansible/inventory/hosts.ini <<EOF
[oracle_databases]
db1 ansible_host=192.168.1.101 ansible_user=oracle
db2 ansible_host=192.168.1.102 ansible_user=oracle
[oracle_servers:children]
oracle_databases
[oracle_servers:vars]
ansible_python_interpreter=/usr/bin/python3
EOF
19.3.3 Oracle运维Playbook
Playbook 1:数据库健康检查
yaml
---
# 文件名:db_health_check.yml
# 功能:批量数据库健康检查
- name: Oracle Database Health Check
hosts: oracle_databases
gather_facts: no
vars:
oracle_home: /u01/app/oracle/product/19.0.0/dbhome_1
oracle_sid: ORCL
tasks:
- name: 检查实例状态
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
echo "SELECT status FROM v\$instance;" | sqlplus -s / as sysdba
register: instance_status
- name: 输出实例状态
debug:
msg: "实例状态: {{ instance_status.stdout }}"
- name: 检查表空间使用率
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
sqlplus -s / as sysdba <<SQL
set pages 0 lines 200 feedback off
select tablespace_name || ':' || round(used_pct, 2) || '%'
from (
select df.tablespace_name,
(1 - nvl(fs.free_bytes,0) / df.total_bytes) * 100 as used_pct
from (select tablespace_name, sum(bytes) as total_bytes
from dba_data_files group by tablespace_name) df,
(select tablespace_name, sum(bytes) as free_bytes
from dba_free_space group by tablespace_name) fs
where df.tablespace_name = fs.tablespace_name(+)
)
where used_pct > 85;
SQL
register: tablespace_usage
ignore_errors: yes
- name: 输出表空间使用率
debug:
msg: "表空间使用率告警: {{ tablespace_usage.stdout_lines }}"
- name: 检查无效对象
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
echo "SELECT count(*) FROM dba_objects WHERE status = 'INVALID';" | sqlplus -s / as sysdba
register: invalid_objects
- name: 输出无效对象
debug:
msg: "无效对象数量: {{ invalid_objects.stdout }}"
- name: 检查归档日志空间
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
sqlplus -s / as sysdba <<SQL
set pages 0 lines 200 feedback off
select 'FRA使用率:' || round(percent_space_used, 2) || '%'
from v\$flash_recovery_area_usage;
SQL
register: fra_usage
- name: 输出归档空间
debug:
msg: "{{ fra_usage.stdout_lines }}"
Playbook 2:批量配置数据库参数
yaml
---
# 文件名:db_config_params.yml
# 功能:批量配置数据库参数
- name: Configure Oracle Database Parameters
hosts: oracle_databases
gather_facts: no
vars:
oracle_home: /u01/app/oracle/product/19.0.0/dbhome_1
oracle_sid: ORCL
db_params:
- { name: "session_cached_cursors", value: "200" }
- { name: "open_cursors", value: "1000" }
- { name: "db_block_checking", value: "MEDIUM" }
- { name: "db_block_checksum", value: "TYPICAL" }
- { name: "audit_trail", value: "DB" }
tasks:
- name: 设置数据库参数
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
sqlplus -s / as sysdba <<SQL
alter system set {{ item.name }}={{ item.value }} scope=both;
SQL
loop: "{{ db_params }}"
register: param_result
ignore_errors: yes
- name: 输出参数设置结果
debug:
msg: "设置 {{ item.item.name }} = {{ item.item.value }} - {{ '完成' if item.rc == 0 else '失败' }}"
loop: "{{ param_result.results }}"
- name: 验证参数设置
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
sqlplus -s / as sysdba <<SQL
set pages 0 lines 200 feedback off
select name || '=' || value from v\$parameter
where name in ('session_cached_cursors', 'open_cursors',
'db_block_checking', 'db_block_checksum', 'audit_trail');
SQL
register: verify_params
- name: 输出参数验证结果
debug:
msg: "{{ verify_params.stdout_lines }}"
Playbook 3:RMAN备份自动化
yaml
---
# 文件名:rman_backup.yml
# 功能:自动化RMAN备份
- name: Oracle RMAN Backup Automation
hosts: oracle_databases
gather_facts: no
vars:
oracle_home: /u01/app/oracle/product/19.0.0/dbhome_1
oracle_sid: ORCL
backup_dir: /backup/{{ oracle_sid }}
backup_tag: "{{ oracle_sid }}_{{ ansible_date_time.date }}"
tasks:
- name: 创建备份目录
file:
path: "{{ backup_dir }}"
state: directory
mode: '0755'
- name: 执行RMAN备份
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
rman target / <<EOF
RUN {
ALLOCATE CHANNEL ch1 DEVICE TYPE DISK FORMAT '{{ backup_dir }}/%U_%T';
BACKUP INCREMENTAL LEVEL 1 DATABASE TAG '{{ backup_tag }}'
PLUS ARCHIVELOG DELETE INPUT;
RELEASE CHANNEL ch1;
}
DELETE NOPROMPT OBSOLETE;
LIST BACKUP SUMMARY;
EOF
register: rman_result
ignore_errors: yes
- name: 输出备份结果
debug:
msg: "{{ rman_result.stdout_lines[-5:] }}"
- name: 检查备份是否成功
shell: |
export ORACLE_HOME={{ oracle_home }}
export ORACLE_SID={{ oracle_sid }}
sqlplus -s / as sysdba <<SQL
set pages 0 lines 200 feedback off
select status from v\$rman_backup_job_details
where rownum = 1 order by end_time desc;
SQL
register: backup_status
- name: 发送备份通知
debug:
msg: >
{% if 'COMPLETED' in backup_status.stdout %}
[OK] 备份成功 - {{ inventory_hostname }}
{% else %}
[FAIL] 备份失败 - {{ inventory_hostname }}
{% endif %}
19.3.4 Ansible Roles
yaml
# 创建Oracle Role目录结构
$ mkdir -p roles/oracle_db/{tasks,handlers,vars,templates,files}
# 角色主文件:roles/oracle_db/tasks/main.yml
---
- name: 设置Oracle环境变量
include_tasks: set_env.yml
- name: 配置数据库参数
include_tasks: config_params.yml
- name: 设置自动备份
include_tasks: setup_backup.yml
- name: 配置监控
include_tasks: setup_monitoring.yml
# 使用Role的Playbook
---
- name: Oracle Database Management
hosts: oracle_databases
roles:
- oracle_db
19.3.5 Ansible最佳实践
- 使用Ansible Vault加密敏感信息:
bash
# 加密密码文件
$ ansible-vault encrypt group_vars/all/vault.yml
# 运行Playbook时输入密码
$ ansible-playbook -i inventory/hosts.ini playbooks/db_health_check.yml --ask-vault-pass
- 使用Tags管理任务:
yaml
- name: 检查表空间
shell: ...
tags: [check, tablespace]
- name: 检查备份
shell: ...
tags: [check, backup]
bash
# 只执行tags为check的任务
$ ansible-playbook playbooks/db_health_check.yml --tags "check"
- 使用Check模式验证:
bash
# 不实际执行,只输出变更
$ ansible-playbook playbooks/db_config_params.yml --check --diff
19.4 Terraform管理Oracle云资源
19.4.1 Terraform概述
Terraform是HashiCorp开发的基础设施即代码(IaC)工具,通过声明式配置管理云资源。在Oracle环境中,Terraform可以管理OCI(Oracle Cloud Infrastructure)资源。
Terraform的优势:
- 声明式配置,代码化管理基础设施
- 支持OCI、AWS、Azure等多云环境
- 状态管理,可追踪资源变更历史
- 模块化,可复用资源配置
- 与CI/CD流水线无缝集成
19.4.2 Terraform配置OCI Provider
hcl
# 文件名:provider.tf
# 配置OCI Provider
terraform {
required_version = ">= 1.0"
required_providers {
oci = {
source = "oracle/oci"
version = ">= 5.0.0"
}
}
}
# 配置OCI Provider
provider "oci" {
tenancy_ocid = var.tenancy_ocid
user_ocid = var.user_ocid
fingerprint = var.fingerprint
private_key_path = var.private_key_path
region = var.region
}
19.4.3 创建OCI数据库资源
hcl
# 文件名:variables.tf
variable "tenancy_ocid" {
description = "Tenancy OCID"
type = string
}
variable "user_ocid" {
description = "User OCID"
type = string
sensitive = true
}
variable "fingerprint" {
description = "API Key Fingerprint"
type = string
}
variable "private_key_path" {
description = "Private Key Path"
type = string
}
variable "region" {
description = "OCI Region"
type = string
default = "ap-chuncheon-1"
}
variable "compartment_ocid" {
description = "Compartment OCID"
type = string
}
# 文件名:networking.tf
# 创建VCN和子网
resource "oci_core_virtual_network" "db_vcn" {
cidr_block = "10.0.0.0/16"
compartment_id = var.compartment_ocid
display_name = "db-vcn"
dns_label = "dbvcn"
}
resource "oci_core_subnet" "db_subnet" {
cidr_block = "10.0.1.0/24"
compartment_id = var.compartment_ocid
vcn_id = oci_core_virtual_network.db_vcn.id
display_name = "db-subnet"
dns_label = "dbsubnet"
security_list_ids = [oci_core_security_list.db_security_list.id]
}
resource "oci_core_security_list" "db_security_list" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_virtual_network.db_vcn.id
display_name = "db-security-list"
ingress_security_rules {
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
min = 1521
max = 1522
}
}
egress_security_rules {
protocol = "all"
destination = "0.0.0.0/0"
}
}
# 文件名:database.tf
# 创建Oracle数据库
resource "oci_database_db_system" "oracle_db" {
availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
compartment_id = var.compartment_ocid
database_edition = "ENTERPRISE_EDITION_EXTREME_PERFORMANCE"
db_home {
database {
admin_password = var.db_admin_password
db_name = "ORCL"
db_workload = "OLTP"
character_set = "AL32UTF8"
ncharacter_set = "AL16UTF16"
db_backup_config {
auto_backup_enabled = true
recovery_window_in_days = 7
}
}
db_version = "19.0.0.0"
display_name = "db-home-19c"
}
db_system_options {
storage_management = "ASM"
}
display_name = "Oracle-DB-System"
hostname = "oradb01"
shape = "VM.Standard.E4.Flex"
cpu_core_count = 4
data_storage_size_in_gb = 256
license_model = "LICENSE_INCLUDED"
node_count = 1
ssh_public_keys = file(var.ssh_public_key_path)
subnet_id = oci_core_subnet.db_subnet.id
nsg_ids = [oci_core_network_security_group.db_nsg.id]
timeouts {
create = "120m"
update = "60m"
delete = "60m"
}
}
# 创建网络安全组
resource "oci_core_network_security_group" "db_nsg" {
compartment_id = var.compartment_ocid
vcn_id = oci_core_virtual_network.db_vcn.id
display_name = "db-nsg"
}
resource "oci_core_network_security_group_security_rule" "db_nsg_rule" {
network_security_group_id = oci_core_network_security_group.db_nsg.id
direction = "INGRESS"
protocol = "6"
source = "0.0.0.0/0"
tcp_options {
min = 1521
max = 1522
}
}
# 获取可用域
data "oci_identity_availability_domains" "ads" {
compartment_id = var.tenancy_ocid
}
19.4.4 Terraform状态管理
hcl
# 文件名:backend.tf
# 使用OCI Object Storage存储状态文件
terraform {
backend "s3" {
bucket = "terraform-state-bucket"
key = "oracle-db/terraform.tfstate"
region = "ap-chuncheon-1"
# 使用OCI兼容的S3 API
endpoints = {
s3 = "https://compat.objectstorage.ap-chuncheon-1.oraclecloud.com"
}
skip_region_validation = true
skip_credentials_validation = true
skip_metadata_api_check = true
force_path_style = true
}
}
19.4.5 Terraform常用命令
bash
# 初始化Terraform
$ terraform init
# 格式化配置文件
$ terraform fmt
# 验证配置语法
$ terraform validate
# 查看执行计划
$ terraform plan
# 应用配置(创建资源)
$ terraform apply -auto-approve
# 查看资源状态
$ terraform show
# 查看资源列表
$ terraform state list
# 销毁资源
$ terraform destroy -auto-approve
19.4.6 Terraform最佳实践
- 使用模块化设计:将常用资源配置封装为模块
- 远程状态存储:使用OCI Object Storage存储状态文件
- 版本管理:将Terraform配置文件纳入Git管理
- 变量分离:使用变量文件管理不同环境的配置差异
- 标签管理:为所有资源添加标签,便于资源分类和成本管理
- 生命周期管理 :使用
prevent_destroy保护关键资源
19.5 运维知识沉淀
19.5.1 知识沉淀的重要性
运维知识沉淀是将运维过程中积累的经验、教训、最佳实践系统化记录和传承的过程。良好的知识沉淀机制可以:
- 降低对个人的依赖,提升团队整体能力
- 避免重复踩坑,提高运维效率
- 加速新人培养,缩短上手时间
- 形成标准化流程,提升运维质量
19.5.2 知识库建设
知识库分类:
| 分类 | 内容 | 更新频率 |
|---|---|---|
| 标准操作流程(SOP) | 巡检、备份、扩容等标准操作步骤 | 按需更新 |
| 故障处理手册 | 常见故障的诊断和处理步骤 | 故障后更新 |
| 变更记录 | 所有数据库变更的详细记录 | 每次变更后 |
| 配置清单 | 数据库配置、参数、权限等 | 配置更改后 |
| 架构文档 | 数据库架构图、数据流向 | 架构变更后 |
| 学习资源 | 培训资料、技术文章、MOS文档 | 持续更新 |
使用Git管理知识库:
bash
# 创建运维知识库
$ mkdir -p dba-knowledge-base
$ cd dba-knowledge-base
$ git init
# 创建目录结构
$ mkdir -p {sop,troubleshooting,changes,configs,architecture,learning}
# 创建SOP文件
$ cat > sop/backup_check.md <<EOF
# 备份检查SOP
## 检查项目
1. 检查RMAN备份状态
2. 检查备份文件完整性
3. 检查归档日志清理状态
4. 检查备份文件空间使用
## 操作步骤
1. 登录数据库服务器
2. 执行以下SQL检查备份状态
\`\`\`sql
select session_key, status, start_time, end_time, input_type
from v\$rman_backup_job_details
where start_time > sysdate - 1
order by start_time desc;
\`\`\`
3. ...
## 异常处理
- 如果备份失败,立即检查并通知DBA
- 如果备份空间不足,执行扩容
## 更新记录
- 2024-06-01 创建文档
EOF
# 提交到Git
$ git add .
$ git commit -m "初始化运维知识库"
19.5.3 自动化文档生成
bash
#!/bin/bash
# 文件名:gen_doc.sh
# 功能:自动生成数据库配置文档
ORACLE_SID=$1
ORACLE_HOME=/u01/app/oracle/product/19.0.0/dbhome_1
DOC_DIR=/u01/docs/${ORACLE_SID}
mkdir -p $DOC_DIR
export ORACLE_SID ORACLE_HOME
# 生成数据库配置报告
sqlplus -s / as sysdba <<EOF > $DOC_DIR/db_config_${ORACLE_SID}.txt
set pages 1000 lines 200 feedback off
prompt ====================================
prompt 数据库配置报告 - $ORACLE_SID
prompt 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
prompt ====================================
prompt
prompt 1. 数据库基本信息
select name, db_unique_name, database_role, open_mode, log_mode, platform_name, version
from v\$database, v\$instance;
prompt
prompt 2. 内存配置
select name, value/1024/1024 as value_mb
from v\$parameter
where name in ('sga_target', 'sga_max_size', 'pga_aggregate_target',
'shared_pool_size', 'db_cache_size', 'large_pool_size',
'java_pool_size', 'streams_pool_size');
prompt
prompt 3. 表空间配置
select tablespace_name, round(sum(bytes)/1024/1024, 2) as size_mb,
round(sum(decode(autoextensible, 'YES', maxbytes, bytes))/1024/1024, 2) as max_mb,
count(*) as datafile_count
from dba_data_files
group by tablespace_name
order by tablespace_name;
prompt
prompt 4. 数据文件配置
select file_name, tablespace_name, round(bytes/1024/1024, 2) as size_mb,
autoextensible, round(maxbytes/1024/1024, 2) as max_mb
from dba_data_files
order by tablespace_name, file_name;
prompt
prompt 5. 控制文件配置
select name, status, is_recovery_dest_file
from v\$controlfile;
prompt
prompt 6. Redo日志配置
select group#, thread#, sequence#, members, bytes/1024/1024 as size_mb, status
from v\$log;
prompt
prompt 7. 参数文件
select name, value
from v\$parameter
where name in ('compatible', 'db_block_size', 'db_domain', 'db_unique_name',
'diagnostic_dest', 'audit_trail', 'undo_tablespace',
'db_recovery_file_dest', 'db_recovery_file_dest_size',
'remote_login_passwordfile', 'parallel_max_servers',
'processes', 'sessions');
EOF
echo "文档已生成: $DOC_DIR/db_config_${ORACLE_SID}.txt"
19.5.4 变更管理流程
┌──────────────────────────────────────────────────────┐
│ 变更申请 │
│ (描述变更内容、原因、影响范围、回退方案) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 变更评审 │
│ (技术评审、风险评估、审批) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 变更计划 │
│ (详细操作步骤、执行时间、预期结果) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 变更执行 │
│ (按计划执行,记录操作日志) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 变更验证 │
│ (验证变更结果,确认无异常) │
└──────────────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ 变更关闭 │
│ (更新文档,归档记录) │
└──────────────────────────────────────────────────────┘
19.5.5 运维知识沉淀最佳实践
- 文档即代码:使用Markdown格式编写文档,纳入Git版本管理
- SOP标准化:所有重复性操作都编写标准操作流程
- 故障复盘记录:每次故障处理完成后,记录故障报告和经验教训
- 定期分享:定期组织技术分享会,促进知识交流和沉淀
- 自动化文档:使用脚本自动生成数据库配置文档,确保文档与实际一致
- 知识库维护:指定专人维护知识库,定期审查和更新
- 新人培训:利用知识库中的文档和SOP加速新人培训
- 持续改进:定期回顾运维流程,发现改进空间
第20章 Oracle云数据库
随着云计算的普及,Oracle数据库在云上的部署方式越来越多样化。本章将介绍Oracle Cloud Infrastructure(OCI)的基础服务、Autonomous Database自治数据库、云迁移策略以及混合云架构,帮助DBA掌握云时代的数据库运维技能。
20.1 Oracle Cloud Infrastructure(OCI)
20.1.1 OCI概述
Oracle Cloud Infrastructure(OCI)是Oracle提供的公有云平台,提供计算、存储、网络、数据库等云服务。OCI以其高性能、高安全性和企业级特性著称。
OCI的核心特性:
- 高性能:基于裸金属和RDMA网络,提供极致性能
- 高可用:跨可用域(Availability Domain)部署,SLA达99.99%
- 安全:默认加密,安全隔离,零信任架构
- 开放:兼容多种操作系统和云原生工具
- 企业级:支持Oracle数据库、RAC、Exadata等企业级特性
20.1.2 OCI全球基础设施
| 概念 | 说明 | 对应关系 |
|---|---|---|
| Region(区域) | 地理区域,包含多个可用域 | 如:ap-chuncheon-1 |
| Availability Domain(AD) | 可用域,独立的物理数据中心 | 一个Region包含1-3个AD |
| Fault Domain(FD) | 故障域,AD内的物理分组 | 每个AD包含3个FD |
| Compartment | 资源隔离单元,用于访问控制 | 类似账户或项目 |
OCI全球Region分布:
- 亚太:东京、首尔、春川、孟买、新加坡、悉尼等
- 美洲:美东、美西、圣保罗等
- 欧洲:法兰克福、伦敦、苏黎世等
- 中东:迪拜、吉达等
- 中国:托管在合作伙伴数据中心的区域
20.1.3 OCI数据库服务
| 服务名称 | 说明 | 适用场景 |
|---|---|---|
| Bare Metal DB | 裸金属服务器上的Oracle DB | 高性能OLTP,RAC |
| Virtual Machine DB | 虚拟机上的Oracle DB | 中小型数据库 |
| Exadata DB | Exadata云服务 | 高性能OLTP/OLAP混合负载 |
| Autonomous DB | 自治数据库 | 自动化运维,弹性扩展 |
| MySQL HeatWave | MySQL + 内存分析 | Web应用,分析查询 |
| NoSQL DB | 分布式NoSQL数据库 | IoT,实时应用 |
20.1.4 OCI基础配置
bash
# 使用OCI CLI管理资源
# 安装OCI CLI
$ bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)"
# 配置OCI CLI
$ oci setup config
# 列出所有可用域
$ oci iam availability-domain list
# 列出所有数据库系统
$ oci db system list -c <compartment_ocid>
# 查看数据库系统详情
$ oci db system get --db-system-id <db_system_ocid>
# 创建数据库
$ oci db database create \
--db-system-id <db_system_ocid> \
--admin-password <password> \
--db-name ORCL \
--db-version 19.0.0.0
# 列出数据库备份
$ oci db backup list -c <compartment_ocid>
20.1.5 OCI网络与安全
hcl
# 使用Terraform配置VCN子网(参考19.4节)
# OCI安全最佳实践:
# 1. 使用堡垒机访问数据库
$ oci bastion session create \
--bastion-id <bastion_ocid> \
--target-resource-id <db_ocid> \
--session-type PORT_FORWARDING \
--session-ttl 3600
# 2. 配置网络安全组(NSG)
# 限制数据库1521端口的访问源
$ oci network nsg update \
--nsg-id <nsg_ocid> \
--defined-tags '{"Operations": {"Environment": "Production"}}'
# 3. 启用数据库审计日志
SQL> alter system set audit_trail = DB,EXTENDED scope=both;
20.1.6 OCI成本管理
bash
# 查看资源使用情况
$ oci usage-cpi usagestatement list \
-c <compartment_ocid> \
--from-time 2024-01-01 \
--to-time 2024-01-31
# 设置预算告警
$ oci budget create \
-c <compartment_ocid> \
--amount 1000 \
--description "Monthly DB budget" \
--alert-rule-query "Actual Spend > 800" \
--threshold 80 \
--threshold-type PERCENTAGE
# 使用标签管理资源成本
$ oci db system update \
--db-system-id <db_system_ocid> \
--defined-tags '{"CostCenter": {"Department": "DBA"}}'
20.2 Autonomous Database
20.2.1 Autonomous Database概述
Oracle Autonomous Database是Oracle推出的自治数据库服务,通过机器学习技术实现数据库的自动管理,包括自动备份、自动调优、自动扩展、自动修复等功能。
Autonomous Database的类型:
| 类型 | 全称 | 适用场景 |
|---|---|---|
| ADW | Autonomous Data Warehouse | 数据仓库,分析查询 |
| ATP | Autonomous Transaction Processing | OLTP,事务处理 |
| AJDE | Autonomous JSON Database | JSON文档存储 |
| APEX | Autonomous APEX | 低代码应用开发 |
Autonomous Database的核心特性:
- 自助服务:通过OCI Console或API一键创建数据库
- 自动配置:自动选择最优配置参数
- 自动扩展:根据负载自动调整CPU和存储
- 自动备份:自动执行备份和恢复
- 自动调优:自动优化SQL执行计划
- 自动修复:自动检测和修复故障
- 自动安全:自动应用安全补丁
20.2.2 创建Autonomous Database
bash
# 使用OCI CLI创建ATP
$ oci db autonomous-database create \
-c <compartment_ocid> \
--admin-password "MyStr0ng!Passw0rd" \
--db-name ATPDB \
--display-name "ATP Database" \
--db-workload OLTP \
--cpu-core-count 4 \
--data-storage-size-in-tbs 1 \
--license-model LICENSE_INCLUDED \
--is-auto-scaling-enabled true \
--db-version 19c
# 使用OCI CLI创建ADW
$ oci db autonomous-database create \
-c <compartment_ocid> \
--admin-password "MyStr0ng!Passw0rd" \
--db-name ADWDB \
--display-name "ADW Database" \
--db-workload DW \
--cpu-core-count 8 \
--data-storage-size-in-tbs 5 \
--license-model BYOL \
--is-auto-scaling-enabled true \
--db-version 19c
20.2.3 连接Autonomous Database
bash
# 1. 下载Wallet文件
$ oci db autonomous-database generate-wallet \
--autonomous-database-id <atp_ocid> \
--password "WalletPass123" \
--file /tmp/Wallet_ATPDB.zip
# 2. 解压Wallet到客户端目录
$ mkdir -p /u01/app/oracle/wallet/atp
$ unzip /tmp/Wallet_ATPDB.zip -d /u01/app/oracle/wallet/atp
# 3. 配置tnsnames.ora
# Wallet目录中的tnsnames.ora已包含连接信息
# 4. 使用SQL*Plus连接
$ sqlplus admin/password@atpdb_low \
-wallet /u01/app/oracle/wallet/atp
# 5. 使用JDBC连接(Java)
# String url = "jdbc:oracle:thin:@atpdb_medium?TNS_ADMIN=/path/to/wallet";
# Connection conn = DriverManager.getConnection(url, "admin", "password");
20.2.4 管理Autonomous Database
bash
# 查看数据库详情
$ oci db autonomous-database get \
--autonomous-database-id <atp_ocid>
# 修改CPU核心数
$ oci db autonomous-database update \
--autonomous-database-id <atp_ocid> \
--cpu-core-count 8
# 启动自动扩展
$ oci db autonomous-database update \
--autonomous-database-id <atp_ocid> \
--is-auto-scaling-enabled true
# 配置自动备份保留天数
$ oci db autonomous-database update \
--autonomous-database-id <atp_ocid> \
--backup-retention-period-in-days 30
# 创建手动备份
$ oci db autonomous-database create-backup \
--autonomous-database-id <atp_ocid> \
--display-name "pre_weekend_backup"
# 恢复数据库
$ oci db autonomous-database restore \
--autonomous-database-id <atp_ocid> \
--timestamp "2024-06-15T02:00:00Z"
# 克隆数据库
$ oci db autonomous-database clone \
--autonomous-database-id <atp_ocid> \
--display-name "ATP_Clone_Dev" \
--clone-type FULL
20.2.5 Autonomous Database的性能监控
sql
-- 查看自动调优建议
SQL> select name, type, status, description
from dba_autotask_task
where client_name = 'auto optimizer stats collection';
-- 查看自动SQL调优结果
SQL> select sql_id, type, status, recommendation_count
from dba_advisor_tasks
where task_name like 'SYS_AUTO_SQL_TUNING%';
-- 查看自动索引建议
SQL> select task_name, action, sql_text
from dba_advisor_actions
where task_name like 'SYS_AUTO_INDEX%';
-- 查看自动扩展历史
SQL> select timestamp, metric_name, value, action
from dba_hist_autonomous_db_metrics
where metric_name = 'storage_allocated'
order by timestamp desc;
-- 查看数据库活动
SQL> select count(*) as session_count, status, wait_class
from v$session
where type = 'USER'
group by status, wait_class
order by session_count desc;
20.2.6 Autonomous Database最佳实践
- 选择合适的工作负载类型:OLTP选择ATP,数据仓库选择ADW
- 合理配置初始CPU:从4-8 OCPU开始,启用自动扩展应对峰值
- 使用数据库连接池:利用UCP或HikariCP管理连接
- 监控自动扩展:定期检查自动扩展次数,评估是否需要调整基线CPU
- 利用自动调优:不要手动设置参数,让自治数据库自动管理
- 定期备份:虽然自动备份开箱即用,建议定期手动备份重要数据
- 安全配置:使用Network ACL限制访问源,启用审计
20.3 云迁移策略
20.3.1 迁移策略概述
将Oracle数据库迁移到OCI有多种方式,根据停机时间要求和数据量选择合适的策略:
| 迁移策略 | 停机时间 | 数据量 | 复杂度 | 工具 |
|---|---|---|---|---|
| Data Pump | 较长 | 中 | 低 | expdp/impdp |
| RMAN | 中等 | 大 | 中 | RMAN |
| Zero Downtime Migration(ZDM) | 极短 | 任意 | 低 | ZDM工具 |
| GoldenGate | 零停机 | 任意 | 高 | OGG |
| Data Guard | 短 | 大 | 中 | Data Guard |
20.3.2 使用Zero Downtime Migration(ZDM)
ZDM是Oracle提供的零停机迁移工具,支持从本地数据库或云数据库迁移到OCI。
bash
# 1. 下载并安装ZDM
$ wget https://<oracle_zdm_url>/zdm-21.4.zip
$ unzip zdm-21.4.zip -d /u01/app/oracle/zdm
# 2. 配置ZDM
$ cat > /u01/app/oracle/zdm/zdm_config.rsp <<EOF
SOURCE_DATABASE_TYPE=ONPREM
SOURCE_DATABASE_CONNECTION_STRING=source_db_host:1521/ORCL
SOURCE_DATABASE_ADMIN_USER=system
TARGET_DATABASE_CONNECTION_STRING=target_db_host:1521/ORCL
TARGET_DATABASE_ADMIN_USER=admin
OCI_COMPARTMENT_OCID=ocid1.compartment.oc1..
OCI_USER_OCID=ocid1.user.oc1..
OCI_PRIVATE_KEY_FILE=/home/oracle/.oci/oci_api_key.pem
OCI_FINGERPRINT=12:34:56:78:90:ab:cd:ef:12:34:56:78:90:ab:cd:ef
MIGRATION_METHOD=ONLINE
GOLDENGATE_HOME=/u01/app/oracle/ogg
EOF
# 3. 启动ZDM迁移
$ java -jar /u01/app/oracle/zdm/zdm.jar \
-config /u01/app/oracle/zdm/zdm_config.rsp
# 4. 监控迁移进度
$ java -jar /u01/app/oracle/zdm/zdm.jar \
-status
# 5. 迁移完成后的切换
$ java -jar /u01/app/oracle/zdm/zdm.jar \
-switchover
20.3.3 使用Data Pump迁移到OCI
bash
# 源库导出(使用网络链接)
# 1. 在源库和目标库创建Directory对象
SQL> create directory dump_dir as '/u01/expdir';
# 2. 使用expdp导出
$ expdp system/password@source_db \
full=y \
directory=dump_dir \
dumpfile=fullexp_%U.dmp \
logfile=fullexp.log \
parallel=4 \
compression=all \
exclude=STATISTICS
# 3. 传输dump文件到目标库
$ scp /u01/expdir/*.dmp opc@target_host:/u01/impdir/
# 4. 目标库导入
$ impdp admin/password@atpdb_low \
full=y \
directory=imp_dir \
dumpfile=fullexp_%U.dmp \
logfile=fullimp.log \
parallel=4 \
transform=segment_attributes:n
20.3.4 使用RMAN迁移到OCI
bash
# 1. 备份源库
$ rman target /
RMAN> backup database plus archivelog format '/backup/%U';
# 2. 传输备份集到OCI存储
# 使用OCI CLI上传到Object Storage
$ oci os object bulk-upload \
-bn oracle-backup-bucket \
--src-dir /backup/ \
--include "*.bkp" \
--parallel-upload-count 4
# 3. 在OCI目标库上恢复
$ rman target /
RMAN> startup nomount;
RMAN> restore controlfile from 'oci://oracle-backup-bucket/controlfile.bkp';
RMAN> alter database mount;
RMAN> restore database;
RMAN> recover database;
SQL> alter database open resetlogs;
20.3.5 迁移后检查清单
- 数据库版本和配置是否与源库一致
- 所有对象是否编译成功(无无效对象)
- 统计信息是否已收集
- 应用程序是否可正常连接
- Data Guard或备份配置是否完成
- 性能基线是否已建立
- 监控告警是否已配置
- 安全配置(审计、加密)是否已设置
- 用户权限是否已迁移
- 回退方案是否准备就绪
20.4 混合云架构
20.4.1 混合云架构概述
混合云架构是同时使用本地数据中心和公有云资源的部署模式。Oracle提供了多种混合云解决方案,让企业可以在本地和云上统一管理数据库。
混合云架构的优势:
- 弹性扩展:本地资源不足时,快速扩展到云上
- 灾备:使用云数据中心作为灾备站点
- 数据驻留:敏感数据保留在本地,分析数据放在云上
- 渐进式迁移:逐步将工作负载迁移到云上
- 统一管理:使用统一的工具管理本地和云上资源
20.4.2 混合云架构模式
模式1:本地主库 + 云上备库(Data Guard)
┌──────────────────┐ Data Guard ┌──────────────────┐
│ 本地数据中心 │ ◄──────────────► │ OCI云数据中心 │
│ │ │ │
│ ┌────────────┐ │ SYNC/ASYNC │ ┌────────────┐ │
│ │ Primary DB │──┼─────────────────┼─►│ Standby DB │ │
│ │ (RAC) │ │ │ │ (ADG) │ │
│ └────────────┘ │ │ └────────────┘ │
│ │ │ │
│ ┌────────────┐ │ │ ┌────────────┐ │
│ │ 应用服务器 │ │ │ │ 分析查询 │ │
│ └────────────┘ │ │ └────────────┘ │
└──────────────────┘ └──────────────────┘
模式2:本地OLTP + 云上ADW(数据仓库)
┌──────────────────┐ ┌──────────────────┐
│ 本地数据中心 │ │ OCI云数据中心 │
│ │ │ │
│ ┌────────────┐ │ GoldenGate │ ┌────────────┐ │
│ │ OLTP 主库 │──┼─────────────────┼─►│ ADW/ADB │ │
│ │ (RAC) │ │ 实时同步 │ │ 分析数据 │ │
│ └────────────┘ │ │ └────────────┘ │
│ │ │ │
│ ┌────────────┐ │ │ ┌────────────┐ │
│ │ 本地备份 │ │ │ │ BI报表 │ │
│ └────────────┘ │ │ └────────────┘ │
└──────────────────┘ └──────────────────┘
模式3:Oracle Dedicated Region
┌──────────────────────────────────────────────────────┐
│ 数据中心(客户现场) │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Oracle Dedicated Region │ │
│ │ (完整的OCI云服务,在客户数据中心运行) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Compute │ │ DB │ │ 网络/存储 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Oracle管理平面(通过VPN连接Oracle云) │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
20.4.3 Exadata Cloud@Customer
Exadata Cloud@Customer是将Exadata云服务部署在客户本地数据中心的解决方案,提供与OCI Exadata一致的管理体验。
bash
# 查看Exadata Cloud@Customer状态
$ oci db exadata-infrastructure get \
--exadata-infrastructure-id <exadata_ocid>
# 创建VM集群
$ oci db vm-cluster create \
--compartment-id <compartment_ocid> \
--exadata-infrastructure-id <exadata_ocid> \
--display-name "VMCluster1" \
--cpu-core-count 16 \
--data-storage-size-in-tbs 10 \
--gi-version "19.0.0.0" \
--license-model LICENSE_INCLUDED
# 在VM集群上创建数据库
$ oci db database create \
--db-home-id <db_home_ocid> \
--admin-password "MyStr0ng!Pass" \
--db-name ORCL \
--db-version 19.0.0.0
20.4.4 混合云网络配置
bash
# 1. 创建FastConnect专线连接
$ oci network fast-connect create \
--compartment-id <compartment_ocid> \
--bandwidth-shape-name "10 Gbps" \
--provider-name "China Telecom" \
--provider-service-key-name "FASTCONNECT_KEY"
# 2. 配置Site-to-Site VPN
$ oci network ipsec create \
--compartment-id <compartment_ocid> \
--cpe-id <cpe_ocid> \
--drg-id <drg_ocid> \
--static-routes '["10.0.0.0/16"]'
# 3. 配置远程对等连接(RPC)
$ oci network remote-peering-connection create \
--compartment-id <compartment_ocid> \
--display-name "RPC-to-OnPrem"
# 4. 配置DNS解析
# 使用OCI DNS解析私有域名
$ oci dns zone create \
--compartment-id <compartment_ocid> \
--name "oracle.company.com" \
--zone-type PRIMARY
20.4.5 混合云统一管理
bash
# 使用OCI Console统一管理本地和云上资源
# 1. 安装本地管理代理
$ sudo yum install oracle-cloud-agent
# 2. 配置代理连接到OCI
$ sudo /usr/libexec/oracle-cloud-agent/configure \
--compartment-id <compartment_ocid> \
--region ap-chuncheon-1
# 3. 在OCI Console上查看本地资源
# 登录 OCI Console → 资源管理器 → 本地资源
# 4. 使用OCI CLI管理本地资源
$ oci db database list \
-c <compartment_ocid> \
--lifecycle-state AVAILABLE
20.4.6 混合云最佳实践
- 网络规划:使用FastConnect或专线保障网络质量和安全性
- 数据一致性:使用Data Guard确保主备数据一致性
- 灾备测试:定期执行云上备库的切换演练
- 成本控制:使用OCI Budgets监控云上资源消耗
- 安全合规:确保数据驻留和隐私保护符合合规要求
- 统一监控:使用OCI Console或Oracle Enterprise Manager统一监控
- 渐进式迁移:先迁移非核心业务,积累经验后再迁移核心业务
第21章 数据管理与治理
21.1 数据生命周期管理
21.1.1 数据生命周期概述
数据生命周期管理(Information Lifecycle Management, ILM)是管理数据从创建到最终销毁全过程的方法论。合理的ILM策略可以降低存储成本、提高查询性能、满足合规要求。
数据生命周期阶段:
创建 → 活跃使用 → 冷数据 → 归档 → 销毁
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
高性能 标准存储 低成本 离线 安全
存储 存储 存储 存储 删除
21.1.2 Oracle ILM实现
Oracle提供了多种技术实现数据生命周期管理:
| 技术 | 功能 | 适用场景 |
|---|---|---|
| 分区表 | 按时间分区,管理数据生命周期 | 最适合ILM的数据组织方式 |
| 行压缩 | 对冷数据启用压缩 | 减少存储空间 |
| 列压缩(HCC) | Exadata环境的高级压缩 | 数据仓库,归档数据 |
| 数据移动 | 在不同表空间间移动数据 | 冷热数据分离 |
| 自动数据优化(ADO) | 自动执行压缩和移动策略 | 自动化ILM |
| In-Memory | 将热数据加载到内存 | 实时分析查询 |
21.1.3 使用分区实现ILM
sql
-- 创建按时间分区的销售表
CREATE TABLE sales (
sale_id NUMBER,
sale_date DATE,
customer_id NUMBER,
amount NUMBER
)
PARTITION BY RANGE (sale_date) (
PARTITION p_2024_q1 VALUES LESS THAN (TO_DATE('2024-04-01','YYYY-MM-DD')),
PARTITION p_2024_q2 VALUES LESS THAN (TO_DATE('2024-07-01','YYYY-MM-DD')),
PARTITION p_2024_q3 VALUES LESS THAN (TO_DATE('2024-10-01','YYYY-MM-DD')),
PARTITION p_2024_q4 VALUES LESS THAN (TO_DATE('2025-01-01','YYYY-MM-DD')),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
-- 将历史数据移到归档表空间
SQL> ALTER TABLE sales MOVE PARTITION p_2024_q1
TABLESPACE ts_archive
COMPRESS FOR ARCHIVE HIGH;
-- 对历史分区启用行压缩
SQL> ALTER TABLE sales MODIFY PARTITION p_2024_q1
ROW STORE COMPRESS ADVANCED;
-- 删除过期的历史数据
SQL> ALTER TABLE sales DROP PARTITION p_2023_q1;
21.1.4 使用ADO实现自动ILM
sql
-- 创建ADO策略:数据超过6个月后自动压缩
BEGIN
dbms_ilm_admin.create_ilm_policy(
policy_name => 'COMPRESS_6M',
policy_type => dbms_ilm_admin.policy_type_segment,
description => 'Compress data older than 6 months');
dbms_ilm_admin.add_ilm_action(
policy_name => 'COMPRESS_6M',
action_type => dbms_ilm_admin.ilm_action_compression,
compression_type => 'ROW STORE COMPRESS ADVANCED');
dbms_ilm_admin.add_ilm_condition(
policy_name => 'COMPRESS_6M',
condition_type => dbms_ilm_admin.ilm_condition_last_mod_time,
condition_parameter => '180');
END;
/
-- 应用ADO策略到表
BEGIN
dbms_ilm_admin.assign_ilm_policy(
policy_name => 'COMPRESS_6M',
object_name => 'SALES',
object_type => dbms_ilm_admin.ilm_object_table);
END;
/
-- 查看ADO策略执行情况
SQL> select policy_name, object_name, state, completion_time, status
from dba_ilm_results
order by completion_time desc;
21.1.5 ILM策略设计
sql
-- 创建不同层级的表空间
-- 热数据(SSD存储)
CREATE TABLESPACE ts_hot
DATAFILE '/u01/oradata/hot01.dbf' SIZE 10G
AUTOEXTEND ON NEXT 1G MAXSIZE 100G;
-- 温数据(标准存储)
CREATE TABLESPACE ts_warm
DATAFILE '/u01/oradata/warm01.dbf' SIZE 10G
AUTOEXTEND ON NEXT 1G MAXSIZE 200G;
-- 冷数据(归档存储,启用压缩)
CREATE TABLESPACE ts_cold
DATAFILE '/u01/oradata/cold01.dbf' SIZE 10G
AUTOEXTEND ON NEXT 1G MAXSIZE 500G
DEFAULT ROW STORE COMPRESS ADVANCED;
21.2 归档策略
21.2.1 归档策略概述
数据归档是将不再活跃但需要保留的数据从主数据库迁移到归档存储的过程。合理的归档策略可以降低主数据库的存储压力,提高查询性能,同时满足数据保留需求。
归档策略的关键要素:
- 归档时机:何时将数据从主库移出
- 归档方式:使用何种技术归档数据
- 归档存储:归档数据存储在何处
- 访问方式:归档数据如何查询
- 保留期限:归档数据保留多久
- 销毁方式:归档数据如何安全销毁
21.2.2 归档方式对比
| 归档方式 | 说明 | 优点 | 缺点 |
|---|---|---|---|
| 分区交换 | 将分区转换为独立表 | 操作速度快,影响小 | 需要定期维护 |
| Data Pump导出 | 导出为dump文件 | 简单通用 | 恢复慢 |
| 外部表 | 数据保留在文件中,通过外部表访问 | 不占用数据库空间 | 查询性能差 |
| 闪回数据归档 | Oracle内置的归档功能 | 自动管理 | 仅适用于查询 |
| 自定义归档表 | 创建结构相同的归档表 | 灵活可控 | 开发工作量大 |
21.2.3 使用分区交换归档
sql
-- 创建归档表(与源表结构相同)
CREATE TABLE sales_archive_2024_q1
TABLESPACE ts_archive
COMPRESS FOR ARCHIVE HIGH
AS SELECT * FROM sales WHERE 1=0;
-- 交换分区到归档表
SQL> ALTER TABLE sales EXCHANGE PARTITION p_2024_q1
WITH TABLE sales_archive_2024_q1
INCLUDING INDEXES
WITHOUT VALIDATION;
-- 归档表移动到独立表空间
SQL> ALTER TABLE sales_archive_2024_q1
MOVE TABLESPACE ts_archive_offline;
-- 使用视图实现透明查询
CREATE OR REPLACE VIEW sales_all AS
SELECT * FROM sales
UNION ALL
SELECT * FROM sales_archive_2024_q1;
21.2.4 使用Data Pump归档
bash
# 导出归档数据
$ expdp system/password \
directory=exp_dir \
dumpfile=sales_2023.dmp \
logfile=sales_2023_exp.log \
tables=scott.sales \
query='scott.sales:"WHERE sale_date < TO_DATE(''2024-01-01'',''YYYY-MM-DD'')"' \
compression=all
# 导入归档数据到归档数据库
$ impdp system/password@archive_db \
directory=exp_dir \
dumpfile=sales_2023.dmp \
logfile=sales_2023_imp.log \
remap_tablespace=users:ts_archive
21.2.5 使用分区表实现自动归档
sql
-- 创建分区引用表(Partition Reference Table)
-- 使用DBMS_SCHEDULER定期创建新分区并归档旧分区
BEGIN
dbms_scheduler.create_job(
job_name => 'auto_partition_mgmt',
job_type => 'PLSQL_BLOCK',
job_action => 'BEGIN
-- 为下个月创建新分区
EXECUTE IMMEDIATE
''ALTER TABLE sales ADD PARTITION '' ||
''p_'' || TO_CHAR(ADD_MONTHS(SYSDATE,1),''YYYY_MM'') ||
'' VALUES LESS THAN (TO_DATE('''''''' ||
TO_CHAR(ADD_MONTHS(TRUNC(SYSDATE,''MM''),2),''YYYY-MM-DD'') ||
'''''''',''''''''YYYY-MM-DD''''''''))'';
-- 归档12个月前的老分区
FOR rec IN (
SELECT partition_name
FROM dba_tab_partitions
WHERE table_name = ''SALES''
AND table_owner = ''SCOTT''
AND partition_name < ''p_'' ||
TO_CHAR(ADD_MONTHS(SYSDATE,-12),''YYYY_MM'')
AND partition_name != ''P_FUTURE''
) LOOP
EXECUTE IMMEDIATE
''ALTER TABLE SALES MOVE PARTITION '' ||
rec.partition_name || '' TABLESPACE TS_ARCHIVE'';
EXECUTE IMMEDIATE
''ALTER TABLE SALES MODIFY PARTITION '' ||
rec.partition_name || '' COMPRESS FOR ARCHIVE HIGH'';
END LOOP;
END;',
start_date => systimestamp,
repeat_interval => 'freq=monthly; bymonthday=1; byhour=1',
enabled => true);
END;
/
21.2.6 归档数据访问策略
sql
-- 创建透明归档视图
CREATE OR REPLACE VIEW v_sales_all AS
SELECT * FROM sales
UNION ALL
SELECT * FROM sales_archive;
-- 创建物化视图加速归档查询
CREATE MATERIALIZED VIEW mv_sales_summary
REFRESH COMPLETE ON DEMAND
AS
SELECT TRUNC(sale_date, 'MM') AS sale_month,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM sales_archive
GROUP BY TRUNC(sale_date, 'MM');
21.3 数据质量管理
21.3.1 数据质量概述
数据质量管理是确保数据准确性、完整性、一致性和及时性的过程。在数据库运维中,数据质量管理是数据治理的重要组成部分。
数据质量维度:
| 维度 | 说明 | 示例 |
|---|---|---|
| 准确性 | 数据值是否正确 | 金额与实际交易一致 |
| 完整性 | 数据是否缺失 | 必填字段不为空 |
| 一致性 | 数据是否矛盾 | 订单金额与明细金额合计一致 |
| 及时性 | 数据是否及时更新 | 交易数据实时同步 |
| 唯一性 | 数据是否重复 | 同一客户只有一条记录 |
| 有效性 | 数据是否符合规则 | 邮箱格式正确 |
21.3.2 数据质量检查SQL
sql
-- 1. 检查完整性(必填字段)
SQL> SELECT COUNT(*) AS missing_count
FROM orders
WHERE order_date IS NULL
OR customer_id IS NULL
OR amount IS NULL;
-- 2. 检查唯一性
SQL> SELECT customer_id, COUNT(*) AS dup_count
FROM customers
GROUP BY customer_id
HAVING COUNT(*) > 1;
-- 3. 检查数据一致性
SQL> SELECT o.order_id,
o.amount AS order_amount,
SUM(oi.quantity * oi.unit_price) AS detail_amount
FROM orders o, order_items oi
WHERE o.order_id = oi.order_id
GROUP BY o.order_id, o.amount
HAVING ABS(o.amount - SUM(oi.quantity * oi.unit_price)) > 0.01;
-- 4. 检查数据有效性
SQL> SELECT COUNT(*) AS invalid_email
FROM customers
WHERE email NOT LIKE '%@%.%';
-- 5. 检查数据范围
SQL> SELECT COUNT(*) AS out_of_range
FROM products
WHERE price <= 0 OR price > 1000000;
-- 6. 检查外键完整性
SQL> SELECT COUNT(*) AS orphan_records
FROM orders o
WHERE NOT EXISTS (SELECT 1 FROM customers c
WHERE c.customer_id = o.customer_id);
21.3.3 数据清洗脚本
sql
-- 创建数据质量检查临时表
CREATE TABLE data_quality_issues (
check_date DATE,
check_name VARCHAR2(100),
issue_count NUMBER,
sample_data VARCHAR2(4000)
);
-- 定期执行数据质量检查
BEGIN
-- 检查重复客户
INSERT INTO data_quality_issues
SELECT SYSDATE, 'Duplicate Customers',
COUNT(*) - COUNT(DISTINCT customer_id),
'Check customer_id column'
FROM customers;
-- 检查订单金额一致性
INSERT INTO data_quality_issues
SELECT SYSDATE, 'Order Amount Mismatch',
COUNT(*),
'Order ID: ' || MAX(order_id)
FROM (
SELECT o.order_id
FROM orders o, order_items oi
WHERE o.order_id = oi.order_id
GROUP BY o.order_id, o.amount
HAVING o.amount != SUM(oi.quantity * oi.unit_price)
);
COMMIT;
END;
/
-- 查看数据质量报告
SQL> SELECT check_date, check_name, issue_count, sample_data
FROM data_quality_issues
WHERE check_date > SYSDATE - 7
ORDER BY check_date DESC, issue_count DESC;
21.3.4 数据质量监控最佳实践
- 自动化检查:使用DBMS_SCHEDULER定期执行数据质量检查
- 阈值告警:设置数据质量阈值,超阈值时触发告警
- 数据血缘:记录数据来源和转换过程,便于追溯
- 元数据管理:维护数据字典和业务规则
- 定期清洗:定期执行数据清洗操作,修复质量问题
- 质量报告:定期生成数据质量报告,推动持续改进
21.4 合规性要求
21.4.1 合规性概述
数据库合规性是指数据库管理和操作符合法律法规、行业标准和企业内部政策的要求。不同行业和地区有不同的合规要求,DBA需要了解并确保数据库配置满足这些要求。
常见合规性标准:
| 标准 | 全称 | 适用范围 | 关键要求 |
|---|---|---|---|
| GDPR | 通用数据保护条例 | 欧盟公民数据 | 数据保护、隐私权、删除权 |
| PCI DSS | 支付卡行业数据安全标准 | 信用卡处理 | 加密、审计、访问控制 |
| SOX | 萨班斯-奥克斯利法案 | 上市公司 | 审计、内部控制 |
| HIPAA | 健康保险便携和责任法案 | 医疗数据 | 数据加密、隐私保护 |
| 等保2.0 | 网络安全等级保护 | 中国企业 | 安全审计、数据加密 |
| CCPA | 加州消费者隐私法案 | 加州居民数据 | 数据隐私、消费者权利 |
21.4.2 GDPR合规配置
sql
-- 1. 数据加密(TDE)
SQL> ADMINISTER KEY MANAGEMENT SET KEY
IDENTIFIED BY "WalletPass123" WITH BACKUP;
-- 创建加密表空间存储个人数据
SQL> CREATE TABLESPACE tde_pii
DATAFILE '/u01/oradata/tde_pii01.dbf' SIZE 1G
ENCRYPTION USING 'AES256'
DEFAULT STORAGE (ENCRYPT);
-- 2. 数据脱敏
BEGIN
dbms_redact.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'redact_pii',
column_name => 'EMAIL',
function_type => dbms_redact.partial,
function_parameters => 'VVVFVVVVVVVV',
expression => '1=1');
END;
/
-- 3. 审计个人数据访问
BEGIN
dbms_fga.add_policy(
object_schema => 'HR',
object_name => 'EMPLOYEES',
policy_name => 'audit_pii_access',
audit_column => 'EMAIL, PHONE_NUMBER, SSN',
enable => true,
statement_types => 'SELECT, UPDATE');
END;
/
-- 4. 数据保留策略
-- 创建分区表,按时间自动删除过期数据
SQL> ALTER TABLE user_data
ADD PARTITION p_retention VALUES LESS THAN (
ADD_MONTHS(TRUNC(SYSDATE), -36));
-- 定期删除过期分区
SQL> ALTER TABLE user_data DROP PARTITION p_retention;
21.4.3 PCI DSS合规配置
sql
-- 1. 信用卡号加密
SQL> CREATE TABLE payment_info (
payment_id NUMBER PRIMARY KEY,
card_number VARCHAR2(200) ENCRYPT USING 'AES256',
card_holder_name VARCHAR2(100),
expiry_date DATE,
amount NUMBER(10,2)
);
-- 2. 禁止存储CVV和PIN
-- 应用层设计:不存储CVV和PIN码
-- 3. 审计所有支付相关操作
BEGIN
dbms_fga.add_policy(
object_schema => 'FINANCE',
object_name => 'PAYMENT_INFO',
policy_name => 'audit_payment_access',
enable => true,
statement_types => 'SELECT, INSERT, UPDATE, DELETE');
END;
/
-- 4. 限制访问支付数据的用户
SQL> CREATE ROLE payment_viewer;
SQL> GRANT SELECT ON finance.payment_info TO payment_viewer;
SQL> GRANT payment_viewer TO audit_team;
-- 5. 启用统一审计
AUDIT POLICY dba_audit_policy;
21.4.4 等保2.0合规配置
sql
-- 1. 身份鉴别
-- 密码复杂度要求
SQL> ALTER PROFILE default LIMIT
password_life_time 90
password_grace_time 7
password_reuse_time 365
password_reuse_max 5
failed_login_attempts 5
password_lock_time 1
password_verify_function verify_password;
-- 2. 访问控制
-- 最小权限原则
SQL> REVOKE DBA FROM app_user;
SQL> CREATE ROLE app_read_only;
SQL> GRANT SELECT ANY TABLE TO app_read_only;
SQL> GRANT app_read_only TO app_user;
-- 3. 安全审计
-- 启用统一审计
SQL> ALTER SYSTEM SET audit_trail = DB,EXTENDED SCOPE=SPFILE;
-- 审计关键操作
AUDIT POLICY dba_audit_policy;
AUDIT POLICY sensitive_table_policy;
-- 4. 数据完整性
-- 启用块校验
SQL> ALTER SYSTEM SET db_block_checking = MEDIUM;
SQL> ALTER SYSTEM SET db_block_checksum = FULL;
-- 5. 数据保密性
-- 启用TDE加密
SQL> ADMINISTER KEY MANAGEMENT SET KEY
IDENTIFIED BY "WalletPass123" WITH BACKUP;
-- 6. 资源控制
-- 限制会话资源
SQL> ALTER PROFILE app_profile LIMIT
sessions_per_user 5
cpu_per_session 10000
logical_reads_per_session 1000000
connect_time 480
idle_time 30;
21.4.5 合规性检查清单
- 是否启用了数据库审计?
- 审计记录是否保存至少6个月?
- 是否使用了强密码策略?
- 敏感数据是否加密存储?
- 是否实施了最小权限原则?
- 是否定期审查用户权限?
- 是否有数据备份和恢复策略?
- 备份数据是否加密?
- 是否有数据脱敏策略?
- 是否有数据保留和销毁策略?
- 是否定期进行安全漏洞扫描?
- 是否有事件响应计划?
21.4.6 合规性最佳实践
- 了解合规要求:与法务和合规团队合作,明确适用的合规标准
- 建立合规基线:制定数据库安全配置基线,确保所有数据库符合要求
- 自动化合规检查:使用脚本自动检查数据库配置是否符合合规要求
- 定期审计:定期执行合规审计,发现并修复违规项
- 文档化:记录所有安全配置和变更,便于审计
- 培训:定期培训DBA和开发人员,提高合规意识
- 持续改进:根据审计结果和合规要求变化,持续改进安全配置
附录
附录A 常用SQL脚本
附录B 常用数据字典视图
附录C Oracle版本特性对比
附录D 运维工具清单
附录E 术语表
附录F 参考文献与资源
说明: 本文档为Oracle数据库运维的完整知识体系大纲,后续将逐步填充各章节详细内容。