Spring Boot 3 实现 MySQL 主从数据库之间的数据同步

Spring Boot 3 实现 MySQL 主从数据库之间的数据同步

在实际项目中,为了提高 系统的读性能数据的可用性 ,通常会使用 主从数据库架构 。Spring Boot 提供了对 多数据源 的良好支持,可以轻松配置 主从数据库 的数据同步,实现 读写分离


🎯 方案介绍

我们将通过 Spring Boot 3 来实现以下目标:

  1. 主库(Master) :处理所有的 写操作INSERTUPDATEDELETE)。
  2. 从库(Slave) :处理所有的 读操作SELECT)。

通过 读写分离 的方式,我们可以有效减轻主库的压力,同时提升系统的读性能。


📋 步骤 1:配置 MySQL 主从同步

首先,确保你的 MySQL 主从服务器已经配置好。

如果你还没有配置主从,请参考以下步骤:

  1. 主库 上启用 binlog 日志。
  2. 从库 上配置 CHANGE MASTER TO 语句。

具体配置可以参考这里的指南:

👉 MySQL 主从同步配置


📋 步骤 2:Spring Boot 多数据源配置

1️⃣ 添加依赖

pom.xml 文件中添加 MySQL 驱动Spring Data JPA 依赖。

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2️⃣ 配置 application.properties

配置 主库从库 的数据源。

properties 复制代码
# 主库数据源
spring.datasource.master.url=jdbc:mysql://localhost:3306/master_db
spring.datasource.master.username=root
spring.datasource.master.password=master_password
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver

# 从库数据源
spring.datasource.slave.url=jdbc:mysql://localhost:3306/slave_db
spring.datasource.slave.username=root
spring.datasource.slave.password=slave_password
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver

3️⃣ 配置多数据源

创建 MasterDataSourceConfig
java 复制代码
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.example.repository",
        entityManagerFactoryRef = "masterEntityManager",
        transactionManagerRef = "masterTransactionManager"
)
public class MasterDataSourceConfig {

    @Primary
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "masterEntityManager")
    public LocalContainerEntityManagerFactoryBean masterEntityManager(EntityManagerFactoryBuilder builder,
                                                                      @Qualifier("masterDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.entity")
                .persistenceUnit("master")
                .build();
    }

    @Primary
    @Bean(name = "masterTransactionManager")
    public PlatformTransactionManager masterTransactionManager(
            @Qualifier("masterEntityManager") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

创建 SlaveDataSourceConfig
java 复制代码
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = "com.example.repository",
        entityManagerFactoryRef = "slaveEntityManager",
        transactionManagerRef = "slaveTransactionManager"
)
public class SlaveDataSourceConfig {

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "slaveEntityManager")
    public LocalContainerEntityManagerFactoryBean slaveEntityManager(EntityManagerFactoryBuilder builder,
                                                                     @Qualifier("slaveDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.example.entity")
                .persistenceUnit("slave")
                .build();
    }

    @Bean(name = "slaveTransactionManager")
    public PlatformTransactionManager slaveTransactionManager(
            @Qualifier("slaveEntityManager") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

4️⃣ 实现读写分离的 AOP 拦截器

为了自动将 写操作 路由到主库、读操作 路由到从库,可以使用 AOP 来实现。

创建 DataSourceContextHolder
java 复制代码
public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();

    public static void setDataSource(String dataSource) {
        CONTEXT.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT.get();
    }

    public static void clear() {
        CONTEXT.remove();
    }
}
创建 DynamicDataSource
java 复制代码
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}
创建 DataSourceAspect
java 复制代码
@Aspect
@Component
public class DataSourceAspect {

    @Pointcut("execution(* com.example.service..*.read*(..))")
    public void readPointcut() {}

    @Pointcut("execution(* com.example.service..*.write*(..))")
    public void writePointcut() {}

    @Before("readPointcut()")
    public void useSlaveDataSource() {
        DataSourceContextHolder.setDataSource("slaveDataSource");
    }

    @Before("writePointcut()")
    public void useMasterDataSource() {
        DataSourceContextHolder.setDataSource("masterDataSource");
    }

    @After("readPointcut() || writePointcut()")
    public void clearDataSource() {
        DataSourceContextHolder.clear();
    }
}

📋 步骤 3:测试读写分离

创建一个测试服务类
java 复制代码
@Service
public class UserService {

    @Transactional
    public void writeUser(User user) {
        userRepository.save(user);
    }

    @Transactional(readOnly = true)
    public List<User> readUsers() {
        return userRepository.findAll();
    }
}
测试读写操作
java 复制代码
@RestController
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public void createUser(@RequestBody User user) {
        userService.writeUser(user);
    }

    @GetMapping
    public List<User> getUsers() {
        return userService.readUsers();
    }
}

🎯 总结

主从同步架构的优势:

优势 描述
提升读性能 将大量读请求分发到从库,减轻主库压力
提高可用性 从库可用作备份库,主库故障时从库可切换为主库
数据一致性保障 通过半同步或异步复制保障数据一致性
相关推荐
百结21416 小时前
Mysql数据库操作
数据库·mysql·oracle
keep one's resolveY17 小时前
时区问题解决
数据库
Leinwin17 小时前
OpenClaw 多 Agent 协作框架的并发限制与企业化规避方案痛点直击
java·运维·数据库
qq_4176950517 小时前
机器学习与人工智能
jvm·数据库·python
漫随流水17 小时前
旅游推荐系统(view.py)
前端·数据库·python·旅游
ego.iblacat17 小时前
MySQL 服务基础
数据库·mysql
enjoy嚣士18 小时前
springboot之Exel工具类
java·spring boot·后端·easyexcel·excel工具类
Maverick0619 小时前
Oracle Redo 日志操作手册
数据库·oracle
攒了一袋星辰19 小时前
高并发强一致性顺序号生成系统 -- SequenceGenerator
java·数据库·mysql
小涛不学习19 小时前
Spring Boot 详解(从入门到原理)
java·spring boot·后端