Skip to content

基于GoldenGate的读写分离

概述

基于GoldenGate的读写分离是指利用Oracle GoldenGate技术,将写操作路由到主数据库,将读操作路由到一个或多个备数据库,从而实现数据库的负载均衡和高可用性。GoldenGate支持异构数据库环境,提供低延迟、高吞吐量的数据复制功能,适用于复杂的读写分离场景。

架构设计

读写分离架构

基于GoldenGate的读写分离架构主要包括以下组件:

  • 主数据库:处理所有写操作和部分读操作
  • 备数据库:处理只读操作,可以是Oracle或其他异构数据库
  • GoldenGate Extract进程:从主数据库捕获数据变更
  • GoldenGate Data Pump进程:将捕获的数据变更传输到备数据库
  • GoldenGate Replicat进程:将数据变更应用到备数据库
  • 负载均衡器:负责将客户端请求路由到主库或备库
  • 应用层:实现读写分离逻辑,将写操作发送到主库,将读操作发送到备库

读写分离模式

基于GoldenGate的读写分离支持以下模式:

  • 单向复制读写分离:从一个主库复制到多个备库,实现读写分离
  • 双向复制读写分离:两个数据库之间相互复制,实现双向读写分离
  • 级联复制读写分离:通过中间库实现多级读写分离
  • 异构数据库读写分离:从Oracle主库复制到其他异构数据库(如MySQL、SQL Server等),实现读写分离

环境准备

系统要求

配置项要求
Oracle版本19c或21c
GoldenGate版本19c或21c(与Oracle版本兼容)
操作系统Linux 64位或Windows 64位
内存至少8GB(推荐16GB)
磁盘空间至少100GB
网络带宽至少1Gbps(推荐10Gbps)

初始化配置

  1. 安装GoldenGate软件
  2. 配置主数据库和备数据库的Oracle参数
  3. 配置主数据库和备数据库的网络连接
  4. 在主数据库和备数据库中创建GoldenGate用户
  5. 启用主数据库的补充日志和强制日志记录

配置步骤

1. 配置主库GoldenGate

创建GoldenGate用户

sql
CREATE USER ggadmin IDENTIFIED BY ggadmin DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
GRANT CONNECT, RESOURCE, DBA TO ggadmin;
GRANT SELECT ANY DICTIONARY, SELECT ANY TABLE TO ggadmin;
GRANT CREATE ANY TABLE, ALTER ANY TABLE, DROP ANY TABLE TO ggadmin;
GRANT CREATE ANY INDEX, ALTER ANY INDEX, DROP ANY INDEX TO ggadmin;

启用补充日志和强制日志记录

sql
ALTER DATABASE FORCE LOGGING;
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA;
ALTER DATABASE ADD SUPPLEMENTAL LOG DATA (PRIMARY KEY, UNIQUE INDEX) COLUMNS;

配置Manager进程

bash
ggsci
CREATE SUBDIRS
EDIT PARAMS MGR

在Manager参数文件中添加以下内容:

PORT 7809
DYNAMICPORTLIST 7810-7909
AUTORESTART EXTRACT *, RETRIES 5, WAITMINUTES 3
AUTORESTART REPLICAT *, RETRIES 5, WAITMINUTES 3
PURGEOLDEXTRACTS /u01/app/ogg/dirdat/*, USECHECKPOINTS, MINKEEPDAYS 7

创建Extract进程

bash
ggsci
ADD EXTRACT ext1, TRANLOG, BEGIN NOW
ADD EXTTRAIL /u01/app/ogg/dirdat/et, EXTRACT ext1, MEGABYTES 100
EDIT PARAMS ext1

在Extract参数文件中添加以下内容:

EXTRACT ext1
USERID ggadmin@primary_db, PASSWORD ggadmin
EXTTRAIL /u01/app/ogg/dirdat/et
TABLE hr.employees;
TABLE hr.departments;

创建Data Pump进程

bash
ggsci
ADD EXTRACT dp1, EXTTRAILSOURCE /u01/app/ogg/dirdat/et
ADD RMTTRAIL /u01/app/ogg/dirdat/rt, EXTRACT dp1, MEGABYTES 100
EDIT PARAMS dp1

在Data Pump参数文件中添加以下内容:

EXTRACT dp1
USERID ggadmin@primary_db, PASSWORD ggadmin
RMTHOST standby_host, MGRPORT 7809
RMTTRAIL /u01/app/ogg/dirdat/rt
PASSTHRU
TABLE hr.employees;
TABLE hr.departments;

2. 配置备库GoldenGate

创建GoldenGate用户

sql
CREATE USER ggadmin IDENTIFIED BY ggadmin DEFAULT TABLESPACE users TEMPORARY TABLESPACE temp;
GRANT CONNECT, RESOURCE, DBA TO ggadmin;
GRANT SELECT ANY DICTIONARY, SELECT ANY TABLE TO ggadmin;
GRANT CREATE ANY TABLE, ALTER ANY TABLE, DROP ANY TABLE TO ggadmin;
GRANT CREATE ANY INDEX, ALTER ANY INDEX, DROP ANY INDEX TO ggadmin;
GRANT INSERT ANY TABLE, UPDATE ANY TABLE, DELETE ANY TABLE TO ggadmin;

配置Manager进程

bash
ggsci
CREATE SUBDIRS
EDIT PARAMS MGR

在Manager参数文件中添加以下内容:

PORT 7809
DYNAMICPORTLIST 7810-7909
AUTORESTART EXTRACT *, RETRIES 5, WAITMINUTES 3
AUTORESTART REPLICAT *, RETRIES 5, WAITMINUTES 3
PURGEOLDEXTRACTS /u01/app/ogg/dirdat/*, USECHECKPOINTS, MINKEEPDAYS 7

创建Checkpoint表

bash
ggsci
DBLOGIN USERID ggadmin@standby_db, PASSWORD ggadmin
ADD CHECKPOINTTABLE ggadmin.checkpoint

创建Replicat进程

bash
ggsci
ADD REPLICAT rep1, EXTTRAIL /u01/app/ogg/dirdat/rt, CHECKPOINTTABLE ggadmin.checkpoint
EDIT PARAMS rep1

在Replicat参数文件中添加以下内容:

REPLICAT rep1
USERID ggadmin@standby_db, PASSWORD ggadmin
ASSUMETARGETDEFS
DISCARDFILE /u01/app/ogg/dirrpt/rep1.dsc, APPEND, MEGABYTES 100
MAP hr.employees, TARGET hr.employees;
MAP hr.departments, TARGET hr.departments;

3. 启动GoldenGate进程

bash
# 启动主库进程
ggsci@primary
START MGR
START EXTRACT ext1
START EXTRACT dp1

# 启动备库进程
ggsci@standby
START MGR
START REPLICAT rep1

4. 应用层读写分离实现

Java应用实现

在Java应用中,可以使用Spring框架的AbstractRoutingDataSource实现读写分离:

java
// 配置主库数据源
@Bean(name = "primaryDataSource")
public DataSource primaryDataSource() {
    // 配置主库数据源
}

// 配置备库数据源
@Bean(name = "standbyDataSource")
public DataSource standbyDataSource() {
    // 配置备库数据源
}

// 配置动态数据源
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource,
                                   @Qualifier("standbyDataSource") DataSource standbyDataSource) {
    DynamicDataSource dynamicDataSource = new DynamicDataSource();
    Map<Object, Object> targetDataSources = new HashMap<>();
    targetDataSources.put(DataSourceType.PRIMARY, primaryDataSource);
    targetDataSources.put(DataSourceType.STANDBY, standbyDataSource);
    dynamicDataSource.setTargetDataSources(targetDataSources);
    dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
    return dynamicDataSource;
}

// 配置事务切面,实现读写分离
@Aspect
@Component
public class DataSourceAspect {
    @Before("execution(* com.example.service.*.select*(..)) || execution(* com.example.service.*.get*(..))")
    public void setReadDataSourceType() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.STANDBY);
    }

    @Before("execution(* com.example.service.*.insert*(..)) || execution(* com.example.service.*.update*(..)) || execution(* com.example.service.*.delete*(..))")
    public void setWriteDataSourceType() {
        DataSourceContextHolder.setDataSourceType(DataSourceType.PRIMARY);
    }
}

.NET应用实现

在.NET应用中,可以使用Oracle Data Provider for .NET (ODP.NET)实现读写分离:

csharp
// 配置主库连接字符串
string primaryConnectionString = "Data Source=primary_db;User Id=scott;Password=tiger;";

// 配置备库连接字符串
string standbyConnectionString = "Data Source=standby_db;User Id=scott;Password=tiger;";

// 写操作使用主库
using (OracleConnection conn = new OracleConnection(primaryConnectionString))
{
    conn.Open();
    OracleCommand cmd = new OracleCommand("INSERT INTO employees (employee_id, first_name, last_name) VALUES (:id, :first_name, :last_name)", conn);
    cmd.Parameters.Add(":id", OracleDbType.Int32).Value = 1001;
    cmd.Parameters.Add(":first_name", OracleDbType.Varchar2).Value = "John";
    cmd.Parameters.Add(":last_name", OracleDbType.Varchar2).Value = "Doe";
    cmd.ExecuteNonQuery();
}

// 读操作使用备库
using (OracleConnection conn = new OracleConnection(standbyConnectionString))
{
    conn.Open();
    OracleCommand cmd = new OracleCommand("SELECT * FROM employees", conn);
    OracleDataReader reader = cmd.ExecuteReader();
    while (reader.Read())
    {
        Console.WriteLine(reader["employee_id"] + ", " + reader["first_name"] + ", " + reader["last_name"]);
    }
}

版本差异

Oracle 19c vs 21c基于GoldenGate的读写分离差异

特性Oracle 19cOracle 21c
并行复制支持增强并行复制,提高复制性能
双向复制支持增强双向复制,减少冲突概率
异构数据库支持支持增强异构数据库支持,支持更多数据库类型
自动发现表支持增强自动发现表,支持更多场景
云集成基础云集成增强云集成,支持与OCI、AWS、Azure等云平台集成
自动诊断基础自动诊断增强自动诊断和修复能力
安全功能基础安全功能增强安全功能,支持数据加密和访问控制
监控能力基础监控能力增强监控能力,提供更详细的性能指标

监控与管理

监控GoldenGate读写分离状态

  1. 查看GoldenGate进程状态
bash
ggsci
INFO ALL
LAG EXTRACT ext1
LAG REPLICAT rep1
STATS EXTRACT ext1
STATS REPLICAT rep1
  1. 监控复制延迟
bash
ggsci
LAG EXTRACT ext1
LAG REPLICAT rep1
  1. 查看复制统计信息
bash
ggsci
STATS EXTRACT ext1, TOTAL
STATS REPLICAT rep1, TOTAL
  1. 监控备库性能
sql
-- 查看备库的等待事件
SELECT EVENT, COUNT(*) FROM V$SESSION_WAIT GROUP BY EVENT ORDER BY COUNT(*) DESC;

-- 查看备库的SQL执行情况
SELECT SQL_ID, PLAN_HASH_VALUE, EXECUTIONS, ELAPSED_TIME/1000000 AS ELAPSED_TIME_SEC FROM V$SQL ORDER BY ELAPSED_TIME DESC;

常见管理任务

  1. 添加新表到复制
bash
ggsci
EDIT PARAMS ext1
# 添加新表到Extract参数文件
TABLE hr.jobs;

EDIT PARAMS dp1
# 添加新表到Data Pump参数文件
TABLE hr.jobs;

EDIT PARAMS rep1
# 添加新表到Replicat参数文件
MAP hr.jobs, TARGET hr.jobs;

# 重新启动进程或刷新参数
ggsci
ALTER EXTRACT ext1, EXTEND TABLE hr.jobs
ALTER EXTRACT dp1, EXTEND TABLE hr.jobs
ALTER REPLICAT rep1, EXTEND MAP hr.jobs, TARGET hr.jobs
  1. 处理复制冲突

查看冲突日志:

bash
cat /u01/app/ogg/dirrpt/rep1.dsc

修复冲突数据,然后重新启动Replicat进程:

bash
ggsci
START REPLICAT rep1
  1. 调整复制性能
bash
# 调整Extract进程并行度
ggsci
ALTER EXTRACT ext1, PARALLEL 4

# 调整Replicat进程并行度
ggsci
ALTER REPLICAT rep1, PARALLEL 4

最佳实践

性能优化

  1. 优化GoldenGate进程参数

    • 调整Extract和Replicat进程的并行度
    • 配置合适的Trail文件大小
    • 使用BATCHSQL参数提高批量处理能力
    • 启用压缩选项减少网络传输量
  2. 优化网络连接

    • 使用高速网络连接,至少10Gbps
    • 调整TCP缓冲区大小
    • 使用多个Data Pump进程分散网络负载
    • 配置合适的网络超时参数
  3. 优化备库性能

    • 配置足够的内存和CPU资源
    • 使用高速存储设备,提高I/O性能
    • 优化备库的Oracle参数
    • 启用并行查询,提高复杂查询的性能

高可用性

  1. 配置Manager进程自动重启

    AUTORESTART EXTRACT *, RETRIES 5, WAITMINUTES 3
    AUTORESTART REPLICAT *, RETRIES 5, WAITMINUTES 3
  2. 使用备用Manager进程

    • 配置多个Manager进程,确保高可用性
    • 使用虚拟IP地址实现Manager进程的故障转移
  3. 配置多个备库

    • 配置多个备库,实现读写分离的高可用性
    • 当一个备库不可用时,自动切换到其他备库

安全管理

  1. 使用加密传输

    RMTHOST target_host, MGRPORT 7809, ENCRYPT AES256, KEYNAME mykey
  2. 使用加密Trail文件

    EXTTRAIL /u01/app/ogg/dirdat/et, ENCRYPT AES256, KEYNAME mykey
  3. 限制GoldenGate用户权限

    • 遵循最小权限原则,只授予必要的权限
    • 定期轮换GoldenGate用户密码
    • 使用OS认证或Wallet存储密码

故障处理

常见故障及解决方法

  1. Extract进程无法启动

症状:GGSMGR日志显示"ERROR OGG-00664: Oracle GoldenGate Capture for Oracle, ext1.prm: Failed to attach to logmining server."

解决方法

  • 检查主库是否启用了补充日志和强制日志记录
  • 检查GoldenGate用户是否有足够的权限
  • 检查主库的LOG_MINING_SERVER参数配置
  1. Replicat进程应用失败

症状:GGSMGR日志显示"ERROR OGG-01003: Oracle GoldenGate Delivery for Oracle, rep1.prm: There is a problem in mapping record to target table HR.EMPLOYEES."

解决方法

  • 检查源表和目标表的结构是否一致
  • 验证GoldenGate版本与Oracle版本是否兼容
  • 检查Trail文件的完整性
  • 查看DISCARDFILE获取详细的错误信息
  1. 复制延迟过大

症状:LAG命令显示Extract或Replicat进程延迟超过阈值

解决方法

  • 检查网络带宽和延迟
  • 调整Extract和Replicat进程的并行度
  • 优化源库和备库的性能
  • 调整Trail文件大小和数量

常见问题(FAQ)

Q: 基于GoldenGate的读写分离有什么优势?

A: 基于GoldenGate的读写分离具有以下优势:

  • 支持异构数据库环境,可以实现Oracle与其他数据库之间的读写分离
  • 复制延迟极低,通常在毫秒级
  • 吞吐量极高,适合高并发场景
  • 灵活性高,支持复杂的读写分离策略
  • 可以实现双向复制和多级复制

Q: 基于GoldenGate的读写分离适用于哪些场景?

A: 基于GoldenGate的读写分离适用于以下场景:

  • 读多写少的业务场景,如电商网站、新闻网站等
  • 异构数据库环境,需要在不同数据库之间实现读写分离
  • 对复制延迟要求极高的场景
  • 需要复杂读写分离策略的场景
  • 数据迁移和数据集成场景

Q: 如何处理GoldenGate读写分离中的数据冲突?

A: 可以通过以下方式处理数据冲突:

  • 使用冲突检测和解决(CDR)功能
  • 配置适当的冲突解决规则
  • 定期监控和处理冲突日志
  • 优化应用设计,减少冲突概率
  • 使用时间戳或序列生成器确保数据一致性

Q: 基于GoldenGate的读写分离支持多少个备库?

A: GoldenGate支持从一个主库复制到多个备库,理论上没有数量限制。实际使用中,备库数量取决于系统资源、网络带宽和业务需求。

Q: 如何验证基于GoldenGate的读写分离的数据一致性?

A: 可以使用以下方法验证数据一致性:

  • 使用Oracle Data Compare工具比较主库和备库的数据
  • 使用DBMS_COMPARISON包验证主备库数据一致性
  • 执行业务查询,比较主库和备库的查询结果
  • 定期执行数据校验和计算
  • 使用GoldenGate的VERIFY功能验证数据一致性

Q: 基于GoldenGate的读写分离与基于Data Guard的读写分离有什么区别?

A: 基于GoldenGate的读写分离与基于Data Guard的读写分离的主要区别:

  • 技术原理:GoldenGate基于逻辑复制,Data Guard基于物理复制
  • 延迟:GoldenGate的延迟极低,Data Guard的延迟较低
  • 异构支持:GoldenGate支持异构数据库,Data Guard只支持Oracle数据库
  • 配置复杂度:GoldenGate的配置相对复杂,Data Guard的配置相对简单
  • 灵活性:GoldenGate的灵活性更高,支持更复杂的读写分离场景

总结

基于GoldenGate的读写分离是一种高效、灵活的数据复制解决方案,支持异构数据库环境,提供低延迟、高吞吐量的数据复制功能。在配置基于GoldenGate的读写分离时,需要根据业务需求选择合适的复制模式和配置参数,并定期监控和管理GoldenGate进程,确保其正常运行。Oracle 21c相比19c在GoldenGate方面有显著增强,包括增强的并行复制、双向复制和云集成等功能。