`
maakey
  • 浏览: 16420 次
文章分类
社区版块
存档分类
最新评论

在应用层通过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 forMySQL:http://www.iteye.com/topic/188598http://www.iteye.com/topic/1113437

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

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

该方案目前支持

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


考虑未来支持

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

使用场景

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

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

优势

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

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

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

两种方案



方案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、写库配置

  	<bean id="writeDataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">		<property name="alias" value="writeDataSource"/>		<property name="driver" value="${write.connection.driver_class}" />		<property name="driverUrl" value="${write.connection.url}" />		<property name="user" value="${write.connection.username}" />		<property name="password" value="${write.connection.password}" />		<property name="maximumConnectionCount" value="${write.proxool.maximum.connection.count}"/>		<property name="minimumConnectionCount" value="${write.proxool.minimum.connection.count}" />		<property name="statistics" value="${write.proxool.statistics}" />		<property name="simultaneousBuildThrottle" value="${write.proxool.simultaneous.build.throttle}"/>	</bean>


1.2、读库配置

    <bean id="readDataSource1" class="org.logicalcobwebs.proxool.ProxoolDataSource">        <property name="alias" value="readDataSource"/>        <property name="driver" value="${read.connection.driver_class}" />        <property name="driverUrl" value="${read.connection.url}" />        <property name="user" value="${read.connection.username}" />        <property name="password" value="${read.connection.password}" />        <property name="maximumConnectionCount" value="${read.proxool.maximum.connection.count}"/>        <property name="minimumConnectionCount" value="${read.proxool.minimum.connection.count}" />        <property name="statistics" value="${read.proxool.statistics}" />        <property name="simultaneousBuildThrottle" value="${read.proxool.simultaneous.build.throttle}"/>    </bean>

1.3、读写动态库配置

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

    <bean id="readWriteDataSource" class="cn.javass.common.datasource.ReadWriteDataSource">        <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(必须,以此来判断是否是读方法)。

    <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

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="readWriteDataSource"/>    </bean>


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

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

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

    <bean id="readWriteDataSourceTransactionProcessor" class="cn.javass.common.datasource.ReadWriteDataSourceProcessor">       <property name="forceChoiceReadWhenWrite" value="false"/>    </bean>


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

    <aop:config expose-proxy="true">        <!-- 只对业务逻辑层实施事务 -->        <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />        <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=Integer.MIN_VALUE(即最高的优先级),从而保证在操作事务之前已经决定了使用读/写库。

6、测试用例

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


可以参考附件的:

cn.javass.readwrite.ReadWriteDBTestWithForceChoiceReadOnWriteFalse

cn.javass.readwrite.ReadWriteDBTestWithNoForceChoiceReadOnWriteTrue



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

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

PS:欢迎拍砖指正。





支持一下博主:------------------------------------------

http://blog.51cto.com/contest2012/2058573

--------------------------------------------------------------



如何配置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:当只有读操作的时候,直接操作读库(从库);

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

此方案其实是使用事务传播行为为: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、写库配置

  	<bean id="writeDataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource">		<property name="alias" value="writeDataSource"/>		<property name="driver" value="${write.connection.driver_class}" />		<property name="driverUrl" value="${write.connection.url}" />		<property name="user" value="${write.connection.username}" />		<property name="password" value="${write.connection.password}" />		<property name="maximumConnectionCount" value="${write.proxool.maximum.connection.count}"/>		<property name="minimumConnectionCount" value="${write.proxool.minimum.connection.count}" />		<property name="statistics" value="${write.proxool.statistics}" />		<property name="simultaneousBuildThrottle" value="${write.proxool.simultaneous.build.throttle}"/>	</bean>


1.2、读库配置

    <bean id="readDataSource1" class="org.logicalcobwebs.proxool.ProxoolDataSource">        <property name="alias" value="readDataSource"/>        <property name="driver" value="${read.connection.driver_class}" />        <property name="driverUrl" value="${read.connection.url}" />        <property name="user" value="${read.connection.username}" />        <property name="password" value="${read.connection.password}" />        <property name="maximumConnectionCount" value="${read.proxool.maximum.connection.count}"/>        <property name="minimumConnectionCount" value="${read.proxool.minimum.connection.count}" />        <property name="statistics" value="${read.proxool.statistics}" />        <property name="simultaneousBuildThrottle" value="${read.proxool.simultaneous.build.throttle}"/>    </bean>

1.3、读写动态库配置

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

    <bean id="readWriteDataSource" class="cn.javass.common.datasource.ReadWriteDataSource">        <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(必须,以此来判断是否是读方法)。

    <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

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">        <property name="dataSource" ref="readWriteDataSource"/>    </bean>


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

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

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

    <bean id="readWriteDataSourceTransactionProcessor" class="cn.javass.common.datasource.ReadWriteDataSourceProcessor">       <property name="forceChoiceReadWhenWrite" value="false"/>    </bean>


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

    <aop:config expose-proxy="true">        <!-- 只对业务逻辑层实施事务 -->        <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service..*.*(..))" />        <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=Integer.MIN_VALUE(即最高的优先级),从而保证在操作事务之前已经决定了使用读/写库。

6、测试用例

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


可以参考附件的:

cn.javass.readwrite.ReadWriteDBTestWithForceChoiceReadOnWriteFalse

cn.javass.readwrite.ReadWriteDBTestWithNoForceChoiceReadOnWriteTrue



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

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

PS:欢迎拍砖指正。





支持一下博主:------------------------------------------

http://blog.51cto.com/contest2012/2058573

--------------------------------------------------------------


分享到:
评论

相关推荐

    在应用层透过spring解决数据库读写分离

    在应用层解决数据库读写分离可以通过Spring框架来实现,本文将介绍如何使用Spring解决数据库读写分离。 概念 * 读写分离:将数据库的读取和写入操作分开处理,以提高数据库的性能和可用性。 * 主从复制:将主...

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

    通过以上步骤,你就可以在应用层利用Spring的特性实现数据库读写分离了。在实际项目中,还需要考虑其他的因素,如数据同步(如MySQL的binlog同步)、故障转移、负载均衡等。同时,合理地设计读写策略,避免在高并发...

    Spring实现数据库读写分离代码

    通过以上分析,我们可以看出,Spring、Spring MVC、MyBatis和MySQL的组合,为实现数据库读写分离提供了一种高效且灵活的解决方案。理解并掌握这些知识点,对于提升系统的稳定性和性能具有重要的意义。

    Spring实现数据库读写分离

    在数据库读写分离的场景下,Spring可以通过配置不同的数据源,实现对主从库的管理和切换。例如,通过`@Qualifier`注解来指定使用哪个数据源,或者利用AOP在特定方法上动态切换。 3. **MyBatis**:MyBatis是一个优秀...

    spring+springmvc+mybatis的整合以及数据库读写分离的测试

    在IT行业中,Spring、SpringMVC和MyBatis是三个非常重要的...同时,通过数据库读写分离,提升了系统的可扩展性和响应速度。对于开发者而言,理解和掌握这些技术能够提升开发效率,构建出高效、稳定的大型分布式应用。

    数据库读写分离demo

    总之,这个"数据库读写分离demo"展示了如何在Java应用中,利用MyBatis和AOP技术来实现数据库读写分离,提高系统的读写性能和稳定性。通过这种方式,我们可以在不影响原有代码结构的前提下,优雅地解决高并发场景下的...

    springboot+mybatis+druid+redis实现数据库读写分离和缓存

    在现代Web应用开发中,数据库读写分离和缓存技术是提高系统性能和可扩展性的关键策略。本项目采用SpringBoot、MyBatis、Druid和Redis这四个核心技术来实现这一目标。以下将详细讲解这些组件如何协同工作,以及它们...

    Spring+mysql读写分离

    本示例"Spring+MySQL+MyBatis+SpringMVC 读写分离"是针对这种策略的一个实现,旨在帮助开发者理解如何在实际项目中应用这一技术。下面将详细介绍这个Demo中的关键知识点。 首先,我们要理解什么是读写分离。读写...

    使用Spring实现读写分离(MySQL实现主从复制)

    在Spring框架下实现读写分离的核心在于通过AOP(面向切面编程)技术,在调用业务逻辑方法前根据一定的规则(如方法名前缀)来决定使用哪个数据源。具体实现包括以下几个步骤: 1. **定义动态数据源**:继承`...

    spring+springmvc+mybatis+maven+mysql数据库读写分离

    开发者可以通过学习这个项目,理解如何在实际项目中结合这些技术实现数据库读写分离,提升系统的并发处理能力和稳定性。 总结来说,Spring、SpringMVC、MyBatis和Maven是Java开发中的常见工具,它们共同构建了一个...

    spring之mysql读写分离

    本文将深入探讨如何利用Spring AOP(面向切面编程)实现应用层的MySQL读写分离,并结合一主多从的配置来进行详细讲解。 首先,我们需要理解MySQL的主从复制机制。在主从复制中,数据的写操作(INSERT、UPDATE、...

    AOP动态实现MYSQL数据库读写分离

    本示例通过Java的面向切面编程(AOP)技术来实现实现MySQL数据库的读写分离,旨在帮助开发者理解如何在实际项目中应用这一技术。 首先,我们要理解什么是AOP(Aspect-Oriented Programming)。AOP是面向切面编程的...

    JAVA数据库读写分离项目源码(MYSQL)

    在Java开发领域,数据库读写分离是一种常见的优化策略,它能显著提高系统性能,尤其是在高并发环境下。本项目源码是基于MySQL数据库实现的,利用SSM(Spring、SpringMVC、MyBatis)框架进行开发,旨在帮助开发者快速...

    spring解决读写分离的方案有两种

    ### Spring 解决读写分离方案详解 #### 一、背景介绍 在现代应用程序开发中,...Spring 的 AOP 特性为我们提供了一个优雅的方式来实现读写分离,尤其适合那些希望在不引入额外中间件的情况下优化数据库性能的项目。

    SSM项目整合,spring+springmvc+mybatis,实现mysql数据库读写分离

    本项目着重于实现数据库的读写分离,以提高系统的性能和可用性。下面我们将详细探讨这个项目中的关键知识点。 1. **Spring框架**:Spring作为核心的依赖注入(DI)和面向切面编程(AOP)框架,提供了一种管理对象...

    springBoot+mybatis读写分离(AOP)

    在IT行业中,数据库读写分离是一种常见的优化策略,特别是在高并发场景下,它能有效提升系统的性能和稳定性。本文将详细讲解如何利用Spring Boot、MyBatis和AOP(面向切面编程)来实现这一技术。 首先,让我们理解...

    springmvc mybatis 读写分离(一写多读)

    在现代的高并发Web应用中,数据库的读写分离是一种常见的优化策略,它可以提高系统的并发处理能力和稳定性。本文将深入探讨如何使用SpringMVC和MyBatis框架来实现读写分离,尤其是“一写多读”的模式。我们将讨论...

    50-Spring Boot读写分离1

    在本篇文章中,我们将探讨如何在Spring Boot应用中配置读写分离,以及使用MySQL Router作为中间件。 首先,了解MySQL的主从复制是非常必要的。在一个典型的主从架构中,有一个主库负责写操作,多个从库负责读操作。...

    springboot 实现mysql读写分离

    在IT行业中,数据库读写分离是一种常见的优化策略,特别是在高并发和大数据量的场景下,以提高系统的性能和可用性。本教程将详细介绍如何利用SpringBoot框架实现MySQL的读写分离,以及涉及到的相关技术。 首先,...

Global site tag (gtag.js) - Google Analytics