mysql从库数据源_java实现mysql数据库读写分离之定义多数据源方式

该示例是基于spring提供的AbstractRoutingDataSource,实现了一个动态数据源的功能,在spring配置中定义多个数据库分为主、从数据库,实现效果为当进行保存和修改记录时则对主表操作,查询则对从表进行操作,从而实现对数据库表的读写分离。这样做有利于提高网站的性能,特别是在数据库这一层。因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验。我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力。该示例并未对数据库同步进行说明,只对读写操作的分离实现:

在进行操作之前,先简单说一下AbstractRoutingDataSource相关的东西:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

1 AbstractRoutingDataSource继承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子类。DataSource 是javax.sql 的数据源接口,定义如下:2

3 public abstract class AbstractRoutingDataSource extends AbstractDataSource implementsInitializingBean {}4

5 public interface DataSource extendsCommonDataSource,Wrapper {6

7 /**

8 *

Attempts to establish a connection with the data source that9 * this DataSource object represents.10 *11 *@returna connection to the data source12 *@exceptionSQLException if a database access error occurs13 */

14 Connection getConnection() throwsSQLException;15

16 /**

17 *

Attempts to establish a connection with the data source that18 * this DataSource object represents.19 *20 *@paramusername the database user on whose behalf the connection is21 * being made22 *@parampassword the user‘s password23 *@returna connection to the data source24 *@exceptionSQLException if a database access error occurs25 *@since1.426 */

27 Connection getConnection(String username, String password)28 throwsSQLException;29

30 }31

32

33 public Connection getConnection() throwsSQLException {34 returndetermineTargetDataSource().getConnection();35 }36

37 public Connection getConnection(String username, String password) throwsSQLException {38 returndetermineTargetDataSource().getConnection(username, password);39 }40

41 protectedDataSource determineTargetDataSource() {42 Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");43 Object lookupKey =determineCurrentLookupKey();44 DataSource dataSource = this.resolvedDataSources.get(lookupKey);45 if (dataSource == null && (this.lenientFallback || lookupKey == null)) {46 dataSource = this.resolvedDefaultDataSource;47 }48 if (dataSource == null) {49 throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");50 }51 returndataSource;52 }

View Code

从上面的代码中不难看出,获取数据源首先是通过对determineCurrentLookupKey()的调用获取resolvedDataSources对应key的值,故执行创建一个动态数据源类继承AbstractRoutingDataSource,复写determineCurrentLookupKey()去自定义设置和获取resolvedDataSources的key就可以实现了

具体步骤如下:

第一步:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

default-lazy-init="true">

class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">

class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

View Code

第二步:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

1 public class DynamicDataSource extendsAbstractRoutingDataSource {2

3 @Override4 protectedObject determineCurrentLookupKey() {5

6 returnDynamicDataSourceHolder.getDataSouce();7 }8

9 }

创建动态数据源类继承AbstractRoutingDataSource

第三步:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

1 public classDynamicDataSourceHolder {2 public static final ThreadLocal holder = new ThreadLocal();3

4 public static voidputDataSource(String name) {5 holder.set(name);6 }7

8 public staticString getDataSouce() {9 returnholder.get();10 }11 }

设置及获取每个线程访问的哪个数据源

第四步:

2b65ef29a5872cc0e4771c25889edd04.gif

6a087676c59fa8b19d76e6bb55a32902.gif

1 @Service("userService")2 @Transactional3 public class UserServiceImpl implementsUserService{4

5 @Autowired6 private UserMapper userDao;public voidadd(User user) {7

8 DynamicDataSourceHolder.putDataSource("masterdataSource");9 userDao.add(user);10 }11

12 public voidupdate(User user) {13

14 DynamicDataSourceHolder.putDataSource("masterdataSource");15 userDao.updates(user);16

17

18 }19

20 @Transactional(propagation =Propagation.NOT_SUPPORTED)21 public Listquery() {22

23 DynamicDataSourceHolder.putDataSource("slavedataSource");24 List user =userDao.query();25 returnuser;26

27 }28

29

30

31

32 }

对service实现层加入设置数据源代码

上述为实现读写分离的关键部分,只是为了简单的做一个示例,完成上面操作以后,可自行的对数据库进行新增和查询操作,查看效果

原文:http://www.cnblogs.com/HeQiangJava/p/7287740.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Spring Boot应用中使用多数据源可以实现读写分离,提高系统的性能和可用性,同时也可以满足不同业务场景下的需求。下面我们来介绍如何使用Spring Boot多数据源实现读写分离。 1. 添加依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> ``` 其中,spring-boot-starter-jdbc是Spring Boot提供的JDBC Starter,用于连接数据库。druid是阿里巴巴开源的数据库连接池。 2. 配置数据源 在application.yml文件中添加数据源配置: ``` spring: datasource: master: url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver ``` 其中,我们配置了两个数据源:master和slave。分别连接了两个不同的数据库,用于实现读写分离。 3. 配置数据源和事务管理器 在Spring Boot中,我们需要自己配置数据源和事务管理器。可以在代码中通过@Primary和@Qualifier注解实现数据源的动态切换。 ``` @Configuration public class DataSourceConfig { @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean(name = "dynamicDataSource") @Primary public DynamicDataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DynamicDataSource.DataSourceType.MASTER, masterDataSource); targetDataSources.put(DynamicDataSource.DataSourceType.SLAVE, slaveDataSource); return new DynamicDataSource(masterDataSource, targetDataSources); } @Bean(name = "transactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("dynamicDataSource") DynamicDataSource dynamicDataSource) { return new DataSourceTransactionManager(dynamicDataSource); } } ``` 其中,我们定义了DataSourceConfig配置类,用于配置数据源和事务管理器。我们通过@Bean注解创建数据源对象并读取application.yml配置文件中的数据源信息。然后通过@Primary注解指定默认的数据源为Master数据源。最后,我们创建了DynamicDataSource对象,用于动态切换数据源,同时创建了事务管理器对象,用于管理事务。 4. 定义数据源切换注解 我们可以通过自定义注解来实现动态切换数据源。 ``` @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { DynamicDataSource.DataSourceType value() default DynamicDataSource.DataSourceType.MASTER; } ``` 其中,@TargetDataSource注解用于标记数据源类型,我们通过value()方法指定数据源类型,默认为Master数据源。 5. 定义动态数据源 我们可以通过继承AbstractRoutingDataSource类来实现动态数据源的切换。 ``` public class DynamicDataSource extends AbstractRoutingDataSource { private DataSource masterDataSource; private Map<Object, Object> targetDataSources; public DynamicDataSource(DataSource masterDataSource, Map<Object, Object> targetDataSources) { this.masterDataSource = masterDataSource; this.targetDataSources = targetDataSources; super.setDefaultTargetDataSource(masterDataSource); super.setTargetDataSources(targetDataSources); super.afterPropertiesSet(); } @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); } public enum DataSourceType { MASTER, SLAVE } } ``` 其中,我们定义了DynamicDataSource类,继承了AbstractRoutingDataSource类。在DynamicDataSource类中,我们重写了determineCurrentLookupKey()方法,根据当前线程的数据源类型,返回对应的数据源。我们通过枚举类型DataSourceType定义了Master和Slave两种数据源类型。 6. 实现数据源切换 我们可以通过AOP的方式,在方法执行前切换数据源。 ``` @Aspect @Component public class DataSourceAspect { @Pointcut("@annotation(com.example.demo.annotation.TargetDataSource)") public void dataSourcePointCut() { } @Around("dataSourcePointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { MethodSignature signature = (MethodSignature) point.getSignature(); TargetDataSource dataSource = signature.getMethod().getAnnotation(TargetDataSource.class); if (dataSource == null) { DataSourceContextHolder.setDataSourceType(DynamicDataSource.DataSourceType.MASTER); } else { DataSourceContextHolder.setDataSourceType(dataSource.value()); } try { return point.proceed(); } finally { DataSourceContextHolder.clearDataSourceType(); } } } ``` 其中,我们定义了DataSourceAspect切面类,用于切换数据源。我们通过@Around注解标记了切点,在方法执行前,根据注解中指定的数据源类型,切换数据源。执行完毕后,再切换回默认的Master数据源。 7. 编写业务代码 最后,我们编写业务代码,通过@TargetDataSource注解指定数据源类型。 ``` @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Override @TargetDataSource(DynamicDataSource.DataSourceType.MASTER) public void addUser(User user) { userMapper.addUser(user); } @Override @TargetDataSource(DynamicDataSource.DataSourceType.SLAVE) public User getUserById(int id) { return userMapper.getUserById(id); } } ``` 其中,我们定义了UserServiceImpl类,实现了UserService接口。在addUser()方法中,我们指定数据源类型为Master数据源,在getUserById()方法中,我们指定数据源类型为Slave数据源。 通过以上步骤,我们就可以实现Spring Boot多数据源读写分离了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董德生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值