Spring + JPA实现数据库读写分离

    SQL的读写分离方案,是一种通用的设计,通常可以较好的提升数据库集群的整体负载能力,当一个mysql实例无法支撑客户端的读写负载时,我们首先会想到对数据库进行“读写分离”

    1)在数据库的架构层面,我们使用M-S架构模式,即一主多从,Master主要用于处理write、transaction等核心操作,这些操作必须发生在master上,否则将会导致数据一致性问题。对于slaves,通常用于分流read操作,对于那些对数据实时性要求不高、批量读取、SLOW SQL等操作,我们应该将它们分配到slaves中。

 

    2)能进行读写分离的前提,一个非常重要的指标,就是“集群的R:W比较高”,即较多的read、较少的write,如果读写比很低,读写分离所产生的收益,将是比较微弱的。

 

    3)从架构的视角考虑,Master与slave在整个timeline中,所需要处理的物理write操作是相同的,即在master上发生一个write操作,此操作也必将在slaves上发生(可能时间线不一致,但是最终一定会发生,这取决于binlog和mysql IO线程的处理效率)。因此,粗略来讲,slaves所面对的writes负载与master一致。这也限定我们,“读写比较低”的应用场景下,读写分离收益较低的原因。

 

    4)slaves机器的物理性能,应该与master保持一致,甚至可以更好,毕竟,大量的reads操作需要等待slaves去处理,这些load是master所不会面对的。

 

    本文展示了如何在Spring环境中使用JPA实现dataSource的读写分离(本文没有使用JTA事务),这个东西看起来简单,其实实现起来比较蹩脚,与JDBC有很大区别。

    1)使用Spring中的AbstractRoutingDataSource,辅助程序在运行时选择合适的dataSource。

    2)可以使用@Master、@Slave注释来强制dao方法调用必须使用master或者slave的数据库源。

    3)本例提供的ReadWriteDataSourceRouter可以根据当前Transaction的readOnly特性,将SQL调用按需分发给master或者slaves;可以指定多个slaves,可以简单的负载均衡。

 

1、persistence.xml

    如果我们不适用JTA事务的话,这个文件可以为空即可。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud可以通过使用动态数据源实现数据库读写分离,具体步骤如下: 1. 引入相关依赖 在Spring Boot项目的pom.xml文件中引入以下依赖: ``` <!-- MyBatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency> <!-- Druid 数据库连接池 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> <!-- HikariCP 数据库连接池 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>3.4.5</version> </dependency> <!-- Spring Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 配置数据源 在application.properties或application.yml文件中配置数据源,例如: ``` # 主数据源 spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/masterdb?useUnicode=true&characterEncoding=utf-8 spring.datasource.master.username=root spring.datasource.master.password=123456 spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver # 从数据源 spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/slavedb?useUnicode=true&characterEncoding=utf-8 spring.datasource.slave.username=root spring.datasource.slave.password=123456 spring.datasource.slave.driver-class-name=com.mysql.jdbc.Driver ``` 3. 配置动态数据源 创建动态数据源配置类,例如: ``` @Configuration public class DynamicDataSourceConfig { @Bean @ConfigurationProperties("spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties("spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } @Bean public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DataSourceType.MASTER, masterDataSource); targetDataSources.put(DataSourceType.SLAVE, slaveDataSource); return new DynamicDataSource(masterDataSource, targetDataSources); } } ``` 其中,masterDataSource()和slaveDataSource()方法分别返回主数据源和从数据源,而dynamicDataSource()方法则创建一个动态数据源,该数据源包含主数据源和从数据源,且可以根据具体的业务需求动态切换数据源。 4. 创建数据源切换类 创建数据源切换类,例如: ``` public class DataSourceContextHolder { private static final ThreadLocal<DataSourceType> contextHolder = new ThreadLocal<>(); public static void setDataSourceType(DataSourceType dataSourceType) { contextHolder.set(dataSourceType); } public static DataSourceType getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } } ``` 该类使用ThreadLocal存储数据源类型,setDataSourceType()方法设置当前线程使用的数据源类型,getDataSourceType()方法获取当前线程使用的数据源类型,clearDataSourceType()方法清空当前线程使用的数据源类型。 5. 创建数据源切面类 创建数据源切面类,例如: ``` @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.example.demo.annotation.DataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); DataSource dataSource = method.getAnnotation(DataSource.class); if (dataSource == null) { DataSourceContextHolder.setDataSourceType(DataSourceType.MASTER); } else { DataSourceContextHolder.setDataSourceType(dataSource.value()); } try { return point.proceed(); } finally { DataSourceContextHolder.clearDataSourceType(); } } } ``` 该类使用@Aspect注解声明为切面类,使用@Pointcut注解定义切点,使用@Around注解定义环绕通知,在方法执行前根据注解中指定的数据源类型切换数据源,在方法执行后清空数据源类型。 6. 根据具体业务需求使用动态数据源 在需要使用动态数据源的地方,使用@DataSource注解指定数据源类型,例如: ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override @DataSource(DataSourceType.MASTER) public int addUser(User user) { return userMapper.addUser(user); } @Override @DataSource(DataSourceType.SLAVE) public User getUserById(int id) { return userMapper.getUserById(id); } } ``` 以上就是使用Spring Cloud实现数据库读写分离的步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值