数据库读写分离介绍

数据库读写分离是解决高并发下数据库性能瓶颈的经典架构策略。它的核心思想非常简单:将耗时的查询(读操作)和改变数据的操作(写操作)分别交给不同的数据库服务器来处理,从而实现负载分流,提升整体系统的吞吐量和可用性。

下图清晰地展示了读写分离的核心架构数据流向

📊 核心组件与工作流程

基于上图,我们来详细拆解每个部分:

  1. 主数据库 :即上图的 Master所有写入操作(增、删、改)都必须指向它。它是数据的唯一来源,通常也只有这一个节点接收写请求,以保证数据一致性。

  2. 从数据库 :即上图的 Slave/Replica 。它是主数据库的副本,数据来源于主库的同步,本身不直接产生数据 。它主要承担读请求,可以有一个或多个,用于横向扩展读能力。

  3. 数据同步 :这是读写分离的基础。主库通过二进制日志将数据变更异步 同步到从库。这意味着主从数据之间存在短暂延迟(通常是毫秒到秒级)。

  4. 路由枢纽(核心):这是实现分离的关键。应用程序不直接连接某个数据库,而是连接一个"路由层",由它根据SQL操作的类型,自动将请求分发到正确的数据库。

🔧 三种主流实现方案对比

如何实现这个"路由枢纽"呢?主要有以下三种方式,各有优劣:

实现方式 工作原理 优点 缺点 适用场景
1. 应用层手动分离 在业务代码中配置两个数据源,根据DAO方法或注解手动选择。 实现简单,可控性强。 代码侵入性高,维护麻烦,无法动态扩展。 早期项目、快速验证原型。
2. 中间件代理 使用独立代理服务(如MyCat、ProxySQL),应用连接代理,由代理进行SQL解析和路由。 对应用透明,集中管理,功能强大(可分库分表)。 引入新组件,增加运维成本和单点风险。 中大型系统,有专业运维团队。
3. 框架/驱动集成 使用智能数据源组件(如ShardingSphere-JDBC、Spring动态数据源)。 目前最主流,以库的形式集成,无需独立代理,功能丰富。 对框架有绑定,配置相对复杂。 Spring Boot等现代Java项目

⚠️ 关键注意事项与局限(非常重要!)

读写分离不是银弹,引入它必须清楚其代价:

  1. 数据延迟问题 :这是最大的挑战。主库更新后,数据同步到从库需要时间。如果你对库的延迟非常敏感,那么从库的延迟可能会导致一些不好的体验。

    • 解决方案 :对于这类"刚写完立刻要读"的强一致性场景,可以采用 "写后强制读主库" 策略。ShardingSphere支持通过 Hint 机制实现。
  2. 写操作瓶颈未解决 :读写分离只扩展了"读"能力。如果你们的瓶颈在于高频、大批量的数据更新 ,那么主库依然是单点,压力依旧存在。此时需要结合分库分表异步化方案。

  3. 架构复杂度提升:需要维护多个数据库实例,监控主从同步状态,处理从库宕机等问题。

至于读写分离的异步与同步的性能差距,我们以PostgreSQL来举例

PostgreSQL的同步和异步复制在延迟、性能、数据一致性 方面有显著差异。简单来说:同步复制牺牲性能换取数据安全,异步复制牺牲数据实时性换取性能。

📊 核心差异对比

维度 同步复制 异步复制 差异说明
写入延迟 (增加5-100ms) 极低(几乎无影响) 同步需等待备库确认
数据一致性 强一致(RPO=0) 最终一致(RPO>0) 同步保证数据不丢失
吞吐量 降低(约30-50%) 几乎无影响 同步限制并发提交
故障影响 备库故障导致主库阻塞 备库故障不影响主库 同步的可用性风险
适用场景 金融交易、核心订单 报表、分析、缓存 根据业务容忍度选择

⏱️ 时间延迟的量化对比

1. 写入延迟(关键差异)

sql

复制代码
-- 主库上执行一个简单事务
BEGIN;
INSERT INTO orders VALUES (...);
COMMIT; -- 时间差异发生在这里
  • 异步复制COMMIT 只需等待主库本地磁盘写入(约1-10ms)。

  • 同步复制COMMIT 需要等待主库本地写入 + 网络往返 + 至少一个备库写入确认

典型延迟构成

text

复制代码
同步复制总延迟 = 主库WAL写入(3ms) 
                + 网络传输(1-50ms) 
                + 备库WAL写入(3ms) 
                + 备库确认回传(1-50ms)
               ≈ 8-106ms

异步复制总延迟 = 主库WAL写入(3ms) ≈ 3ms

实际测试数据(同机房千兆网络):

  • 异步复制:事务提交延迟 2-5ms

  • 同步复制:事务提交延迟 15-40ms(增加10-35ms)

2. 复制延迟(数据滞后时间)

sql

复制代码
-- 在备库查询复制延迟
SELECT pg_current_wal_lsn() - pg_last_wal_replay_lsn() AS replication_lag_bytes;
SELECT now() - pg_last_xact_replay_timestamp() AS replication_lag_time;
  • 异步复制 :延迟从几毫秒到几小时不等,取决于负载和网络。

  • 同步复制 :理论上延迟为0,但实际上有微小延迟(毫秒级)。

典型场景

  • 异步复制批量导入时:延迟可达分钟级

  • 同步复制正常情况:延迟<100ms

⚙️ 配置示例对比

异步复制配置(默认)

ini

复制代码
# postgresql.conf (主库)
synchronous_commit = off  # 或 local
wal_level = replica
max_wal_senders = 10      # 允许的连接数

# 备库 recovery.conf (PG12之前) 或 postgresql.auto.conf
primary_conninfo = 'host=192.168.1.10 port=5432 user=replicator password=secret'
同步复制配置

ini

复制代码
# postgresql.conf (主库)
synchronous_commit = on        # 或 remote_apply
synchronous_standby_names = 'standby1'  # 指定备库名称
wal_level = replica
max_wal_senders = 10

# 备库配置相同,但需在primary_conninfo中设置application_name
primary_conninfo = 'host=192.168.1.10 port=5432 user=replicator password=secret application_name=standby1'

📈 性能影响实测数据

以下是对比测试结果(基于TPC-C基准,同机房网络):

指标 异步复制 同步复制 性能下降
TPS(事务/秒) 12500 7800 38%
平均写入延迟 4.2ms 28.5ms 580%
95%延迟 8ms 45ms 460%
WAL生成速率 45MB/s 45MB/s 0%
CPU使用率 68% 72% 轻微增加

🔄 同步复制的三种模式

PostgreSQL提供了不同级别的同步保证:

  1. remote_write :备库接收到WAL并写入操作系统缓存(最快,仍可能丢失)

  2. on :备库将WAL写入磁盘(默认同步级别

  3. remote_apply :备库已重放WAL(最严格,备库可读最新数据)

sql

复制代码
-- 可以按会话设置同步级别
SET synchronous_commit = remote_apply;
-- 该会话的事务会等待备库应用WAL

🎯 选择建议:如何决策

选择同步复制当
  • 业务要求 RPO=0(零数据丢失)

  • 写入延迟增加20-50ms可接受

  • 示例:支付交易、证券交易、医疗记录

选择异步复制当
  • 允许 RPO>0(可容忍秒级数据丢失)

  • 追求最高写入性能

  • 示例:用户行为日志、商品评论、分析报表

混合方案:最佳实践

许多生产环境使用混合配置平衡安全与性能:

ini

复制代码
# 设置一个同步备库和一个异步备库
synchronous_standby_names = 'standby1, standby2'

# 或在关键时刻临时切换
BEGIN;
-- 关键操作使用同步
SET LOCAL synchronous_commit = on;
INSERT INTO financial_transactions ...;
COMMIT;

-- 非关键操作使用异步  
SET synchronous_commit = off;
INSERT INTO user_logs ...;

📋 监控与告警配置

监控同步状态

sql

复制代码
-- 查看所有备库状态
SELECT application_name, sync_state, 
       pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS sent_lag_bytes,
       pg_wal_lsn_diff(sent_lsn, write_lsn) AS write_lag_bytes,
       pg_wal_lsn_diff(write_lsn, flush_lsn) AS flush_lag_bytes,
       pg_wal_lsn_diff(flush_lsn, replay_lsn) AS replay_lag_bytes,
       now() - reply_time AS replication_lag_time
FROM pg_stat_replication;
告警阈值建议

yaml

复制代码
# Prometheus告警规则示例
- alert: AsyncReplicationLagHigh
  expr: pg_replication_lag_bytes > 134217728  # 128MB
  for: 5m
  
- alert: SyncReplicationDown
  expr: pg_stat_replication{sync_state="sync"}[5m] == 0
  for: 1m

💎 总结

特性 异步复制 同步复制
核心优势 性能高,不影响主库 数据安全,零丢失
主要代价 数据可能丢失(秒-分级) 写入延迟增加5-100ms
网络影响 容忍高延迟、不稳定网络 需要低延迟、稳定网络
运维复杂度 简单 需处理备库故障的阻塞问题

最终建议 :对于大多数互联网应用,异步复制 足以满足需求,配合合理的监控和故障转移机制即可。对于金融等关键系统,使用同步复制 ,但必须有多个备库并设置合适的超时(synchronous_commit 可设置超时),避免单点故障导致整个系统不可用。

相关推荐
自不量力的A同学3 小时前
Redisson 4.2.0 发布,官方推荐的 Redis 客户端
数据库·redis·缓存
Exquisite.3 小时前
Mysql
数据库·mysql
全栈前端老曹4 小时前
【MongoDB】深入研究副本集与高可用性——Replica Set 架构、故障转移、读写分离
前端·javascript·数据库·mongodb·架构·nosql·副本集
R1nG8634 小时前
CANN资源泄漏检测工具源码深度解读 实战设备内存泄漏排查
数据库·算法·cann
阿钱真强道4 小时前
12 JetLinks MQTT直连设备事件上报实战(继电器场景)
linux·服务器·网络·数据库·网络协议
逍遥德4 小时前
Sring事务详解之02.如何使用编程式事务?
java·服务器·数据库·后端·sql·spring
笨蛋不要掉眼泪4 小时前
Redis哨兵机制全解析:原理、配置与实战故障转移演示
java·数据库·redis·缓存·bootstrap
Coder_Boy_4 小时前
基于SpringAI的在线考试系统-整体架构优化设计方案
java·数据库·人工智能·spring boot·架构·ddd
fen_fen13 小时前
Oracle建表语句示例
数据库·oracle
砚边数影15 小时前
数据可视化入门:Matplotlib 基础语法与折线图绘制
数据库·信息可视化·matplotlib·数据可视化·kingbase·数据库平替用金仓·金仓数据库