Skip to content

基于Data Guard的读写分离

概述

基于Data Guard的读写分离是指利用Oracle Data Guard技术,将写操作路由到主数据库,将读操作路由到物理备库,从而实现数据库的负载均衡和高可用性。通过读写分离,可以提高系统的整体吞吐量,减少主数据库的负载,同时利用备库资源提高资源利用率。

架构设计

读写分离架构

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

  • 主数据库:处理所有写操作和部分读操作
  • 物理备库:使用Active Data Guard功能,处理只读操作
  • 负载均衡器:负责将客户端请求路由到主库或备库
  • 应用层:实现读写分离逻辑,将写操作发送到主库,将读操作发送到备库
  • Data Guard配置:确保主库和备库之间的数据一致性

读写分离模式

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

  • 应用层读写分离:在应用程序中实现读写分离逻辑,将写操作发送到主库,将读操作发送到备库
  • 中间件读写分离:使用中间件(如Oracle Data Provider for .NET、JDBC连接池等)实现读写分离
  • 负载均衡器读写分离:使用负载均衡器(如F5、NGINX等)根据SQL类型将请求路由到主库或备库

环境准备

系统要求

配置项要求
Oracle版本19c或21c Enterprise Edition
Data Guard配置已配置物理备库
Active Data Guard已启用
网络带宽至少1Gbps(推荐10Gbps)
备库模式READ ONLY WITH APPLY

初始化配置

  1. 确保主库和备库已配置Data Guard
  2. 启用备库的Active Data Guard功能
  3. 配置主库和备库的网络连接
  4. 配置客户端连接字符串
  5. 安装并配置负载均衡器(可选)

配置步骤

1. 启用Active Data Guard

在备库上执行以下命令启用Active Data Guard:

sql
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;
ALTER DATABASE OPEN READ ONLY;
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT FROM SESSION;

2. 验证Active Data Guard状态

在备库上执行以下命令验证Active Data Guard状态:

sql
SELECT DB_UNIQUE_NAME, DATABASE_ROLE, OPEN_MODE FROM V$DATABASE;
-- 输出应为:OPEN_MODE = "READ ONLY WITH APPLY"

SELECT NAME, VALUE FROM V$DATAGUARD_STATS WHERE NAME IN ('transport lag', 'apply lag');
-- 验证延迟是否在可接受范围内

3. 配置客户端连接

配置tnsnames.ora文件

在客户端配置tnsnames.ora文件,添加主库和备库的连接字符串:

# 主库连接字符串
PRIMARY_DB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = primary_host)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = primary_db)
    )
  )

# 备库连接字符串
STANDBY_DB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = standby_host)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = standby_db)
    )
  )

# 负载均衡连接字符串(可选)
DG_RAC =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = primary_host)(PORT = 1521))
      (ADDRESS = (PROTOCOL = TCP)(HOST = standby_host)(PORT = 1521))
      (LOAD_BALANCE = yes)
    )
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = primary_db)
    )
  )

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"]);
    }
}

5. 负载均衡器配置(可选)

使用F5或NGINX等负载均衡器实现读写分离,将写操作路由到主库,将读操作路由到备库。

F5负载均衡器配置

  1. 创建主库和备库的服务器池
  2. 创建iRule,根据SQL类型将请求路由到主库或备库
  3. 配置虚拟服务器,应用iRule

NGINX负载均衡器配置

nginx
http {
    upstream primary_group {
        server primary_host:1521;
    }

    upstream standby_group {
        server standby_host:1521;
    }

    server {
        listen 80;
        server_name oracle-lb.example.com;

        location /write {
            proxy_pass http://primary_group;
            # 其他配置
        }

        location /read {
            proxy_pass http://standby_group;
            # 其他配置
        }
    }
}

版本差异

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

特性Oracle 19cOracle 21c
Active Data Guard支持增强Active Data Guard,提高只读性能
并行查询支持增强并行查询,提高只读备库的查询性能
自动索引支持主库自动索引,备库不支持支持备库自动索引,提高备库查询性能
实时统计信息主库实时统计信息,备库延迟更新增强备库实时统计信息,减少统计信息延迟
内存管理基础内存管理增强内存管理,优化备库内存使用
云集成基础云集成增强云集成,支持与OCI、AWS、Azure等云平台的读写分离
监控能力基础监控能力增强监控能力,提供更详细的读写分离性能指标

监控与管理

监控读写分离状态

  1. 监控备库状态
sql
SELECT DB_UNIQUE_NAME, DATABASE_ROLE, OPEN_MODE FROM V$DATABASE;
SELECT NAME, VALUE FROM V$DATAGUARD_STATS WHERE NAME IN ('transport lag', 'apply lag');
  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. 监控读写分离流量
sql
-- 查看主库和备库的连接数
SELECT COUNT(*) FROM V$SESSION WHERE STATUS = 'ACTIVE';

-- 查看主库和备库的SQL类型分布
SELECT COMMAND_TYPE, COUNT(*) FROM V$SESSION GROUP BY COMMAND_TYPE;

常见管理任务

  1. 调整读写分离策略

    • 根据业务需求调整读操作和写操作的分布比例
    • 调整备库的资源分配,优化只读性能
    • 配置多个备库,实现更灵活的读写分离
  2. 处理备库延迟

    • 检查网络带宽和延迟
    • 调整Redo传输模式和应用模式
    • 优化备库的I/O性能
  3. 备库维护

    • 定期备份备库
    • 定期更新备库的统计信息
    • 监控备库的磁盘空间和内存使用

最佳实践

性能优化

  1. 优化备库性能

    • 配置足够的内存和CPU资源
    • 使用高速存储设备,提高I/O性能
    • 优化备库的Oracle参数,如DB_CACHE_SIZE、SHARED_POOL_SIZE等
    • 启用并行查询,提高复杂查询的性能
  2. 减少备库延迟

    • 使用SYNC传输模式,确保Redo日志及时传输到备库
    • 启用Real-Time Apply,实时应用Redo日志
    • 调整备库的并行恢复进程数
    • 优化网络连接,减少传输延迟
  3. 优化应用层读写分离

    • 合理设计读写分离策略,根据业务需求调整读操作和写操作的分布比例
    • 使用连接池管理数据库连接,提高连接利用率
    • 实现读写分离的容错机制,当备库不可用时自动切换到主库

高可用性

  1. 配置多个备库

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

    • 配置Fast-Start Failover,实现主库故障时的自动切换
    • 实现备库故障时的自动切换,确保读写分离的连续性
  3. 定期测试切换流程

    • 定期执行主备库切换测试,确保切换流程正常
    • 测试读写分离在切换过程中的表现,确保业务连续性

安全管理

  1. 限制备库用户权限

    • 备库用户只授予只读权限,防止误操作
    • 定期轮换备库用户密码
    • 使用最小权限原则,只授予必要的权限
  2. 加密传输

    • 配置主库和备库之间的Redo传输加密
    • 配置客户端和数据库之间的连接加密
    • 使用SSL/TLS协议保护网络传输
  3. 监控访问日志

    • 监控主库和备库的访问日志,及时发现异常访问
    • 配置审计日志,记录所有数据库操作
    • 定期分析访问日志,发现潜在的安全风险

故障处理

常见故障及解决方法

  1. 备库延迟过大

症状:V$DATAGUARD_STATS显示apply lag或transport lag超过阈值,影响读写分离的效果。

解决方法

  • 检查网络带宽和延迟
  • 调整Redo传输模式为SYNC
  • 启用Real-Time Apply
  • 增加备库的并行恢复进程数
  • 优化备库的I/O性能
  1. 备库不可用

症状:客户端无法连接到备库,读写分离失效,所有请求都路由到主库。

解决方法

  • 检查备库的状态,重启备库服务
  • 检查网络连接,确保备库可以正常访问
  • 切换到其他备库(如果配置了多个备库)
  • 暂时将所有请求路由到主库,直到备库恢复正常
  1. 主备库数据不一致

症状:备库查询结果与主库不一致,影响业务数据准确性。

解决方法

  • 检查Redo传输和应用状态
  • 验证主库和备库的Redo日志序列号
  • 重新同步主备库数据
  • 检查是否存在逻辑错误或冲突

常见问题(FAQ)

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

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

  • 利用现有Data Guard配置,无需额外的复制软件
  • 数据一致性高,备库与主库的延迟低
  • 配置简单,维护成本低
  • 支持自动故障转移,提高系统可用性
  • 充分利用备库资源,提高资源利用率

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

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

  • 读多写少的业务场景,如电商网站、新闻网站等
  • 对数据一致性要求高的场景
  • 已有Data Guard配置,希望充分利用备库资源的场景
  • 希望提高系统吞吐量,减少主库负载的场景

Q: 如何选择合适的读写分离比例?

A: 选择合适的读写分离比例需要考虑以下因素:

  • 业务的读操作和写操作比例
  • 备库的性能和资源情况
  • 主备库之间的延迟
  • 业务对数据一致性的要求

一般来说,读操作比例越高,读写分离的效果越好。对于读多写少的业务,可以将90%以上的读操作路由到备库。

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

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

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

Q: 如何处理备库的统计信息更新?

A: 可以通过以下方式处理备库的统计信息更新:

  • 在主库上收集统计信息,备库会自动同步
  • 使用DBMS_STATS包在备库上收集统计信息
  • 配置自动统计信息收集,定期更新统计信息
  • 手动刷新备库的统计信息,确保统计信息的准确性

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

A: Oracle 19c和21c支持最多30个备库,其中可以包含多个Active Data Guard备库,用于实现读写分离。实际使用中,备库数量取决于系统资源和业务需求。

总结

基于Data Guard的读写分离是一种简单、高效的数据库负载均衡方案,利用现有Data Guard配置,将读操作路由到备库,将写操作路由到主库,从而提高系统的整体吞吐量,减少主数据库的负载。在配置基于Data Guard的读写分离时,需要考虑备库性能、延迟、高可用性和安全性等因素,选择合适的读写分离策略和配置参数。Oracle 21c相比19c在基于Data Guard的读写分离方面有显著增强,包括提高备库只读性能、支持备库自动索引和增强实时统计信息等功能。