一、常用场景
读写分离:写走主库,读走从库
分库分表:通常有两种路由算法,范围或hash。
二、读写分离实现方法
spring提供了抽象类AbstractRoutingDataSource,里面有两个重要的参数,
targetDataSources代表提供的数据源。
defaultTargetDataSource代表默认数据源。
public void setTargetDataSources(Map targetDataSources) { this.targetDataSources = targetDataSources;}public void setDefaultTargetDataSource(Object defaultTargetDataSource) { this.defaultTargetDataSource = defaultTargetDataSource;}
还有一个抽象方法
@Nullableprotected abstract Object determineCurrentLookupKey();
我们可以继承AbstractRoutingDataSource,实现determineCurrentLookupKey方法进行动态路由数据源。实现如下:
public class DynamicRoutingDataSource extends AbstractRoutingDataSource { private static ThreadLocal ROUTING_KEY = new ThreadLocal(); @Override protected Object determineCurrentLookupKey() { return ROUTING_KEY.get(); } public static void setRoutingKey(String routingKey) { ROUTING_KEY.set(routingKey); } public static void removeRoutingKey() { ROUTING_KEY.remove(); } //不用重写改方法,这里是为了打印数据源信息 @Override public Connection getConnection() throws SQLException { DataSource dataSource = this.determineTargetDataSource(); logger.info(dataSource); return dataSource.getConnection(); }}
2.基于注解的方式,实现动态切换数据源
@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Master {}@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Slave {}@Aspect@Component@Order(-99)public class MasterAop { @Pointcut(“@annotation(com.example.demo.aop.Master)”) public void recordAspect() {} @Around(“recordAspect()”) public Object recordAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { try { DynamicRoutingDataSource.setRoutingKey(“master”); return proceedingJoinPoint.proceed(); } finally { DynamicRoutingDataSource.removeRoutingKey(); } }}@Aspect@Component@Order(-99)public class SlaveAop { @Pointcut(“@annotation(com.example.demo.aop.Slave)”) public void recordAspect() { } @Around(“recordAspect()”) public Object recordAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { try { DynamicRoutingDataSource.setRoutingKey(“slave”); return proceedingJoinPoint.proceed(); } finally { DynamicRoutingDataSource.removeRoutingKey(); } }}
3.配置数据源
@Bean@Primarypublic DataSource dataSource() { DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(); HikariConfig masterHikariConfig = new HikariConfig(); masterHikariConfig.setPassword(“xx”); masterHikariConfig.setUsername(“xx”); masterHikariConfig.setDriverClassName(“com.mysql.cj.jdbc.Driver”); masterHikariConfig.setJdbcUrl(“jdbc:mysql://xxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true”); masterHikariConfig.setPoolName(“masterPool”); HikariDataSource masterDataSource = new HikariDataSource(masterHikariConfig); HikariConfig slaveHikariConfig = new HikariConfig(); slaveHikariConfig.setPassword(“xx”); slaveHikariConfig.setUsername(“xx”); slaveHikariConfig.setDriverClassName(“com.mysql.cj.jdbc.Driver”); slaveHikariConfig.setJdbcUrl(“jdbc:mysql://xxxx?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true”); slaveHikariConfig.setPoolName(“slavePool”); HikariDataSource slaveDataSource = new HikariDataSource(slaveHikariConfig); HashMap targetDataSources = new HashMap(); targetDataSources.put(“master”, masterDataSource); targetDataSources.put(“slave”, slaveDataSource); dynamicRoutingDataSource.setTargetDataSources(targetDataSources); return dynamicRoutingDataSource;}
4.实现一个测试Service
@Service@Slf4jpublic class MasterSlaveService { @Slave @Transactional public void slaveTest() { } @Master @Transactional public void masterTest() { }}
5.测试如下
@SpringBootApplication@EnableTransactionManagementpublic class RoutingDataSourceDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(RoutingDataSourceDemoApplication.class, args); MasterSlaveService bean = run.getBean(MasterSlaveService.class); bean.masterTest(); bean.slaveTest(); }}
我们可以看到数据源的切换信息
com.zaxxer.hikari.HikariDataSource : masterPool – Starting…com.zaxxer.hikari.HikariDataSource : masterPool – Start completed.com.zaxxer.hikari.HikariDataSource : slavePool – Starting…com.zaxxer.hikari.HikariDataSource : slavePool – Start completed.c.e.d.RoutingDataSourceDemoApplication : Started RoutingDataSourceDemoApplication in 2.054 seconds (JVM running for 2.682)c.e.d.d.DynamicRoutingDataSource : HikariDataSource (masterPool)c.e.d.d.DynamicRoutingDataSource : HikariDataSource (slavePool)
三、其他
如果业务简单,我们可以自己实现数据源的切换,如果复杂的话,建议使用ShardingSphere框架,ShardingSphere是基于更底层的jdbc代理实现。