spring读写分离 未测试

如何配置mysql数据库的主从?

单机配置mysql主从:http://my.oschina.net/god/blog/496

 

常见的解决数据库读写分离有两种方案

1、应用层

http://neoremind.net/2011/06/spring实现数据库读写分离

目前的一些解决方案需要在程序中手动指定数据源,比较麻烦,后边我会通过AOP思想来解决这个问题。

 

2、中间件

mysql-proxy:http://hi.baidu.com/geshuai2008/item/0ded5389c685645f850fab07

Amoeba for MySQL:http://www.iteye.com/topic/188598http://www.iteye.com/topic/1113437

 

此处我们介绍一种在应用层的解决方案,通过spring动态数据源和AOP来解决数据库的读写分离。

 

该方案目前已经在一个互联网项目中使用了,而且可以很好的工作。

 

该方案目前支持

一读多写;当写时默认读操作到写库、当写时强制读操作到读库。

 

考虑未来支持

读库负载均衡、读库故障转移等。

 

使用场景

不想引入中间件,想在应用层解决读写分离,可以考虑这个方案;

建议数据访问层使用jdbc、ibatis,不建议hibernate。

 

优势

应用层解决,不引入额外中间件;

在应用层支持『当写时默认读操作到写库』,这样如果我们采用这种方案,在写操作后读数据直接从写库拿,不会产生数据复制的延迟问题;

应用层解决读写分离,理论支持任意数据库。

 

 

缺点

1、不支持@Transactional注解事务,此方案要求所有读方法必须是read-only=true,因此如果是@Transactional,这样就要求在每一个读方法头上加@Transactional 且readOnly属性=true,相当麻烦。 :oops: 

2、必须按照配置约定进行配置,不够灵活。

 

两种方案



方案1:当只有读操作的时候,直接操作读库(从库);

        当在写事务(即写主库)中读时,也是读主库(即参与到主库操作),这样的优势是可以防止写完后可能读不到刚才写的数据;

 

此方案其实是使用事务传播行为为:SUPPORTS解决的。

 


方案2:当只有读操作的时候,直接操作读库(从库);

        当在写事务(即写主库)中读时,强制走从库,即先暂停写事务,开启读(读从库),然后恢复写事务。

此方案其实是使用事务传播行为为:NOT_SUPPORTS解决的。

 

核心组件

cn.javass.common.datasource.ReadWriteDataSource:读写分离的动态数据源,类似于AbstractRoutingDataSource,具体参考javadoc;

cn.javass.common.datasource.ReadWriteDataSourceDecision:读写库选择的决策者,具体参考javadoc;

cn.javass.common.datasource.ReadWriteDataSourceProcessor:此类实现了两个职责(为了减少类的数量将两个功能合并到一起了):读/写动态数据库选择处理器、通过AOP切面实现读/写选择,具体参考javadoc。

 

具体配置

1、数据源配置

1.1、写库配置

Java代码   收藏代码
  1.     <bean id="writeDataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">  
  2.     <property name="alias" value="writeDataSource"/>  
  3.     <property name="driver" value="${write.connection.driver_class}" />  
  4.     <property name="driverUrl" value="${write.connection.url}" />  
  5.     <property name="user" value="${write.connection.username}" />  
  6.     <property name="password" value="${write.connection.password}" />  
  7.     <property name="maximumConnectionCount" value="${write.proxool.maximum.connection.count}"/>  
  8.     <property name="minimumConnectionCount" value="${write.proxool.minimum.connection.count}" />  
  9.     <property name="statistics" value="${write.proxool.statistics}" />  
  10.     <property name="simultaneousBuildThrottle" value="${write.proxool.simultaneous.build.throttle}"/>  
  11. </bean>  

 

1.2、读库配置

Java代码   收藏代码
  1. <bean id="readDataSource1" class="org.logicalcobwebs.proxool.ProxoolDataSource">  
  2.     <property name="alias" value="readDataSource"/>  
  3.     <property name="driver" value="${read.connection.driver_class}" />  
  4.     <property name="driverUrl" value="${read.connection.url}" />  
  5.     <property name="user" value="${read.connection.username}" />  
  6.     <property name="password" value="${read.connection.password}" />  
  7.     <property name="maximumConnectionCount" value="${read.proxool.maximum.connection.count}"/>  
  8.     <property name="minimumConnectionCount" value="${read.proxool.minimum.connection.count}" />  
  9.     <property name="statistics" value="${read.proxool.statistics}" />  
  10.     <property name="simultaneousBuildThrottle" value="${read.proxool.simultaneous.build.throttle}"/>  
  11. </bean>   

1.3、读写动态库配置   

通过writeDataSource指定写库,通过readDataSourceMap指定从库列表,从库列表默认通过顺序轮询来使用读库,具体参考javadoc;

Java代码   收藏代码
  1. <bean id="readWriteDataSource" class="cn.javass.common.datasource.ReadWriteDataSource">  
  2.     <property name="writeDataSource" ref="writeDataSource"/>  
  3.     <property name="readDataSourceMap">  
  4.        <map>  
  5.           <entry key="readDataSource1" value-ref="readDataSource1"/>  
  6.           <entry key="readDataSource2" value-ref="readDataSource1"/>  
  7.           <entry key="readDataSource3" value-ref="readDataSource1"/>  
  8.           <entry key="readDataSource4" value-ref="readDataSource1"/>  
  9.        </map>  
  10.     </property>  
  11. </bean>   

 

2、XML事务属性配置

所以读方法必须是read-only(必须,以此来判断是否是读方法)。

Java代码   收藏代码
  1. <tx:advice id="txAdvice" transaction-manager="txManager">  
  2.     <tx:attributes>  
  3.         <tx:method name="save*" propagation="REQUIRED" />  
  4.         <tx:method name="add*" propagation="REQUIRED" />  
  5.         <tx:method name="create*" propagation="REQUIRED" />  
  6.         <tx:method name="insert*" propagation="REQUIRED" />  
  7.         <tx:method name="update*" propagation="REQUIRED" />  
  8.         <tx:method name="merge*" propagation="REQUIRED" />  
  9.         <tx:method name="del*" propagation="REQUIRED" />  
  10.         <tx:method name="remove*" propagation="REQUIRED" />  
  11.           
  12.         <tx:method name="put*" read-only="true"/>  
  13.         <tx:method name="query*" read-only="true"/>  
  14.         <tx:method name="use*" read-only="true"/>  
  15.         <tx:method name="get*" read-only="true" />  
  16.         <tx:method name="count*" read-only="true" />  
  17.         <tx:method name="find*" read-only="true" />  
  18.         <tx:method name="list*" read-only="true" />  
  19.           
  20.         <tx:method name="*" propagation="REQUIRED"/>  
  21.     </tx:attributes>  
  22. </tx:advice>   

 

3、事务管理器

事务管理器管理的是readWriteDataSource

Java代码   收藏代码
  1. <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  2.         <property name="dataSource" ref="readWriteDataSource"/>  
  3.     </bean>   

 

4、读/写动态数据库选择处理器

根据之前的txAdvice配置的事务属性决定是读/写,具体参考javadoc;

forceChoiceReadWhenWrite:用于确定在如果目前是写(即开启了事务),下一步如果是读,是直接参与到写库进行读,还是强制从读库读,具体参考javadoc;

Java代码   收藏代码
  1. <bean id="readWriteDataSourceTransactionProcessor" class="cn.javass.common.datasource.ReadWriteDataSourceProcessor">  
  2.    <property name="forceChoiceReadWhenWrite" value="false"/>  
  3. </bean>   

 

5、事务切面和读/写库选择切面

Java代码   收藏代码
  1. <aop:config expose-proxy="true">  
  2.     <!-- 只对业务逻辑层实施事务 -->  
  3.     <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />  
  4.     <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>  
  5.       
  6.     <!-- 通过AOP切面实现读/写库选择 -->  
  7.     <aop:aspect order="-2147483648" ref="readWriteDataSourceTransactionProcessor">  
  8.        <aop:around pointcut-ref="txPointcut" method="determineReadOrWriteDB"/>  
  9.     </aop:aspect>  
  10. </aop:config>   

1、事务切面一般横切业务逻辑层;

2、此处我们使用readWriteDataSourceTransactionProcessor的通过AOP切面实现读/写库选择功能,order=Integer.MIN_VALUE(即最高的优先级),从而保证在操作事务之前已经决定了使用读/写库。

 

6、测试用例

只要配置好事务属性(通过read-only=true指定读方法)即可,其他选择读/写库的操作都交给readWriteDataSourceTransactionProcessor完成。

 

可以参考附件的:

cn.javass.readwrite.ReadWriteDBTestWithForceChoiceReadOnWriteFalse

cn.javass.readwrite.ReadWriteDBTestWithNoForceChoiceReadOnWriteTrue

 

 

可以下载附件的代码进行测试,具体选择主/从可以参考日志输出。

 

暂不想支持@Transactional注解式事务。

 

PS:欢迎拍砖指正。   

转自

http://jinnianshilongnian.iteye.com/blog/1720618

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot可以通过配置数据源来实现MySQL的分离。 1. 首先需要配置主数据源和从数据源,在application.properties中配置好主数据源的URL、用户名、密码等信息,并在从数据源中配置相同的信息。 2. 然后在配置类中配置数据源的负载均衡策略,例如采用轮询策略。 3. 最后在需要切换数据源的地方使用 @Transactional(readOnly = true) 设置当前事务为只事务,这样就会使用从数据源进行操作。 具体实现可以参考 Spring Boot 的文档或者第三方模块如 druid-spring-boot-starter。 ### 回答2: 在Spring Boot中实现MySQL的分离可以通过以下步骤来完成: 1. 配置MySQL主从复制:首先,需要在服务器上设置好MySQL主从复制,确保主数据库和从数据库之间的数据同步。可以使用binlog日志来实现主从复制。 2. 配置数据源:在Spring Boot的application.properties(或application.yml)文件中,配置两个数据源,一个用于操作,一个用于操作。分别配置主数据库和从数据库的连接信息。 3. 设置数据源路由:使用Spring AOP(面向切面编程)和注解进行数据源的动态切换。可以定义一个切点,用于拦截数据库的访问操作,并根据具体的业务需求切换到对应的数据源。 4. 编分离的数据源配置类:在Spring Boot中,可以自定义一个分离的数据源配置类,用于管理数据源的切换。该类可以使用ThreadLocal来保存当前线程使用的数据源,然后根据具体的业务需求来选择具体的数据源。 5. 配置数据源切换的拦截器:在Spring Boot的配置文件中,配置AOP拦截器,将数据源的切换逻辑应用到具体的业务代码中。 6. 测试分离效果:可以编一些测试用例,测试操作是否成功切换到了对应的数据源。 需要注意的是,分离只能解决数据库的性能问题,并不能解决数据库的高可用问题。因此,在实际生产环境中,还需要考虑到主从数据库之间的数据同步延迟和故障切换等问题。 ### 回答3: 在Spring Boot中实现MySQL的分离,可以采用以下步骤: 1. 引入相关依赖:需要在pom.xml文件中引入spring-boot-starter-data-jpa和mysql-connector-java相关依赖。 2. 配置数据源:在application.properties文件中配置主从数据源的连接信息。例如: ``` spring.datasource.url=jdbc:mysql://主数据库IP:主数据库端口/主数据库名称 spring.datasource.username=主数据库用户名 spring.datasource.password=主数据库密码 spring.datasource.slave.url=jdbc:mysql://从数据库IP:从数据库端口/从数据库名称 spring.datasource.slave.username=从数据库用户名 spring.datasource.slave.password=从数据库密码 ``` 3. 创建数据源和数据库连接池:通过@Configuration配置类,使用@Bean注解创建两个数据源和连接池的实例,分别代表主从数据库。例如: ``` @Configuration public class DataSourceConfig { // 主数据源 @Bean(name = "masterDataSource") @ConfigurationProperties(prefix = "spring.datasource") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } // 从数据源 @Bean(name = "slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } } ``` 4. 创建主从数据库的EntityManagerFactory:通过@Configuration配置类,使用@Primary和@Qualifier注解指定主从数据库分别对应的EntityManagerFactory。例如: ``` @Configuration @EnableJpaRepositories( basePackages = "com.example.repositories", entityManagerFactoryRef = "masterEntityManagerFactory", transactionManagerRef = "masterTransactionManager" ) public class MasterConfig { @Autowired @Qualifier("masterDataSource") private DataSource masterDataSource; @Primary @Bean(name = "masterEntityManagerFactory") public LocalContainerEntityManagerFactoryBean masterEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(masterDataSource) .packages("com.example.models") .build(); } @Primary @Bean(name = "masterTransactionManager") public PlatformTransactionManager masterTransactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(masterEntityManagerFactory(builder).getObject()); } } @Configuration @EnableJpaRepositories( basePackages = "com.example.repositories", entityManagerFactoryRef = "slaveEntityManagerFactory", transactionManagerRef = "slaveTransactionManager" ) public class SlaveConfig { @Autowired @Qualifier("slaveDataSource") private DataSource slaveDataSource; @Bean(name = "slaveEntityManagerFactory") public LocalContainerEntityManagerFactoryBean slaveEntityManagerFactory(EntityManagerFactoryBuilder builder) { return builder .dataSource(slaveDataSource) .packages("com.example.models") .build(); } @Bean(name = "slaveTransactionManager") public PlatformTransactionManager slaveTransactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(slaveEntityManagerFactory(builder).getObject()); } } ``` 5. 在需要取数据的地方进行选择:通过在JpaRepository接口上使用@Qualifier注解,指定使用主数据源还是从数据源。例如: ``` @Repository @Qualifier("masterEntityManagerFactory") public interface UserRepository extends JpaRepository<User, Long> { // ... } ``` 通过以上步骤,就可以在Spring Boot中实现MySQL的分离,从而实现数据库的负载均衡和高可用性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值