目录
前言
这两天正好安装了 Oracle 26ai 的单机和 RAC 环境,想着正好可以搭建一套 DataGuard 环境玩玩,说干就干!

数据库安装可参考以下两篇:
本文不再赘述数据库安装相关内容。
ADG 搭建
依然采用使用 Duplicate 在线复制的方式搭建 ADG,无需 RMAN 备份,可以通过 RMAN Duplicate 直接在线从主库拷贝文件。
本文搭建步骤均来自 100天精通Oracle 实战系列 课程。
环境信息
| 组件 | 主库 (RAC) | 备库 (单机) |
|---|---|---|
| 主机名 | rac01, rac02 | orcldg |
| IP地址 | 10.168.1.165 | 10.168.1.25 |
| 数据库名 | orcl | orcldg |
| DB_UNIQUE_NAME | orcl | orcldg |
| 版本 | Oracle 26ai | Oracle 26ai |
hosts 配置
# 所有节点配置 hosts 解析
cat << EOF >> /etc/hosts
10.168.1.165 rac-scan
10.168.1.25 orcldg
EOF
主库配置
主库开启归档和强制日志
搭建 DataGuard 必须要开启归档模式和强制日志模式:
-- 检查当前状态
SELECT log_mode, force_logging FROM v$database;
-- 开启归档模式(如未开启)
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
-- 开启强制日志
ALTER DATABASE FORCE LOGGING;
-- 确认配置生效
SELECT log_mode, force_logging FROM v$database;

配置 Standby Redo Logs
Standby Redo Logs 数量为 (Online Redo Logs + 1) × Threads:
-- 查看在线日志配置
SELECT thread#, group#, bytes/1024/1024 size_mb, members
FROM v$log
ORDER BY thread#, group#;
-- 为每个线程添加Standby Redo Logs
-- Thread 1 (假设在线日志组数=7,大小=1GB)
ALTER DATABASE ADD STANDBY LOGFILE THREAD 1
GROUP 20 ('+DATA', '+DATA') SIZE 1G,
GROUP 21 ('+DATA', '+DATA') SIZE 1G,
GROUP 22 ('+DATA', '+DATA') SIZE 1G,
GROUP 23 ('+DATA', '+DATA') SIZE 1G,
GROUP 24 ('+DATA', '+DATA') SIZE 1G,
GROUP 25 ('+DATA', '+DATA') SIZE 1G,
GROUP 26 ('+DATA', '+DATA') SIZE 1G,
GROUP 27 ('+DATA', '+DATA') SIZE 1G;
-- Thread 2
ALTER DATABASE ADD STANDBY LOGFILE THREAD 2
GROUP 30 ('+DATA', '+DATA') SIZE 1G,
GROUP 31 ('+DATA', '+DATA') SIZE 1G,
GROUP 32 ('+DATA', '+DATA') SIZE 1G,
GROUP 33 ('+DATA', '+DATA') SIZE 1G,
GROUP 34 ('+DATA', '+DATA') SIZE 1G,
GROUP 35 ('+DATA', '+DATA') SIZE 1G,
GROUP 36 ('+DATA', '+DATA') SIZE 1G,
GROUP 37 ('+DATA', '+DATA') SIZE 1G;
-- 验证Standby Redo Logs
SELECT thread#, group#, bytes/1024/1024 size_mb, status
FROM v$standby_log
ORDER BY thread#, group#;

生成并准备密码文件
rac 的密码文件通常存放于 ASM 盘中,查看对应的密码文件位置:
srvctl config database -d orcl | grep PASSWORD

使用 asmcmd 进入到 ASM 磁盘组:
# 从 ASM 复制到文件系统
asmcmd -p
ASMCMD [+] > pwcopy +DATA/RPTDB/PASSWORD/pwdrptdb.256.1180304871 /home/grid/

将密码文件拷贝到备库,并且重命名,命名规则为 orapw+sid:
chown oracle:oinstall /home/grid/pwdorcl.256.1223761825 mv /home/grid/pwdorcl.256.1223761825 /home/oracle/orapworcldg scp /home/oracle/orapworcldg oracle@10.168.1.25:/u01/app/oracle/product/26.1.0/db/dbs/

生成参数文件
使用 RMAN 备份在备库恢复,需要在主库生成参数文件:
-- 在主库生成参数文件
CREATE PFILE='/home/oracle/pfile_orcl.ora' FROM SPFILE;

备库配置
创建参数文件
将主库生成并修改后的参数文件在备库创建初始化参数文件(这里建议源端目标端均使用 OMF):
## 在备库创建初始化参数文件
cat<<-\EOF>$ORACLE_HOME/dbs/initorcldg.ora
*._b_tree_bitmap_plans=FALSE
*._cleanup_rollback_entries=2000
*._optimizer_adaptive_cursor_sharing=FALSE
*._optimizer_extended_cursor_sharing='NONE'
*._optimizer_extended_cursor_sharing_rel='NONE'
*._optimizer_use_feedback=FALSE
*._undo_autotune=FALSE
*.compatible='23.6.0'
*.control_file_record_keep_time=31
*.control_files='/oradata/ORCL/CONTROLFILE/control01.ctl','/oradata/ORCL/CONTROLFILE/control02.ctl'
*.db_block_size=8192
*.db_create_file_dest='/oradata'
*.db_files=5000
*.db_name='orcl'
*.deferred_segment_creation=FALSE
*.diagnostic_dest='/u01/app/oracle'
*.dispatchers='(PROTOCOL=TCP) (SERVICE=orcldgXDB)'
*.enable_pluggable_database=true
*.event='28401 trace name context forever,level 1','10949 trace name context forever,level 1'
*.log_archive_format='%t_%s_%r.dbf'
*.nls_language='SIMPLIFIED CHINESE'
*.nls_territory='CHINA'
*.open_cursors=1000
*.parallel_force_local=TRUE
*.parallel_max_servers=64
*.pga_aggregate_target=2643460096
*.processes=2000
*.remote_login_passwordfile='exclusive'
*.session_cached_cursors=300
*.sga_max_size=10575937536
*.sga_target=10575937536
*.undo_retention=10800
*.undo_tablespace='UNDOTBS1'
*.db_unique_name='orcldg'
*.fal_client='ORCL_STB'
*.fal_server='ORCL_PRI'
*.log_archive_config='dg_config=(orcl,orcldg)'
*.log_archive_dest_1='LOCATION=/oradata/archivelog VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=ORCLDG'
*.log_archive_dest_2='SERVICE=ORCL_PRI VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=ORCL'
*.standby_file_management='AUTO'
EOF
创建目录结构
备库根据参数文件在两个节点均创建必要的目录:
# 创建必要目录
mkdir -p /oradata/ORCL/{CONTROLFILE,DATAFILE,ONLINELOG}
mkdir -p /oradata/archivelog
chown -R oracle:oinstall /oradata
chmod -R 755 /oradata
启动备库到 NOMOUNT
打开备库到 nomount 状态:
-- 创建SPFILE
CREATE SPFILE FROM PFILE;
-- 启动到NOMOUNT状态
STARTUP NOMOUNT;

配置 TNS
备库启动监听:
# 启动监听
lsnrctl start
## 等一段时间或者手动注册监听,再次查看监听状态
lsnrctl stat


配置 TNSNAMES.ORA:
## 所有节点均配置 TNS
cat >> $ORACLE_HOME/network/admin/tnsnames.ora << 'EOF'
ORCL_PRI =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.168.1.165)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcl)
)
)
ORCL_STB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = 10.168.1.25)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = orcldg)
(UR=A)
)
)
EOF
# 测试连接
tnsping ORCL_PRI
tnsping ORCL_STB
# 验证数据库连接
sqlplus sys/oracle@ORCL_PRI as sysdba
sqlplus sys/oracle@ORCL_STB as sysdba

RMAN在线复制
rman 同时连接主备库:
[oracle@orcldg:/home/oracle]$ rman target sys/oracle@ORCL_PRI AUXILIARY sys/oracle@ORCL_STB

执行 duplicate 命令:
RMAN> run {
allocate channel prmy1 type disk;
allocate channel prmy2 type disk;
allocate channel prmy3 type disk;
allocate channel prmy4 type disk;
allocate channel prmy5 type disk;
allocate channel prmy6 type disk;
allocate auxiliary channel aux1 type disk;
allocate auxiliary channel aux2 type disk;
allocate auxiliary channel aux3 type disk;
allocate auxiliary channel aux4 type disk;
allocate auxiliary channel aux5 type disk;
allocate auxiliary channel aux6 type disk;
duplicate target database for standby from active database dorecover nofilenamecheck;
}

等待 RMAN duplicate 复制完成即可。
打开备库只读
SQL> alter database open read only;
SQL> alter pluggable database all open;

备库开启日志应用
SQL> alter database recover managed standby database using current logfile disconnect;
Database altered.

主库设置 ADG 参数
alter system set log_archive_config='DG_CONFIG=(orcl,orcldg)';
alter system set log_archive_dest_1='LOCATION=+DATA VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=ORCL';
alter system set log_archive_dest_2='SERVICE=ORCL_STB VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=ORCLDG';
alter system set fal_client='ORCL_PRI';
alter system set fal_server='ORCL_STB';
alter system set log_archive_dest_state_2=defer;
alter system set log_archive_dest_state_2=enable;

检查同步情况
-- 主库
SQL> set line2222 pages1000
col status for a10
col type for a10
col error for a20
col gap_status for a20
col synchronization_status for a30
col recovery_mode for a60
select inst_id,status,DEST_ID,TYPE,ERROR,GAP_STATUS,SYNCHRONIZED,SYNCHRONIZATION_STATUS,RECOVERY_MODE from GV$ARCHIVE_DEST_STATUS where STatus <> 'INACTIVE' and type = 'PHYSICAL';

备库:
SQL> set line222 pages1000
select process,thread#,group#,sequence#,status from v$managed_standby;
SQL> col member for a100
select t2.thread#,t1.group#,t1.member,t2.STATUS,t2.ARCHIVED,t2.bytes/1024/1024 from v$logfile t1,v$standby_log t2 where t1.group#=t2.group# order by 1,2;

至此,ADG 搭建完成,备库可用于报表查询。
主备同步验证
在主库创建一个 PDB:
-- 创建PDB
CREATE PLUGGABLE DATABASE testpdb ADMIN USER admin IDENTIFIED BY oracle;
-- 打开PDB
ALTER PLUGGABLE DATABASE testpdb OPEN;
ALTER PLUGGABLE DATABASE testpdb SAVE STATE;
-- 连接到新创建的 PDB
ALTER SESSION SET CONTAINER=testpdb;
-- 创建测试用户
CREATE USER testuser IDENTIFIED BY oracle;
GRANT dba TO testuser;
-- 创建测试表
CREATE TABLE testuser.employees (
id NUMBER PRIMARY KEY,
name VARCHAR2(50),
department VARCHAR2(30),
salary NUMBER,
hire_date DATE
);
-- 插入测试数据
INSERT INTO testuser.employees VALUES (1, 'John Doe', 'IT', 5000, SYSDATE-100);
INSERT INTO testuser.employees VALUES (2, 'Jane Smith', 'HR', 4500, SYSDATE-50);
INSERT INTO testuser.employees VALUES (3, 'Bob Johnson', 'Sales', 4000, SYSDATE-30);
COMMIT;
-- 验证数据
SELECT * FROM testuser.employees;
SELECT COUNT(*) FROM testuser.employees;

备库验证数据:
-- 启动 PDB
SQL> alter pluggable database testpdb open;
-- 查看 PDB
SQL> show pdbs
-- 切换到 PDB
ALTER SESSION SET CONTAINER=testpdb;
-- 查询数据
SELECT * FROM testuser.employees;
SELECT COUNT(*) FROM testuser.employees;

主备 SwitchOver 切换
切换前检查
查看主备库同步进程状态:
-- 主库
SQL> set line2222
col status for a10
col type for a10
col error for a20
col gap_status for a20
col synchronization_status for a30
col recovery_mode for a60
select inst_id,status,DEST_ID,TYPE,database_mode,ERROR,GAP_STATUS,SYNCHRONIZED,SYNCHRONIZATION_STATUS,RECOVERY_MODE from GV$ARCHIVE_DEST_STATUS where STatus <> 'INACTIVE' and type = 'PHYSICAL';
SQL> set line2222
col name for a10
col database_role for a20
col switchover_status for a20
col PROTECTION_MODE for a25
select inst_id,name,database_role,protection_mode,switchover_status from gv$database;

备库:
SQL> SELECT
(SELECT MAX(sequence#) FROM v$archived_log WHERE applied='YES') last_applied_seq,
(SELECT MAX(sequence#) FROM v$archived_log) last_received_seq,
(SELECT MAX(sequence#) FROM v$archived_log) -
(SELECT MAX(sequence#) FROM v$archived_log WHERE applied='YES') gap
FROM dual;
SQL> set line222
col database_role for a20
col switchover_status for a20
col PROTECTION_MODE for a25
select inst_id,name,database_role,protection_mode,switchover_status from gv$database;

主切换到备
主库切换为备库 (期间观察主库 alert 日志):
SQL> alter database commit to switchover to physical standby with session shutdown;

备切换到主
备库下检查是否可以切换为主库:
SQL> set line222
col database_role for a20
col switchover_status for a20
col PROTECTION_MODE for a25
select name,database_role,protection_mode,switchover_status from gv$database;

备库切换主库:
SQL> alter database commit to switchover to primary with session shutdown;
Database altered.

启动主备库
打开新的主库:
SQL> alter database open;
Database altered.
SQL> select name,open_mode,database_role from v$database;
NAME OPEN_MODE DATABASE_ROLE
--------- -------------------- ----------------
ORADB READ WRITE PRIMARY

打开新的备库:
[oracle@rac01:/home/oracle]$ srvctl start db -d orcl
SQL> alter database recover managed standby database using current logfile disconnect from session;
SQL> select name,open_mode,database_role from gv$database;

检查同步情况
检查主备同步情况:
-- 主库
SQL> set line2222
col status for a10
col type for a10
col error for a20
col gap_status for a20
col synchronization_status for a30
col recovery_mode for a60
select inst_id,status,DEST_ID,TYPE,database_mode,ERROR,GAP_STATUS,SYNCHRONIZED,SYNCHRONIZATION_STATUS,RECOVERY_MODE from GV$ARCHIVE_DEST_STATUS where STatus <> 'INACTIVE' and type = 'PHYSICAL';

切换后主备同步正常,操作流程也较为简单。
总结
这篇文章是使用经典的架构去搭建和切换 ADG,所以还是比较顺利,但是我看 26ai 支持 PDB 级别的 ADG,后面有时间再学习一下。




