Java案例如何实现读写分离?

wen java案例 3

Java案例如何实现读写分离?从原理到实战的完整指南

目录导读

  1. 什么是读写分离?核心价值与适用场景
  2. Java实现读写分离的四种主流方案
  3. 案例实战:基于Spring Boot + MyBatis + ShardingSphere实现读写分离
  4. 常见问题与避坑指南(含问答)
  5. 性能优化与监控建议

什么是读写分离?核心价值与适用场景

读写分离是指将数据库的读操作(SELECT)和写操作(INSERT/UPDATE/DELETE)分配到不同的数据库实例上,通常由一个主库(Master)处理写请求,多个从库(Slave)处理读请求,主库通过主从复制机制将数据同步到从库。

Java案例如何实现读写分离?

核心价值

  • 提升吞吐量:将读写压力分散到多个节点,避免单点瓶颈。
  • 提高可用性:主库故障时,从库可快速切换为新的主库。
  • 降低延迟:读请求可以分流到性能更优的从库(如配置更高内存的实例)。

适用场景

  • 读多写少(如内容管理系统、电商商品展示页)
  • 数据实时性要求不高的报表查询
  • 需要横向扩展读能力的业务

Java实现读写分离的四种主流方案

方案 技术栈 特点 适用规模
动态数据源 Spring AbstractRoutingDataSource 轻量级,需手动管理路由规则 中小型项目
ORM框架支持 MyBatis拦截器 + 注解 开发简单,对代码侵入小 中型项目
中间件层 ShardingSphere、Mycat、TDDL 功能强大,支持分库分表+读写分离 大型分布式系统
客户端直连 HAProxy + Keepalived 通过代理层透明切换 运维友好型

推荐方案:对于大多数Java企业级项目,ShardingSphere 是成熟选择,它既支持数据源路由,也兼容Spring生态。


案例实战:基于Spring Boot + MyBatis + ShardingSphere实现读写分离

环境准备

  • JDK 1.8+
  • Spring Boot 2.7.x
  • MySQL 5.7+(配置1主2从)
  • ShardingSphere-JDBC 5.1.2(或更高版本)

步骤1:数据库主从配置

-- 主库(192.168.1.10:3306),从库(192.168.1.11:3306, 192.168.1.12:3306)
-- 主库开启binlog,从库配置change master to...

步骤2:引入依赖(pom.xml)

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.1.2</version>
</dependency>

步骤3:配置读写分离规则(application.yml)

spring:
  shardingsphere:
    datasource:
      names: master,slave1,slave2
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://192.168.1.10:3306/demo
        username: root
        password: master_pwd
      slave1:
        # 同master配置,地址改为从库1
      slave2:
        # 同master配置,地址改为从库2
    rules:
      readwrite-splitting:
        data-sources:
          myds:
            write-data-source-name: master
            read-data-source-names: slave1,slave2
            load-balancer-name: round_robin   # 负载均衡策略
        load-balancers:
          round_robin:
            type: ROUND_ROBIN
    props:
      sql-show: true   # 打印SQL日志,便于调试

步骤4:编写业务代码

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;
    @Transactional   // 写操作必须开启事务,自动路由到主库
    public void insertUser(User user) {
        userMapper.insert(user);
    }
    @Transactional(readOnly = true)  // 读操作加readOnly注解,路由到从库
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
}

关键点说明

  • 默认情况下,ShardingSphere根据事务状态判断路由:有@Transactional且无readOnly时走主库,否则走从库。
  • 如果遇到强一致性要求(如“读写后立即查询”),建议强制走主库:通过HintManager设置hintManager.setMasterRouteOnly()

常见问题与避坑指南(含问答)

Q1:为什么读写分离后,写入数据后立即查询不到?

回答:这是主从复制的固有延迟(通常几十毫秒到几秒),解决方案包括:

  • 将“写后立即读”的请求强制路由到主库(使用HintManager)
  • 开启半同步复制(MySQL 5.7+支持)
  • 业务上容忍短期不一致(如先跳转到“操作成功”页面,再异步查询)

Q2:如何监控从库的复制延迟?

回答:在从库执行 SHOW SLAVE STATUS,关注 Seconds_Behind_Master 字段,可在Java中定时采集并报警,若延迟超过阈值,可动态剔除该从库(ShardingSphere支持自动禁用故障节点)。

Q3:读写分离是否支持分库分表?

回答:是的,ShardingSphere支持将“读写分离”与“分库分表”结合使用,配置时需在rules下同时配置readwrite-splittingsharding注意:分片算法需在读写分离的数据源之上定义。

Q4:遇到“死锁”或“事务不回滚”怎么办?

回答:确保读写分离的从库不要参与写事务,即:不要在从库上执行任何更新操作(即使开启了事务),ShardingSphere会自动拦截非读SQL向从库发送并报错。


性能优化与监控建议

优化方向

  1. 连接池配置:主库写连接数保持适中(如10-20),从库读连接数可放大(如30-50)。
  2. 负载均衡策略:推荐使用ROUND_ROBINWEIGHT(根据从库权重分配流量)。
  3. SQL优化:慢查询日志监控从库,避免复杂JOIN拖垮从库。

监控指标

  • 主从复制延迟(秒级)
  • 各数据源连接池状态(活跃、空闲、等待数)
  • 读写比例(理想情况:读占80%以上,写占20%以下)

推荐工具

  • Prometheus + Grafana:可视化主从延迟和SQL执行分布。
  • SkyWalking:分布式链路追踪,定位读写分离后的慢SQL。

读写分离是提升Java应用数据库性能的经典方案,通过ShardingSphere等中间件可以低侵入地实现,关键在于理解主从复制延迟事务路由规则,并根据业务场景做针对性优化。

抱歉,评论功能暂时关闭!