Springboot实现数据库读写分离

前言

由于shardingsphere官方文档的配置参考用了报错,网上的也基本用不了,于是参考其他博客,通过aop手动更改数据源。

参考博客:

第一篇

第二篇

前置操作:配置好mysql的读写分离

Springboot yml配置文件(部分):

spring:
  datasource:
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: 主数据库链接
      username: 用户
      password: 密码
    slave0:
      driver-class-name: com.mysql.cj.jdbc.Driver
      jdbc-url: 从数据库链接
      username: 用户
      password: 密码
    其他从数据库:
      ...

配置了多个数据源,因此需要一个配置类,设置当前使用哪个数据源。

先创建一个枚举类,作为数据源的key:

public enum DBTypeEnum {
    MASTER, SLAVE0;
}

数据源配置类:

@Configuration
public class DataSourceConfig {
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.slave0")
    public DataSource slave0DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                          @Qualifier("slave0DataSource") DataSource slave0DataSource) {
        // 把所有数据源放入map中待更换
        HashMap<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DBTypeEnum.MASTER, masterDataSource);
        targetDataSources.put(DBTypeEnum.SLAVE0, slave0DataSource);

        MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();
        // 设置默认数据源
        myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
        myRoutingDataSource.setTargetDataSources(targetDataSources);
        return myRoutingDataSource;
    }
}

配置mybatis,使用myRoutingDataSource这个数据源

@EnableTransactionManagement(order = 10) // 设置事务优先级,这很重要!!
@Configuration
public class MyBatisConfig {
    @Resource(name = "myRoutingDataSource")
    private DataSource myRoutingDataSource;

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager() {
        return new DataSourceTransactionManager(myRoutingDataSource);
    }
}

把数据源配置到线程上下文中

public class DBContextHolder {
    private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();

    public static DBTypeEnum get() {
        if (contextHolder.get() == null)
            contextHolder.set(DBTypeEnum.MASTER);
        return contextHolder.get();
    }

    public static void master() {
        contextHolder.set(DBTypeEnum.MASTER);
        System.out.println("切换到master");
    }

    public static void slave() {
        contextHolder.set(DBTypeEnum.SLAVE0);
    }
}

重写determineCurrentLookupKey切换数据源

public class MyRoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DBContextHolder.get();
    }
}

最后,通过aop,在每次执行mybatis的前判断sql类型,读操作分配从数据库,写操作分配到主数据库。

根据参考博客,有可能存在必须从主库读的情况,因此加一个注解表示强制从主库读:

@Aspect
@Component
public class DataSourceAop implements Ordered {
    @Pointcut("!@annotation(com.wolf1024hzx.annotation.Master) " +
            "|| execution(* com.wolf1024hzx.service..*.get*(..)))")
    public void readPointcut() {

    }

    @Pointcut("@annotation(com.wolf1024hzx.annotation.Master) " +
            "|| execution(* com.wolf1024hzx.service..*.create*(..)) " +
            "|| execution(* com.wolf1024hzx.service..*.update*(..)) " +
            "|| execution(* com.wolf1024hzx.service..*.delete*(..)) ")
    public void writePointcut() {

    }

    @Before("readPointcut()")
    public void read() {
        DBContextHolder.slave();
    }

    @Before("writePointcut()")
    public void write() {
        DBContextHolder.master();
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

注意这里重写了getOrder方法,这是因为按照第一篇博客的配置,会发现服务器的执行顺序是springboot拿到数据源->尝试执行sql->aop->执行sql,这样一来aop改变数据源就没有效果,全是从主机读写。因此需要把数据库事务的优先级调低,把aop操作的优先级调高,确保数据源被成功切换。

可以通过数据库通用查询日志(general_log)观察读写分离是否成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值