两种方案
方案1:当只有读 *** 作的时候,直接 *** 作读库(从库);
当在写事务(即写主库)中读时,也是读主库(即参与到主库 *** 作),这样的优势是可以防止写完后可能读不到刚才写的数据;
此方案其实是使用事务传播行为为:SUPPORTS解决的。
方案2:当只有读 *** 作的时候,直接 *** 作读库(从库);
当在写事务(即写主库)中读时,强制走从库,即先暂停写事务,开启读(读从库),然后恢复写事务。
此方案其实是使用事务传播行为为:NOT_SUPPORTS解决的。
核心组件
cnjavasscommondatasourceReadWriteDataSource:读写分离的动态数据源,类似于AbstractRoutingDataSource,具体参考javadoc;
cnjavasscommondatasourceReadWriteDataSourceDecision:读写库选择的决策者,具体参考javadoc;
cnjavasscommondatasourceReadWriteDataSourceProcessor:此类实现了两个职责(为了减少类的数量将两个功能合并到一起了):读/写动态数据库选择处理器、通过AOP切面实现读/写选择,具体参考javadoc。
具体配置
1、数据源配置
11、写库配置
Java代码
<bean id="writeDataSource" class="orglogicalcobwebsproxoolProxoolDataSource">
<property name="alias" value="writeDataSource"/>
<property name="driver" value="${writeconnectiondriver_class}" />
<property name="driverUrl" value="${writeconnectionurl}" />
<property name="user" value="${writeconnectionusername}" />
<property name="password" value="${writeconnectionpassword}" />
<property name="maximumConnectionCount" value="${writeproxoolmaximumconnectioncount}"/>
<property name="minimumConnectionCount" value="${writeproxoolminimumconnectioncount}" />
<property name="statistics" value="${writeproxoolstatistics}" />
<property name="simultaneousBuildThrottle" value="${writeproxoolsimultaneousbuildthrottle}"/>
</bean>
12、读库配置
Java代码
<bean id="readDataSource1" class="orglogicalcobwebsproxoolProxoolDataSource">
<property name="alias" value="readDataSource"/>
<property name="driver" value="${readconnectiondriver_class}" />
<property name="driverUrl" value="${readconnectionurl}" />
<property name="user" value="${readconnectionusername}" />
<property name="password" value="${readconnectionpassword}" />
<property name="maximumConnectionCount" value="${readproxoolmaximumconnectioncount}"/>
<property name="minimumConnectionCount" value="${readproxoolminimumconnectioncount}" />
<property name="statistics" value="${readproxoolstatistics}" />
<property name="simultaneousBuildThrottle" value="${readproxoolsimultaneousbuildthrottle}"/>
</bean>
13、读写动态库配置
通过writeDataSource指定写库,通过readDataSourceMap指定从库列表,从库列表默认通过顺序轮询来使用读库,具体参考javadoc;
Java代码
<bean id="readWriteDataSource" class="cnjavasscommondatasourceReadWriteDataSource">
<property name="writeDataSource" ref="writeDataSource"/>
<property name="readDataSourceMap">
<map>
<entry key="readDataSource1" value-ref="readDataSource1"/>
<entry key="readDataSource2" value-ref="readDataSource1"/>
<entry key="readDataSource3" value-ref="readDataSource1"/>
<entry key="readDataSource4" value-ref="readDataSource1"/>
</map>
</property>
</bean>
2、XML事务属性配置
所以读方法必须是read-only(必须,以此来判断是否是读方法)。
Java代码
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save" propagation="REQUIRED" />
<tx:method name="add" propagation="REQUIRED" />
<tx:method name="create" propagation="REQUIRED" />
<tx:method name="insert" propagation="REQUIRED" />
<tx:method name="update" propagation="REQUIRED" />
<tx:method name="merge" propagation="REQUIRED" />
<tx:method name="del" propagation="REQUIRED" />
<tx:method name="remove" propagation="REQUIRED" />
<tx:method name="put" read-only="true"/>
<tx:method name="query" read-only="true"/>
<tx:method name="use" read-only="true"/>
<tx:method name="get" read-only="true" />
<tx:method name="count" read-only="true" />
<tx:method name="find" read-only="true" />
<tx:method name="list" read-only="true" />
<tx:method name="" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3、事务管理器
事务管理器管理的是readWriteDataSource
Java代码
<bean id="txManager" class="orgspringframeworkjdbcdatasourceDataSourceTransactionManager">
<property name="dataSource" ref="readWriteDataSource"/>
</bean>
4、读/写动态数据库选择处理器
根据之前的txAdvice配置的事务属性决定是读/写,具体参考javadoc;
forceChoiceReadWhenWrite:用于确定在如果目前是写(即开启了事务),下一步如果是读,是直接参与到写库进行读,还是强制从读库读,具体参考javadoc;
Java代码
<bean id="readWriteDataSourceTransactionProcessor" class="cnjavasscommondatasourceReadWriteDataSourceProcessor">
<property name="forceChoiceReadWhenWrite" value="false"/>
</bean>
5、事务切面和读/写库选择切面
Java代码
<aop:config expose-proxy="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution( cnjavassservice())" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
<!-- 通过AOP切面实现读/写库选择 -->
<aop:aspect order="-2147483648" ref="readWriteDataSourceTransactionProcessor">
<aop:around pointcut-ref="txPointcut" method="determineReadOrWriteDB"/>
</aop:aspect>
</aop:config>
1、事务切面一般横切业务逻辑层;
2、此处我们使用readWriteDataSourceTransactionProcessor的通过AOP切面实现读/写库选择功能,order=IntegerMIN_VALUE(即最高的优先级),从而保证在 *** 作事务之前已经决定了使用读/写库。
6、测试用例
只要配置好事务属性(通过read-only=true指定读方法)即可,其他选择读/写库的 *** 作都交给readWriteDataSourceTransactionProcessor完成。
可以参考附件的:
cnjavassreadwriteReadWriteDBTestWithForceChoiceReadOnWriteFalse
cnjavassreadwriteReadWriteDBTestWithNoForceChoiceReadOnWriteTrue
可以下载附件的代码进行测试,具体选择主/从可以参考日志输出。
通常读 *** 作要比写 *** 作的数量多得多,而且数据库在数据写入时是会锁表的,这个过程不能读取数据,必须等待写入完成,所以一旦请求量大那么执行效率就降低了。读写分离就是一个库只负责读取不做写入,这样就提高了读的效率。
一般都会以oracle DG的方式实现,主库主要就是写入数据,然后通过日志抄送的方式到备库,然后备库以只读模式打开。不过备用库以只读模式打开对数据库的版本有要求,好像低版本的数据库没有这个功能。
以上就是关于如何在应用层通过spring特性解决数据库读写分离全部的内容,包括:如何在应用层通过spring特性解决数据库读写分离、为什么数据库读写分离可以提高性能、如何实现Oracle数据库的读写分离等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)