SpringBoot配置多数据源(动态切换,主从复制,读写分离)

SpringBoot2.x整合多数据源,基于注解动态切换数据源,主从复制,读写分离,多数据源的事务处理

1.多数据源实现主从复制,读写分离?

这里介绍的是MYSQL的主从复制实现及其原理,数据源分为主从,主数据源用于写操作,从数据源用于读操作,实现了读写分离
MYSQL主从复制实现以及原理

2.在配置文件里面配置主从数据源相关信息以及连接池相关信息

##############配置端口
server.port=8089

###########################################################
#**********************多数据源配置*************************
############################################################
######################主数据库配置(写入)##############
spring.datasource.master.url=jdbc:mysql://192.168.223.129:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.master.username=root
spring.datasource.master.password=123456
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
######################从库配置(读取)##############
spring.datasource.slave.url=jdbc:mysql://192.168.223.130:3306/seckill?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.slave.username=root
spring.datasource.slave.password=123456
spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver

####################################druid连接池配置-开始##########################################
#连接池类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
### 配置初始化大小、最小、最大
#数据库连接池初始化大小
spring.datasource.initialSize=100
#最小空闲数
spring.datasource.minIdle=500
#最大活跃数
spring.datasource.maxActive=1000
#配置获取连接等待超时的时间
spring.datasource.maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#测试连接
spring.datasource.validationQuery=select 'x'
#申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
spring.datasource.testWhileIdle=true
#获取连接时执行检测,建议关闭,影响性能
spring.datasource.testOnBorrow=false
#归还连接时执行检测,建议关闭,影响性能
spring.datasource.testOnReturn=false
#是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
spring.datasource.poolPreparedStatements=false
#开启poolPreparedStatements后生效
spring.datasource.maxOpenPreparedStatements=20
#配置扩展插件,常用的插件有=>stat:监控统计  log4j2:日志  wall:防御sql注入
pring.datasource.filters.commons-log.connection-logger-name=stat,wall,log4j2
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=2000
#### Druid WebStatFilter配置,url统计
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions='*.gif,*.png,*.jpg,*.html,*.js,*.css,*.ico,/druid/*'
#### Druid StatViewServlet配置,监控界面配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=root
spring.datasource.druid.stat-view-servlet.login-password=123456
########################druid连接池配置-结束##########################################

###########################################################
#**********************配置日志文件名***********************
############################################################
logging.config=classpath:log4j2-prod.xml
###配置日志输出
spring.datasource.druid.filter.slf4j.enabled=true
spring.datasource.druid.filter.slf4j.statement-create-after-log-enabled=false
spring.datasource.druid.filter.slf4j.statement-close-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-open-after-log-enabled=false
spring.datasource.druid.filter.slf4j.result-set-close-after-log-enabled=false

###########################################################
#**********************不显示排除日志依赖信息****************
############################################################
logging.level.org.springframework.boot.autoconfigure=ERROR

###########################################################
#**********************thymeleaf配置***********************
############################################################
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

###########################################################
#**********************redis配置***********************
############################################################
redis.host=120.78.192.109
redis.port=6379
redis.timeout=3
redis.password=123456
redis.poolMaxTotal=100
redis.poolMaxIdle=50
redis.poolMaxWait=20

3.Springboot实现数据源

1.自定注解类的实现

默认是主数据源

/**
 * 自定义数据源注解,默认主库(master)
 *
 * @author Administrator
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "master";
}
2.配置数据源,以及多数据源事务

/**
 * @author Administrator
 * 配置数据库信息
 */
@Configuration
public class DataSourceConfig {
    /**
     * 主库
     */
    public static final String DB_SECKILL_GOOD = "master";
    /**
     * 用从库
     */
    public static final String DB_SECKILL_USER = "slave";

    /**
     * name中master作为主数据源
     * (@Primary)该注解声明是默认数据源
     *
     * @return 商品数据源
     */
    @Bean(name = "master")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource goodDateSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * name中slave作为从数据源
     * (@Primary)该注解声明是默认数据源
     *
     * @return 用户数据源
     */
    @Bean(name = "slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource userDateSource() {
        return DruidDataSourceBuilder.create().build();
    }

    /**
     * 默认数据源配置和多数据源配置
     *
     * @return 数据源
     */
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(goodDateSource());
        // 配置多数据源
        Map<Object, Object> dataBaseMap = new HashMap<>(16);
        dataBaseMap.put(DB_SECKILL_USER, userDateSource());
        dataBaseMap.put(DB_SECKILL_GOOD, goodDateSource());
        dynamicDataSource.setTargetDataSources(dataBaseMap);
        return dynamicDataSource;
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
// 指定扫描的xml文件所在位置,在配置文件里面配置,会报Invalid bound statement
        Resource[] resources = new PathMatchingResourcePatternResolver()
                .getResources("classpath:mybatis/*.xml");
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(true);
        bean.setMapperLocations(resources);
        bean.setConfiguration(configuration);
        return bean.getObject();
    }

    /**
     * 事务管理
     *
     * @param dataSource 数据源
     * @return 事务管理
     */
    @Bean(name = "sqlTransactionManager")
    public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
3.获得和设置上下文环境 改变上下文数据源的名称
**
 * 获得和设置上下文环境 主要负责改变上下文数据源的名称
 * @author Administrator
 */
@Component
@Slf4j
public class DataSourceContextHolder {
    /**
     * 线程独立
     */
    private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    /**
     * 主库
     */
    public static final String DB_SECKILL_GOOD = "master";


    /**
     * 获取数据源名
     *
     * @return 数据库名
     */
    public static String getDataBaseType() {
        return contextHolder.get();
    }

    /**
     * 设置数据源名(切换数据源)
     *
     * @param dataBase 数据库类型
     */
    public static void setDataBaseType(String dataBase) {
        log.info("设置数据源:" + dataBase);
        contextHolder.set(dataBase);
    }

    /**
     * 清除数据源名
     */
    public static void clearDataBaseType() {
        contextHolder.remove();
    }

}

4.建立动态数据源
/**
 * @author Administrator
 * 建立动态数据源
 */
@Configuration
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
    /**
     * 获取当前数据源并打印日志记录
     *
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        log.error("当前数据源:" + DataSourceContextHolder.getDataBaseType());
        return DataSourceContextHolder.getDataBaseType();
    }
}
5.使用AOP动态切换数据源
/**
 * @author Administrator
 * 动态切换数据源类
 */
@Aspect
@Component
@Order(-1)
@Slf4j
public class DynamicDataSourceAspect {

    @Before("@annotation(DataSource)")
    public void beforeSwitchDataSource(JoinPoint point) {
        //获得当前访问的class
        Class<?> className = point.getTarget().getClass();
        //获得访问的方法名
        String methodName = point.getSignature().getName();
        //得到方法的参数的类型
        Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();
        String dataSource = DataSourceContextHolder.DB_SECKILL_GOOD;
        try {
            // 得到访问的方法对象
            Method method = className.getMethod(methodName, argClass);
            // 判断是否存在@DataSource注解
            if (method.isAnnotationPresent(DataSource.class)) {
                DataSource annotation = method.getAnnotation(DataSource.class);
                // 取出注解中的数据源名
                dataSource = annotation.value();
            }
        } catch (Exception e) {
            log.error("动态切换数据源失败");
        }
        // 切换数据源
        DataSourceContextHolder.setDataBaseType(dataSource);
    }

    @After("@annotation(DataSource)")
    public void afterSwitchDataSource(JoinPoint point) {
        DataSourceContextHolder.clearDataBaseType();
    }
}

4.测试

1.我们前面在配置文件配置了druid的监控界面,登录进去查看数据源

监控界面相关信息

druid监控

登录监控界面(localhost:8089/druid/index.html)

在这里插入图片描述查看数据源,可以看到里面有主从两个数据源
在这里插入图片描述
查看数据源配置,发现连接池配置没有生效,那么只需要按主从那样配置即可生效
在这里插入图片描述
代码测试如下
在这里插入图片描述
5,完整的实现请参考
springboot整合多数据源

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在 Spring Boot 中配置主从数据,需要使用一个名为 DynamicDataSource 的动态数据。该动态数据可以根据不同的数据 key 来动态切换数据,从而实现主从数据切换。 下面是配置步骤: 1. 创建 DynamicDataSource 类 ``` public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceKey(); } } ``` 2. 创建 DataSourceContextHolder 类 ``` public class DataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDataSourceKey(String dataSourceKey) { contextHolder.set(dataSourceKey); } public static String getDataSourceKey() { return contextHolder.get(); } public static void clearDataSourceKey() { contextHolder.remove(); } } ``` 3. 配置数据 在 application.yml 中配置主从数据: ``` spring: datasource: master: url: jdbc:mysql://localhost:3306/masterdb username: root password: root slave: url: jdbc:mysql://localhost:3306/slavedb username: root password: root ``` 4. 配置 DynamicDataSource ``` @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } @Bean public DynamicDataSource dataSource() { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource()); targetDataSources.put("slave", slaveDataSource()); DynamicDataSource dataSource = new DynamicDataSource(); dataSource.setTargetDataSources(targetDataSources); dataSource.setDefaultTargetDataSource(masterDataSource()); return dataSource; } } ``` 5. 使用数据 在 Service 或 Dao 中使用数据: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override @DataSource(DataSourceType.SLAVE) public List<User> listUsers() { return userDao.listUsers(); } @Override @DataSource(DataSourceType.MASTER) public int addUser(User user) { return userDao.addUser(user); } } ``` 在上面的示例中,我们使用 @DataSource 注解来标记使用哪个数据。@DataSource 注解需要一个 DataSourceType 参数,可以是 MASTER 或 SLAVE。其中 MASTER 是默认数据。 这样就可以实现主从数据配置了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值