Documentation

A good document is like an intriguing story.

Usage

动态多数据源启动器,在 dynamic-datasource-spring-boot-starter 项目基础上进行二次开发。

                        Copy
                        
<dependency>
    <groupId>net.dragonshard</groupId>
    <artifactId>dragonshard-dynamic-datasource-starter</artifactId>
    <version>${latest.version}</version>
</dependency>
                        
                    

核心功能

  • 支持数据源分组、纯粹多库、读写分离、一主多从和混合模式。

  • 连接池支持 Druid 和 HikariCP,并提供全局配置。

  • 使用 SpEL 动态参数解析数据源,可以从 session,header 或参数中获取数据源信息。

  • 支持多层数据源嵌套切换。

  • 提供项目启动后增减数据源方案。

先看一组常用配置信息

                                Copy
                                
dragonshard:

  dynamic-datasource:
    primary: master   # 默认的数据源或者数据源组,默认为master
    datasource:
      master: # 默认数据源
        type: com.alibaba.druid.pool.DruidDataSource # 手动指定连接池类型
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/m
        username: root
        password: 123456
      slave_1: # 使用了分组功能,与 slave_2 为一组。
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/s1
        username: root
        password: 123456
      slave_2:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/s2
        username: root
        password: 123456
      oracle: # 多种数据源可以混合使用
        driver-class-name: oracle.jdbc.driver.OracleDriver
        url: jdbc:oracle:thin:@localhost:1521:yy
        username: root
        password: 123456
                                
                            

按以上配置信息启动后,会使用 Druid 作为 master 数据源的连接池。而其它未指定连接池的 slave_1、slave_2 和 oracle 都将使用 HikariCP( 默认 )作为连接池。

这里使用的连接池配置都是基于默认全局参数的,后面会详细介绍。

切换数据源

在 Dragonshard-sample 中查看

在需要切换数据源的 service 类或方法上使用 @DsfAssignDataSource注解,参数就是目标数据源名称,支持以下方式:

  • 1.@DsfAssignDataSource("slave_1"),直接使用 slave_1 数据源。
  • 2.@DsfAssignDataSource("slave"),使用 slave 组数据源,该组下有 slave_1 和 slave_2 两个子库,默认为负载均衡算法。
  • 3.@DsfAssignDataSource("#session.tenantName"),使用SpEL动态解析 Session 中的参数,再切换数据源。
  • 4.@DsfAssignDataSource("#header.tenantName"),使用SpEL动态解析 Header 中的参数。
  • 5.@DsfAssignDataSource("#tenantName"),使用SpEL直接从参数中获取。
  • 6.@DsfAssignDataSource("#user.tenantName"),使用SpEL直接从复杂参数中获取。

第3,4,5种方式为链式解析,默认为:header > session > SpEL,目前暂不可以自定义顺序,后期会加入该功能。

最后给出代码,save()会写入 oracle 数据源,其它方法则会使用 slave_1 数据源。

注解优先级:方法 > 类

                        Copy
                        
@Service
@DsfAssignDataSource("slave_1")
public class RoleServiceImpl extends DsfServiceImpl<RoleMapper, Role> implements IRoleService {

    @Override
    @DsfAssignDataSource("oracle")
    public boolean save(Role entity) {
        return retBool(baseMapper.insert(entity));
    }

    // 其它方法...

}
                        
                    

完整配置

配置项前缀dragonshard.dynamic-datasource

配置项 默认值 类型/选项 描述
primary master String 默认的数据源或者数据源组名称
strict false true / false 严格模式, 在未匹配到指定数据源时会抛出异常。
p6spy false true / false 开启后会输出真实SQL
有一定性能损耗, 建议线上关闭。
[ datasource ] String 自定义名称,例如上面的"slave_1"、"oracle"
strategy 负载均衡算法 Class 多数据源时的选择算法。
order Ordered.HIGHEST_PRECEDENCE Integer aop 切面顺序
global-druid 参考#连接池 Druid 的全局配置
global-hikari 参考#连接池 Hikari 的全局配置

Connection Pool

Dynamic-datasource 支持 Druid 和 HikariCP 连接池,HikariCP 是 Springboot2.x 自带的,也是龙晶默认的连接池,Druid 是通过 druid-spring-boot-starter 导入的。

所以这些连接池的配置都可以直接使用原生 starter 中的配置,这就减少了对原有系统的改动。

                        Copy
                        
  dynamic-datasource:
    primary: master
    # ...
    global-hikari: # 全局配置
      catalog:
      connection-timeout:
      validation-timeout:
      idle-timeout:
      leak-detection-threshold:
      max-lifetime:
      max-pool-size:
      min-idle:
      initialization-fail-timeout:
      connection-init-sql:
      connection-test-query:
      dataSource-class-name:
      dataSource-jndi-name:
      schema:
      transaction-isolation-name:
      is-auto-commit:
      is-read-only:
      is-isolate-internal-queries:
      is-register-mbeans:
      is-allow-pool-suspension:
      health-check-properties:
    global-druid: # 全局配置
      initial-size:
      max-active:
      min-idle:
      max-wait:
      time-between-eviction-runs-millis:
      time-between-log-stats-millis:
      stat-sqlmax-size:
      min-evictable-idle-time-millis:
      max-evictable-idle-time-millis:
      test-while-idle:
      test-on-borrow:
      test-on-return:
      validation-query:
      validation-query-timeout:
      use-global-datasource-stat:
      async-init:
      clear-filters-enable:
      reset-stat-enable:
      not-full-timeout-retry-count:
      max-wait-thread-count:
      fail-fast:
      phyTimeout-millis:
      keep-alive:
      pool-prepared-statements:
      init-variants:
      init-global-variants:
      use-unfair-lock:
      kill-when-socket-read-timeout:
      connection-properties:
        max-pool-prepared-statement-per-connection-size:
        init-connection-sqls:
        share-prepared-statements:
        connection-errorretry-attempts:
        break-after-acquire-failure:
        filters: stat,wall # 注意这个值和druid原生不一致,默认启动了stat,wall
        wall:
          noneBaseStatementAllow:
          callAllow:
          selectAllow:
          selectIntoAllow:
          selectIntoOutfileAllow:
          selectWhereAlwayTrueCheck:
          selectHavingAlwayTrueCheck:
          selectUnionCheck:
          selectMinusCheck:
          selectExceptCheck:
          selectIntersectCheck:
          createTableAllow:
          dropTableAllow:
          alterTableAllow:
          renameTableAllow:
          hintAllow:
          lockTableAllow:
          startTransactionAllow:
          blockAllow:
          conditionAndAlwayTrueAllow:
          conditionAndAlwayFalseAllow:
          conditionDoubleConstAllow:
          conditionLikeTrueAllow:
          selectAllColumnAllow:
          deleteAllow:
          deleteWhereAlwayTrueCheck:
          deleteWhereNoneCheck:
          updateAllow:
          updateWhereAlayTrueCheck:
          updateWhereNoneCheck:
          insertAllow:
          mergeAllow:
          minusAllow:
          intersectAllow:
          replaceAllow:
          setAllow:
          commitAllow:
          rollbackAllow:
          useAllow:
          multiStatementAllow:
          truncateAllow:
          commentAllow:
          strictSyntaxCheck:
          constArithmeticAllow:
          limitZeroAllow:
          describeAllow:
          showAllow:
          schemaCheck:
          tableCheck:
          functionCheck:
          objectCheck:
          variantCheck:
          mustParameterized:
          doPrivilegedAllow:
          dir:
          tenantTablePattern:
          tenantColumn:
          wrapAllow:
          metadataAllow:
          conditionOpXorAllow:
          conditionOpBitwseAllow:
          caseConditionConstAllow:
          completeInsertValuesCheck:
          insertValuesCheckSize:
          selectLimit:
        stat:
          merge-sql:
          log-slow-sql:
          slow-sql-millis:
    datasource:
      master:
        driver-class-name:
        # ...
        hikari: # 单独配置,会覆盖全局参数,但此处没有提示。
          max-pool-size:
          # ...
      slave_1:
        global-druid: # 单独配置
          initial-size:
          # ...
                        
                    

Print SQL

这里是以数据源的角度,输出带有真实参数的SQL,该功能通过 p6spy 实现的。

                            Copy
                            
  dynamic-datasource:
    primary: master
    p6spy: true # 默认false, 有一定性能损耗, 建议线上关闭。
                            
                        

实际输出大概是这个样子
P6SPY Consume Time > 53 ms
P6SPY Execute SQL > SELECT id,role_name, ... FROM sys_role WHERE role_name LIKE '超级%'