您的位置:

druid 多数据源详解

一、druid 多数据源卡顿

在使用 druid 多数据源的过程中,有时候我们会遇到卡顿的情况。这主要是因为多个数据源使用同一连接池,这会导致连接池资源的竞争。因此,为了解决这个问题,我们可以考虑对每个数据源都单独建立一个连接池。


    @Bean(name = "ds1")
    @ConfigurationProperties(prefix = "spring.datasource.ds1")
    public DataSource ds1() {
        return DruidDataSourceBuilder.create().build();
    }
 
    @Bean(name = "ds2")
    @ConfigurationProperties(prefix = "spring.datasource.ds2")
    public DataSource ds2() {
        return DruidDataSourceBuilder.create().build();
    }

在每个数据源的配置中加入 initialSizemaxActive 属性,分别设置连接池的初始大小和最大连接数,以避免资源竞争问题。


    # 连接池初始大小
    spring.datasource.druid.initialSize=5
    # 连接池最大活跃数量
    spring.datasource.druid.maxActive=20

二、druid 多数据源监控页面

druid 多数据源提供了监控页面,用于查看连接池的状态与连接信息。我们只需要在项目中引入 druid 的监控依赖,并进行相应的配置即可。


    
    
   
        
    com.alibaba
    
        
    druid-spring-boot-starter
    
        
    1.1.20
    
    
   

在项目配置文件中加入监控页面的相关配置。


    ## druid的相关部分
    # 激活监控功能,允许访问druid监控页面
    spring.datasource.druid.stat-view-servlet.enabled=true
    # 配置druid监控页面的访问路径
    spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
    # 监控用户的账号密码,不设置则可输入任意账号密码进入监控页面
    spring.datasource.druid.stat-view-servlet.login-username=admin
    spring.datasource.druid.stat-view-servlet.login-password=admin

三、druid 多数据源配置

在使用 druid 多数据源的过程中,我们需要在项目中配置多个数据源。这里我们给出一个简单的配置示例。


    ## 数据库1的相关配置
    spring.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
    spring.datasource.ds1.username=root
    spring.datasource.ds1.password=123456
 
    ## 数据库2的相关配置
    spring.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.ds2.url=jdbc:mysql://localhost:3306/test2
    spring.datasource.ds2.username=root
    spring.datasource.ds2.password=123456

四、druid 多数据源切换

在使用 druid 多数据源时,我们需要切换数据源。这里我们介绍两种切换数据源的方法。

方法一:利用 @Qualifier 注解指定数据源名称,实现数据源的动态切换。


    @Autowired
    private ApplicationContext context;
    
    // 切换数据源,指定数据源名称
    public void changeDataSource(String dataSourceName) {
        DataSource dataSource = (DataSource) context.getBean(dataSourceName);
        DynamicDataSourceContextHolder.setDataSource(dynamicDataSource);
    }

方法二:使用多线程与 ThreadLocal 实现数据源的动态切换。


    static class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceContextHolder.getDataSource();
        }
    }
 
    public class DynamicDataSourceContextHolder {
        private static final ThreadLocal
    contextHolder = new ThreadLocal<>();
     
        public static void setDataSource(String dataSourceName) {
            contextHolder.set(dataSourceName);
        }
     
        public static String getDataSource() {
            return contextHolder.get();
        }
     
        public static void clearDataSource() {
            contextHolder.remove();
        }
    }

   

五、druid 多数据源监控

druid 多数据源提供了多种监控指标,包括连接数、并发请求数、SQL 运行时间、错误数等。


    ## 配置druid监控统计功能
    # 数据源监控开关
    spring.datasource.druid.stat.enable=true
    # 配置druid监控统计持续的时间
    spring.datasource.druid.stat.time-between-log-stats-millis=60000
    # 慢SQL查询阈值
    spring.datasource.druid.filter.slowsql.enabled=true
    spring.datasource.druid.filter.slowsql.time-threshold=1000
    # 启用mergeSql功能
    spring.datasource.druid.filter.stat.merge-sql=true
    # SQL合并的阈值
    spring.datasource.druid.filter.stat.slow-sql-millis=1000

六、druid 数据源配置

除了多数据源方案,我们还可以对单个数据源进行配置。可以配置以下选项:初始大小、最大连接数、最小连接数、获取连接的最大等待时间、回收连接的最大等待时间、是否进行前缀截断、是否缓存 Statement、是否允许进行多条 SQL 合并等。


    ## 数据源的相关配置
    spring.datasource.initialSize=5 # 初始化连接数量
    spring.datasource.maxActive=10 # 最大连接数量
    spring.datasource.minIdle=2 # 最小空闲连接数量
    spring.datasource.maxWait=60000 # 获取连接的最大等待时间,单位毫秒
    spring.datasource.timeBetweenEvictionRunsMillis=60000 # 连接回收的时间间隔,单位毫秒
    spring.datasource.minEvictableIdleTimeMillis=300000 # 连接可被回收的最大空闲时间,单位毫秒
    spring.datasource.testWhileIdle=true # 测试连接是否有效
    spring.datasource.validationQuery=SELECT 1 FROM DUAL # 验证 SQL 语句
    spring.datasource.testOnBorrow=false # 是否在从池中取出连接前进行检验
    spring.datasource.testOnReturn=false # 是否在归还到池中前进行检验
    spring.datasource.poolPreparedStatements=true # 是否缓存 Statement 对象
    spring.datasource.maxPoolPreparedStatementPerConnectionSize=20 # 开启缓存后,每个连接允许缓存的 Statement 对象数量
    # 开启对多条 SQL 语句合并批量更新操作的支持,例如 insertBatch,updateBatch 等
    spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

七、druid 多数据源事务

在使用 druid 多数据源时,需要对事务进行特别的处理。我们需要使用 Spring 的 @Transactional 注解,在方法级别上进行事务的注解。同时,我们还需要在每个数据源上绑定单独的事务管理器。


    @Bean(name = "transactionManager1")
    public DataSourceTransactionManager transactionManager1() {
        return new DataSourceTransactionManager(ds1());
    }
 
    @Bean(name = "transactionManager2")
    public DataSourceTransactionManager transactionManager2() {
        return new DataSourceTransactionManager(ds2());
    }

之后,在需要使用事务的方法上加入 @Transactional 注解,并指定数据源名称。


    @Transactional(value = "transactionManager1")
    public void saveUser(User user) {
        userDao.insert(user);
    }
 
    @Transactional(value = "transactionManager2")
    public void saveOrder(Order order) {
        orderDao.insert(order);
    }

八、druid 多数据源连接超时

在使用 druid 多数据源时,我们可能会遇到连接超时的问题。我们可以通过设置超时时间来解决这个问题。


    spring.datasource.druid.time-between-eviction-runs-millis=60000 # 连接回收的时间间隔,单位毫秒
    spring.datasource.druid.min-evictable-idle-time-millis=300000 # 连接可被回收的最大空闲时间,单位毫秒
    spring.datasource.druid.max-evictable-idle-time-millis=1800000 # 连接池中连接的最大超时时间,单位毫秒

九、druid 多数据源创建及使用

在使用 druid 多数据源时,我们需要先创建多个数据源,之后再根据实际情况使用相应的数据源。


    @Configuration
    public class DataSourceConfig {
        @Bean(name = "ds1")
        @ConfigurationProperties(prefix = "spring.datasource.ds1")
        public DataSource ds1() {
            return DruidDataSourceBuilder.create().build();
        }
     
        @Bean(name = "ds2")
        @ConfigurationProperties(prefix = "spring.datasource.ds2")
        public DataSource ds2() {
            return DruidDataSourceBuilder.create().build();
        }
    }
     
    @Component
    public class DataSourceUtil {
        // 注入所有数据源
        @Autowired
        @Qualifier("ds1")
        private DataSource ds1;
     
        @Autowired
        @Qualifier("ds2")
        private DataSource ds2;
     
        public void execute() {
            // 使用ds1数据源
            Connection conn = ds1.getConnection();
            ...
            // 使用ds2数据源
            conn = ds2.getConnection();
            ...
        }
    }