数据库读写分离介绍

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

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

📊 核心组件与工作流程

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

  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 可设置超时),避免单点故障导致整个系统不可用。

相关推荐
Codeking__2 小时前
Redis的value类型及编码方式介绍——string
数据库·redis·缓存
ShuiShenHuoLe2 小时前
maven配置阿里源
java·数据库·maven
码农爱学习2 小时前
C语言结构体对齐是怎么计算
java·c语言·数据库
小杨同学492 小时前
C 语言实战:堆内存存储字符串 + 多种递归方案计算字符串长度
数据库·后端·算法
小码编匠2 小时前
完美替代 Navicat,一款开源免费、集成了 AIGC 能力的多数据库客户端工具!
数据库·后端·aigc
linuxxx1102 小时前
正则匹配应用小案例
数据库·正则表达式
末日汐3 小时前
磁盘与文件系统
linux·运维·数据库
win x3 小时前
Redis集群
java·数据库·redis
qq_12498707533 小时前
基于Spring Boot的“味蕾探索”线上零食购物平台的设计与实现(源码+论文+部署+安装)
java·前端·数据库·spring boot·后端·小程序