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( 默认 )作为连接池。
这里使用的连接池配置都是基于默认全局参数的,后面会详细介绍。
切换数据源
在需要切换数据源的 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 '超级%'