在应用层通过spring特性解决数据库读写分离

论坛 期权论坛 脚本     
匿名网站用户   2020-12-21 09:34   83   0

原文地址:http://jinnianshilongnian.iteye.com/blog/1720618

如何配置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 forMySQL: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. <beanid="writeDataSource"class="org.logicalcobwebs.proxool.ProxoolDataSource">
  2. <propertyname="alias"value="writeDataSource"/>
  3. <propertyname="driver"value="${write.connection.driver_class}"/>
  4. <propertyname="driverUrl"value="${write.connection.url}"/>
  5. <propertyname="user"value="${write.connection.username}"/>
  6. <propertyname="password"value="${write.connection.password}"/>
  7. <propertyname="maximumConnectionCount"value="${write.proxool.maximum.connection.count}"/>
  8. <propertyname="minimumConnectionCount"value="${write.proxool.minimum.connection.count}"/>
  9. <propertyname="statistics"value="${write.proxool.statistics}"/>
  10. <propertyname="simultaneousBuildThrottle"value="${write.proxool.simultaneous.build.throttle}"/>
  11. </bean>

1.2、读库配置

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

1.3、读写动态库配置

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

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

2、XML事务属性配置

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

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

3、事务管理器

事务管理器管理的是readWriteDataSource

Java代码
  1. <beanid="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <propertyname="dataSource"ref="readWriteDataSource"/>
  3. </bean>

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

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

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

Java代码
  1. <beanid="readWriteDataSourceTransactionProcessor"class="cn.javass.common.datasource.ReadWriteDataSourceProcessor">
  2. <propertyname="forceChoiceReadWhenWrite"value="false"/>
  3. </bean>
   

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

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

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

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

6、测试用例

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

可以参考附件的:

cn.javass.readwrite.ReadWriteDBTestWithForceChoiceReadOnWriteFalse

cn.javass.readwrite.ReadWriteDBTestWithNoForceChoiceReadOnWriteTrue

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

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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP