当前位置: 代码网 > it编程>编程语言>Java > Spring Boot + MySQL读写分离实现方案全过程

Spring Boot + MySQL读写分离实现方案全过程

2025年12月12日 Java 我要评论
一、mysql主从复制搭建(一主一从)1. 主库配置# 1. 修改主库配置文件 /etc/my.cnf[mysqld]server-id=1 # 唯一idlog-bin=mysql-bin # 启用二

一、mysql主从复制搭建(一主一从)

1. 主库配置

# 1. 修改主库配置文件 /etc/my.cnf
[mysqld]
server-id=1 # 唯一id
log-bin=mysql-bin # 启用二进制日志
binlog-format=mixed # 二进制日志格式

重启mysql后执行:

# 2. 创建复制账号
create user 'repl'@'%' identified by 'repl_password';
grant replication slave on *.* to 'repl'@'%';

# 3. 查看二进制日志坐标
show master status;
# 记录file和position,例如:file 'mysql-bin.000001', position 154

2. 从库配置

# 1. 修改从库配置文件 /etc/my.cnf
[mysqld]
server-id=2 # 必须与主库不同
relay-log=mysql-relay-bin # 中继日志
log-slave-updates=1 # 使从库也能作为其他从库的主库

重启mysql后执行:

# 2. 配置主从连接
change master to 
master_host='主库ip',
master_user='repl',
master_password='repl_password',
master_log_file='mysql-bin.000001',
master_log_pos=154;

# 3. 启动复制
start slave;

# 4. 验证
show slave status\g
# 确认slave_io_running和slave_sql_running都是yes

💡 小贴士:主从复制需要确保网络通畅,主库防火墙开放3306端口,从库能访问主库的3306端口。

二、spring boot读写分离实现(推荐dynamic-datasource)

1. 添加maven依赖(pom.xml)

<dependencies>
    <!-- spring boot starter -->
    <dependency>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-web</artifactid>
    </dependency>
    
    <!-- mybatis-plus -->
    <dependency>
        <groupid>com.baomidou</groupid>
        <artifactid>mybatis-plus-boot-starter</artifactid>
        <version>3.5.3.2</version>
    </dependency>
    
    <!-- dynamic-datasource(核心) -->
    <dependency>
        <groupid>com.baomidou</groupid>
        <artifactid>dynamic-datasource-spring-boot-starter</artifactid>
        <version>3.6.1</version>
    </dependency>
    
    <!-- mysql驱动 -->
    <dependency>
        <groupid>mysql</groupid>
        <artifactid>mysql-connector-java</artifactid>
        <version>8.0.33</version>
    </dependency>
    
    <!-- 连接池(可选) -->
    <dependency>
        <groupid>com.alibaba</groupid>
        <artifactid>druid-spring-boot-starter</artifactid>
        <version>1.2.16</version>
    </dependency>
</dependencies>

2. 配置文件(application.yml)

spring:
  datasource:
    dynamic:
      primary: master # 默认数据源
      datasource:
        master:
          url: jdbc:mysql://主库ip:3306/db_name?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai
          username: root
          password: master_password
          driver-class-name: com.mysql.cj.jdbc.driver
          hikari:
            maximum-pool-size: 10
            minimum-idle: 5
        slave:
          url: jdbc:mysql://从库ip:3306/db_name?useunicode=true&characterencoding=utf-8&servertimezone=asia/shanghai
          username: root
          password: slave_password
          driver-class-name: com.mysql.cj.jdbc.driver
          hikari:
            maximum-pool-size: 15
            minimum-idle: 8
      strategy:
        # 读写分离策略:读操作走从库,写操作走主库
        # 可选:round_robin(轮询)、random(随机)等
        datasource:
          master: slave

3. 代码实现

a. 定义数据源枚举

public enum datasourcetype {
    master,  // 主库:写操作
    slave    // 从库:读操作
}

b. 数据源上下文管理器

public class datasourcecontextholder {
    private static final threadlocal<datasourcetype> context_holder = new threadlocal<>();

    public static void setdatasourcetype(datasourcetype datasourcetype) {
        context_holder.set(datasourcetype);
    }

    public static datasourcetype getdatasourcetype() {
        return context_holder.get() == null ? datasourcetype.master : context_holder.get();
    }

    public static void cleardatasourcetype() {
        context_holder.remove();
    }
}

c. 使用aop标注读写操作

@target({elementtype.method})
@retention(retentionpolicy.runtime)
@documented
public @interface datasource {
    datasourcetype value() default datasourcetype.master;
}

d. aop切面实现

@aspect
@component
public class datasourceaspect {
    @before("@annotation(datasource)")
    public void before(joinpoint point, datasource datasource) {
        datasourcetype type = datasource.value();
        datasourcecontextholder.setdatasourcetype(type);
    }

    @after("@annotation(datasource)")
    public void after(joinpoint point, datasource datasource) {
        datasourcecontextholder.cleardatasourcetype();
    }
}

e. 在service层使用

@service
public class orderservice {
    @autowired
    private ordermapper ordermapper;
    
    // 写操作:默认走主库(也可以显式指定)
    @datasource(datasourcetype.master)
    public void createorder(order order) {
        ordermapper.insert(order);
    }
    
    // 读操作:默认走从库
    @datasource(datasourcetype.slave)
    public order getorderbyid(long id) {
        return ordermapper.selectbyid(id);
    }
}

三、redis和mq整合方案分析

项目引入 redis和mq ,针对【热门数据】的查询可以从redis查询,代码层面往mysql主库执行增删改操作后往mq发送一条消息,然后由消费者将增删改的数据同步到redis中,当然这样的方案只适合少量的增删改操作,不适合大批量的增删改

优点:

  • 保证了数据一致性(先写库,再更新缓存)
  • 适合热点数据缓存
  • 降低数据库压力

问题与建议:

  1. 不适合大批量操作:大批量增删改会导致mq消息量过大,处理延迟高。建议:

    • 对于大批量操作,使用批量同步(如redis的pipeline)而不是mq。
    • 或者使用定时任务定期同步热点数据。
  2. 消息可靠性:需要考虑mq消息丢失、重复消费问题:

    • 使用rocketmq/rabbitmq的事务消息。
    • 添加消息重试机制。
    • 消费者 添加 幂等性 处理。
  3. 更适合的场景

    • 用户信息、商品详情等高频查询数据。
    • 不适用于 订单状态、交易流水等 高一致性 要求的数据。

优化:

@service
public class orderservice {
    @autowired
    private rabbittemplate rabbittemplate;
    
    @datasource(datasourcetype.master)
    @transactional
    public order createorder(order order) {
        // 1. 写数据库
        order savedorder = ordermapper.insert(order);
        
        // 2. 发送mq消息(只针对需要缓存的数据)
        if (iscacheabledata(order)) {
            rabbittemplate.convertandsend("order-cache-topic", 
                new ordercachemessage(savedorder.getid(), "create"));
        }
        
        return savedorder;
    }
}

// 消费者处理
@component
public class ordercacheconsumer {
    @rabbitlistener(queues = "order-cache-queue")
    public void process(ordercachemessage message) {
        if ("create".equals(message.getaction())) {
            order order = ordermapper.selectbyid(message.getorderid());
            redistemplate.opsforvalue().set("order:" + message.getorderid(), order);
        }
        // 处理其他操作...
    }
}

四、总结

  1. 读写分离:使用dynamic-datasource,配置简单,社区支持好,比sharding-jdbc更轻量

  2. redis+mq方案

    • 适合 热点数据 的缓存更新。
    • 不适用于 大批量操作。
    • 建议对需要缓存的数据做分类,只对高频查询的数据使用mq同步。
  3. 建议

    • 在查询方法上使用@datasource(datasourcetype.slave)显式指定,避免默认路由错误。
    • 对于复杂查询,可以考虑在mybatis中使用@select指定数据源。
    • 监控主从延迟,避免从库数据不一致。

🌟 小技巧:在开发阶段,可以在配置中添加dynamic.datasource.log=true,这样能打印出实际使用的数据源,方便排查问题。

到此这篇关于spring boot + mysql读写分离实现方案的文章就介绍到这了,更多相关springboot+mysql读写分离内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com