# DynamicDataSource **Repository Path**: potterjoy/DynamicDataSource ## Basic Information - **Project Name**: DynamicDataSource - **Description**: 动态数据源,主备分离 - **Primary Language**: Java - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 12 - **Forks**: 5 - **Created**: 2018-10-05 - **Last Updated**: 2024-01-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DynamicDataSource #### 项目介绍 动态数据源,主备读写分离。 特性: 1. 主备配置支持一主多备 2. 多个备库查询做随机轮询(load balance) 3. 备库故障自动做故障转移(failover),当所有备库down机备库操作可路由到主库(可配置) 4. 备库故障恢复,自动恢复可用,加入可用备库列表 5. 主备路由注解配置,无代码侵入 6. 基于springboot,注解加配置文件即可,开箱即用,配置简单,上手容易 #### 软件架构 读写分离基础依赖于 Spring的 DynamicRoutingDataSource + Mybatis 的 Interceptor Plugin 通过Annotation的方式,将DataSource TransactionManager SqlSessionFactory 和各个Mapper 注入到Spring容器,利用Spring IOC特性,利用bean名称和别名,获取主备数据库对应的Bean,实现读写分离 #### gradle 配置参考 https://gitee.com/potterjoy/pub_config #### 使用说明 1. 引入jar依赖 2. 配置数据库连接配置 3. 在入口方法新增 @DataSource 的注解,并配置对应参数 具体demo: application.properties ```properties # test 主库配置 spring.datasource.test.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.test.driverClassName=com.mysql.jdbc.Driver spring.datasource.test.url=jdbc\:mysql\://\:/?allowMultiQueries\=true&zeroDateTimeBehavior\=convertToNull&useSSL\=false&characterEncoding\=UTF-8 spring.datasource.test.username= spring.datasource.test.password= spring.datasource.test.initialSize=5 spring.datasource.test.minIdle=5 spring.datasource.test.maxActive=20 spring.datasource.test.maxWait=60000 spring.datasource.test.timeBetweenEvictionRunsMillis=60000 spring.datasource.test.minEvictableIdleTimeMillis=300000 spring.datasource.test.validationQuery=SELECT 1 FROM DUAL spring.datasource.test.testWhileIdle=true spring.datasource.test.testOnBorrow=true spring.datasource.test.testOnReturn=true spring.datasource.test.poolPreparedStatements=true spring.datasource.test.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.test.filters=stat,wall,slf4j spring.datasource.test.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #test 备库1配置 spring.datasource.test_slave1.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.test_slave1.driverClassName=com.mysql.jdbc.Driver spring.datasource.test_slave1.url=jdbc\:mysql\://\:/?allowMultiQueries\=true&zeroDateTimeBehavior\=convertToNull&useSSL\=false&characterEncoding\=UTF-8 spring.datasource.test_slave1.username= spring.datasource.test_slave1.password= spring.datasource.test_slave1.initialSize=5 spring.datasource.test_slave1.minIdle=5 spring.datasource.test_slave1.maxActive=20 spring.datasource.test_slave1.maxWait=60000 spring.datasource.test_slave1.timeBetweenEvictionRunsMillis=60000 spring.datasource.test_slave1.minEvictableIdleTimeMillis=300000 spring.datasource.test_slave1.validationQuery=SELECT 1 FROM DUAL spring.datasource.test_slave1.testWhileIdle=true spring.datasource.test_slave1.testOnBorrow=true spring.datasource.test_slave1.testOnReturn=true spring.datasource.test_slave1.poolPreparedStatements=true spring.datasource.test_slave1.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.test_slave1.filters=stat,wall,slf4j spring.datasource.test_slave1.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #test 备库2配置 spring.datasource.test_slave2.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.test_slave2.driverClassName=com.mysql.jdbc.Driver spring.datasource.test_slave2.url=jdbc\:mysql\://\:/?allowMultiQueries\=true&zeroDateTimeBehavior\=convertToNull&useSSL\=false&characterEncoding\=UTF-8 spring.datasource.test_slave2.username= spring.datasource.test_slave2.password= spring.datasource.test_slave2.initialSize=5 spring.datasource.test_slave2.minIdle=5 spring.datasource.test_slave2.maxActive=20 spring.datasource.test_slave2.maxWait=60000 spring.datasource.test_slave2.timeBetweenEvictionRunsMillis=60000 spring.datasource.test_slave2.minEvictableIdleTimeMillis=300000 spring.datasource.test_slave2.validationQuery=SELECT 1 FROM DUAL spring.datasource.test_slave2.testWhileIdle=true spring.datasource.test_slave2.testOnBorrow=true spring.datasource.test_slave2.testOnReturn=true spring.datasource.test_slave2.poolPreparedStatements=true spring.datasource.test_slave2.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.test_slave2.filters=stat,wall,slf4j spring.datasource.test_slave2.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # other 主库配置 spring.datasource.other.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.other.driverClassName=com.mysql.jdbc.Driver spring.datasource.other.url=jdbc\:mysql\://\:/?allowMultiQueries\=true&zeroDateTimeBehavior\=convertToNull&useSSL\=false&characterEncoding\=UTF-8 spring.datasource.other.username= spring.datasource.other.password= spring.datasource.other.initialSize=5 spring.datasource.other.minIdle=5 spring.datasource.other.maxActive=20 spring.datasource.other.maxWait=60000 spring.datasource.other.timeBetweenEvictionRunsMillis=60000 spring.datasource.other.minEvictableIdleTimeMillis=300000 spring.datasource.other.validationQuery=SELECT 1 FROM DUAL spring.datasource.other.testWhileIdle=true spring.datasource.other.testOnBorrow=true spring.datasource.other.testOnReturn=true spring.datasource.other.poolPreparedStatements=true spring.datasource.other.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.other.filters=stat,wall,slf4j spring.datasource.other.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #test 备库1配置 spring.datasource.other_slave1.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.other_slave1.driverClassName=com.mysql.jdbc.Driver spring.datasource.other_slave1.url=jdbc\:mysql\://\:/?allowMultiQueries\=true&zeroDateTimeBehavior\=convertToNull&useSSL\=false&characterEncoding\=UTF-8 spring.datasource.other_slave1.username= spring.datasource.other_slave1.password= spring.datasource.other_slave1.initialSize=5 spring.datasource.other_slave1.minIdle=5 spring.datasource.other_slave1.maxActive=20 spring.datasource.other_slave1.maxWait=60000 spring.datasource.other_slave1.timeBetweenEvictionRunsMillis=60000 spring.datasource.other_slave1.minEvictableIdleTimeMillis=300000 spring.datasource.other_slave1.validationQuery=SELECT 1 FROM DUAL spring.datasource.other_slave1.testWhileIdle=true spring.datasource.other_slave1.testOnBorrow=true spring.datasource.other_slave1.testOnReturn=true spring.datasource.other_slave1.poolPreparedStatements=true spring.datasource.other_slave1.maxPoolPreparedStatementPerConnectionSize=20 spring.datasource.other_slave1.filters=stat,wall,slf4j spring.datasource.other_slave1.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 ``` Application.java ```java @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class}) @DataSourceConfig(master ="test", alias={"t1", "t2"}, slaves = {"oms_order_slave1","oms_order_slave2"}, scannerBasePackages = {"com.crazyteam.test.mapper", "com.crazyteam.t2.mapper"}, mapperLocations= {"classpath*:mapping/test/*.xml", "classpath*:mapping/t2/*.xml"} ) @DataSourceConfig(master ="other", slaves = "other_slave1", scannerBasePackages = "com.crazyteam.other.mapper", mapperLocations="classpath*:mapping/test/*.xml" ) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` !!!注意: 注解中 master/slaves 值 和配置项中配置路径一致 使用方式 ```java // Mapper 默认读写主库 // name= 类名首字母小写 @Resource(name = "testMapper") private TestMapper defaultTestMapper; @Resource(name = "otherMapper") private OtherMapper defaultOtherMapper; // 主库读写Mapper 会取一个 的别名 // name = master + 类名 @Resource(name = "masterTestMapper") private TestMapper masterTestMapper; @Resource(name = "masterOtherMapper") private OtherMapper masterOtherMapper; // 读备库Mapper (如果用这个mapper做写操作,会自动路由到主库, 不建议) // name = slave + 类名 @Resource(name = "slaveTestMapper") private TestMapper slaveTestMapper; @Resource(name = "slaveOtherMapper") private OtherMapper slaveOtherMapper; // test库 事务管理器 主库,因为被库只读 // name = 库名 + TransactionManager @Resource(name = "testTransactionManager") private PlatformTransactionManager testTransactionManager; @Resource(name = "testTransactionManager") private PlatformTransactionManager testTransactionManager; // 事务执行器 是管理器函数式包装 // name = 库名 + TransactionExecutor @Resource(name = "testTransactionExecutor") private TransactionExecutor testTransactionExecutor; @Resource(name = "otherTransactionExecutor") private TransactionExecutor otherTransactionExecutor; ``` #### 参与贡献 1. Fork 本项目 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request