基于spring的数据库读写分离

背景:
  Spring读写分离是大家都比较常见并一直在使用的技术。
  本博文再次对其进行阐述,一方面是为了更好的分享给大伙,一方面也是对最近做"XXX系统"遇到的问题做一次整理。方便大家以后遇到类似问题可以很快解决。
技术实现:
  1、多数据源配置。配置包括一个主库master_dataSource,一个个从库slave_dataSource。
  数据源托管给tomcat控制,系统通过jndi方式寻找。配置内容如下:

<beans profile="production">
<jee:jndi-lookup id="master_dataSource" jndi-name="java:comp/env/jdbc/master"/>
<jee:jndi-lookup id="slave_dataSource" jndi-name="java:comp/env/jdbc/slave"/>
<bean id="dataSource" class="com.mmb.framework.support.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="master_dataSource"></entry>
<entry key="slave" value-ref="slave_dataSource"></entry>
<entry key="slave2" value-ref="slave2_dataSource"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="master_dataSource"></property>
</bean>
</beans>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />

  2、实现类
  从配置文件发现dataSource的实现类为:com.mmb.framework.support.DynamicDataSource,该类继承了spring中类:AbstractRoutingDataSource。并重写了方法:determineCurrentLookupKey。该方法是主从数据源切换的关键,通过该方法判定当前上下文的数据源key,进而跟进key通过Jndi找到对应的数据源。该类具体内容如下:

  

public class DynamicDataSource extends AbstractRoutingDataSource {
public static String MASTER = "master";
public static String SLAVE = "slave";
private static ThreadLocal<String> local = new ThreadLocal<String>();
@Override
protected Object determineCurrentLookupKey() {
String dString = local.get() == null ? MASTER : local.get();
setRoute(DynamicDataSource.MASTER);
return dString;
}
public static void setRoute(String route) {
if (route == null || route.equals("")) {
route = MASTER;
}
local.set(route);
}
public static void removeRoute() {
local.remove();
}
public static Object getRoute() {
return local.get();
}
}

  通过determineCurrentLookupKey方法可以看见,程序将当前线程key放入到ThreadLocal变量local中。这样就可以很好的解决多线程问题,避免争抢
数据库连接导致数据错误。那么setRoute方法在何时被调用,请读者继续向下看。

  3、使用方法
  系统采用ibatis作为持久层,并使用AbstractDaoSupport辅助获取数据库连接。AbstractDaoSupport属于org.apache.ibatis.spring.support包,我们在其中getSession和getJdbcTemplate。如下:

  

public Session getSession(String sessionFactorySign)
{
dynamicSession(sessionFactorySign);
return session;
}

public JdbcTemplate getJdbcTemplate(String sessionFactorySign)
{
dynamicSession(sessionFactorySign);
return JdbcTemplate;
}
private void dynamicSession(String sessionFactorySign)
{
if(null == sessionFactorySign || "".equals(sessionFactorySign))
DynamicDataSource.setRoute(DynamicDataSource.MASTER);
else
DynamicDataSource.setRoute(sessionFactorySign);
}

  通过代码可以发现,每次获取JdbcTemplate模板类或者获取session时,都会调用DynamicDataSource.setRoute进行数据源路由的设定。这里也回答了上面提到的问题。
具体使用:this.getJdbcTemplate(DynamicDataSource.MASTER).update();

  4、类图和序列图
  到此可能大家依然不是太清晰,到底如何做到了读写分离。下面我给出了上面涉及到的类关系和序列图。方便大家更好的理解。图形工具为:Enterprise Architect 11。

  类图:

  

  序列图:

  
1、TestMapper调用AbstractDaoSupport.getJdbcTemplate方法获取JdbcTemplate
2、AbstractDaoSupport调用自身dynamicSession方法,该方法中调用DynamicDataSource.setRoute方法设置数据源路由key。
3、TestMapper获取到jdbcTemplate后,调用JdbcTemplate.update方法
4、JdbcTemplate调用DataSourceUtil获取数据连接
5、DataSourceUtil调用自身doGetConnection,该方法中调用TransactionSynchronizationManager.getResource方法找到数据源
6、如果当前TransactionSynchronizationManager中包含ConnectionHolder并且开启了事务同步,并且ConnectionHolder中有连接,那么转到7.否则转到8.
7、调用ConnectionHolder.getConnection返回连接,转到9。
8、调用AbstractRoutingDataSource.getConnection方法,该方法调用DynamicDataSource.determineCurrentLookupKey()从ThreadLodal变量DynamicDataSource.local中获取
当前数据源路由key,根据key获取当前的数据源,调用具体数据源的getConnection方法,返回当前数据库连接。转到9。
9、JdbcTemplate接受到数据库连接后执行execute方法完成更新操作,结束。

在实际使用用还遇到如下两个问题,请参看博文:http://www.cnblogs.com/belen/p/4926206.html

时间: 2024-08-04 23:12:34

基于spring的数据库读写分离的相关文章

Spring 实现数据库读写分离

现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力. 采用读写分离技术的目标:有效减轻Master库的压力,又可以

Spring 实现数据库读写分离(转)

现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验.我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从库的查询压力. 采用读写分离技术的目标:有效减轻Master库的压力,又可以

Spring实现数据库读写分离

http://neoremind.net/2011/06/spring%E5%AE%9E%E7%8E%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB/ 借助于Spring框架在2.0.1之后提供的AbstractRoutingDataSource可以实现动态的选择数据源datasource,下面先举一个最简单的例子(可以参考这个链接): 1. 首先新建 一个Catalog VO对象的DAO(见代码1),它继承了S

spring+mybatis利用interceptor(plugin)兑现数据库读写分离

使用spring的动态路由实现数据库负载均衡 系统中存在的多台服务器是“地位相当”的,不过,同一时间他们都处于活动(Active)状态,处于负载均衡等因素考虑,数据访问请求需要在这几台数据库服务器之间进行合理分配, 这个时候,通过统一的一个DataSource来屏蔽这种请求分配的需求,从而屏蔽数据访问类与具体DataSource的耦合: 系统中存在的多台数据库服务器现在地位可能相当也可能不相当,但数据访问类在系统启动时间无法明确到底应该使用哪一个数据源进行数据访问,而必须在系统运行期间通过某种条

161920、使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

使用Spring AOP实现MySQL数据库读写分离案例分析

一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库的主从配置,最简单的是一台Master和一台Slave(大型网站系统的话,当然会很复杂,这里只是分析了最简单的情况).通过主从配置主从数据库保持了相同的数据,我们在进行读操作的时候访问从数据库Slave,在进行写操作的时候访问主数据库Master.这样的话就减轻了一台服务器的压力. 在进行读写分离案

Spring + Mybatis项目实现数据库读写分离

主要思路:通过实现AbstractRoutingDataSource类来动态管理数据源,利用面向切面思维,每一次进入service方法前,选择数据源. 1.首先pom.xml中添加aspect依赖 <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </depen

Spring+MyBatis实现数据库读写分离方案

方案1通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactoryBean对象的mapperLocations属性制定两个读写数据源的配置文件.将所有读的操作配置在读文件中,所有写的操作配置在写文件中. 优点:实现简单缺点:维护麻烦,需要对原有的xml文件进行重新修改,不支持多读,不易扩展实现方式 <bean id="abstractDataSource" abstract="true" class="com

Spring+Mybatis实现主从数据库读写分离

Spring+Mybatis实现主从数据库读写分离 采用配置+注解的方式. 自定义@DataSource注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME