在 Spring Boot 应用程序中集成 MySQL 主从集群涉及到配置多个数据源,确保主库(Master)用于写操作,而从库(Slave)用于读操作。

1. MySQL 主从配置

首先,你需要配置 MySQL 主从复制。这通常涉及到以下步骤:

  • 在主服务器上启用二进制日志。
  • 在从服务器上配置主服务器的信息,包括服务器地址、用户凭证和要复制的数据库。
  • 在从服务器上启动复制线程。
    有关如何设置 MySQL 主从复制的详细信息,请参阅 MySQL 官方文档。

2. Spring Boot 配置

在你的 Spring Boot 应用程序中,你需要配置多个数据源,一个用于主库,其他用于从库。可以使用 Spring Boot 的数据源配置和 JPA / Spring Data 来完成这一任务。

2.1 Maven 依赖

确保你的 pom.xml 包含以下依赖:

<dependencies>
    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>
2.2 应用属性配置

application.propertiesapplication.yml 文件中,配置主库和从库的数据源:

# 主库配置
spring.datasource.master.url=jdbc:mysql://master_host:port/db_name?useSSL=false
spring.datasource.master.username=db_user
spring.datasource.master.password=db_password
# 从库配置
spring.datasource.slave.url=jdbc:mysql://slave_host:port/db_name?useSSL=false
spring.datasource.slave.username=db_user
spring.datasource.slave.password=db_password
# JPA / Hibernate 配置
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
2.3 数据源配置类

创建一个数据源配置类来配置多个数据源:

@Configuration
public class DataSourceConfig {
    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }
    @Bean(name = "dynamicDataSource")
    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource);
        targetDataSources.put("slave", slaveDataSource);
        return new DynamicDataSource(masterDataSource, targetDataSources);
    }
    @Primary
    @Bean(name = "dynamicSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource);
        return sqlSessionFactoryBean.getObject();
    }
    @Primary
    @Bean(name = "dynamicTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }
}
2.4 动态数据源路由

创建一个动态数据源路由类来实现 AbstractRoutingDataSource,用于在运行时根据某些条件(如方法名或注解)选择使用主库或从库:

public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> CONTEXT_HOLDER = ThreadLocal.withInitial(() -> "master");
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }
    @Override
    protected Object determineCurrentLookupKey() {
        return CONTEXT_HOLDER.get();
    }
    public static void setDataSourceType(String dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }
    public static String getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}
2.5 AOP 切面编程

使用 AOP 来拦截特定的方法,根据方法类型(读或写)动态选择数据源:

@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(TargetDataSource)")
    public void switchDataSource(JoinPoint point) {
        // 获取方法上的注解
        MethodSignature signature = (MethodSignature) point.getSignature();
             Method method = signature.getMethod();        TargetDataSource ds = method.getAnnotation(TargetDataSource.class);       
         if (ds != null) {           
          // 根据注解值设置数据源            

DynamicDataSource.setDataSourceType(ds.name(

));       
          } else {       
               // 如果没有注解,默认使用主库            
               DynamicDataSource.setDataSourceType("master");     
  }  
}   
 
@After("@annotation(TargetDataSource)")    public void restoreDataSource(JoinPoint point) {    
    DynamicDataSource.clearDataSourceType();    

}}
2.6 自定义注解

创建一个自定义注解,用于标记哪些方法应该使用主库,哪些方法应该使用从库:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
    String name() default "master";
}

3. 使用数据源

在你的服务类或 repository 中,使用自定义注解来指定方法应该使用的数据源:

@Service
public class MyService {
    @Autowired
    private MyRepository myRepository;
    @TargetDataSource(name = "master")
    public void saveData(MyEntity entity) {
        myRepository.save(entity);
    }
    @TargetDataSource(name = "slave")
    public MyEntity getDataById(Long id) {
        return myRepository.findById(id).orElse(null);
    }
}

4. 测试

配置完成后,你应该测试应用程序以确保主从数据源配置正确。执行一些写操作来测试主库,执行一些读操作来测试从库,并验证数据是否正确复制。

注意事项

  • 确保主从服务器之间的网络连接是稳定的,并且从服务器能够正确地同步主服务器的数据。
  • 考虑到数据一致性问题,如果你的应用程序需要强一致性,那么可能需要实现一些额外的逻辑来处理从库可能存在的延迟问题。
  • 在生产环境中,可能需要考虑更多的因素,如负载均衡、故障转移等。
    集成 MySQL 主从集群是一个复杂的过程,需要你根据应用程序的具体需求来进行适当的配置和优化。上述步骤提供了一个基本的框架,但可能需要根据实际情况进行调整。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐