天天看点

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

  采用读写分离技术的目标:有效减轻master库的压力,又可以把用户查询数据的请求分发到不同的slave库,从而保证系统的健壮性。我们看下采用读写分离的背景。

  随着网站的业务不断扩展,数据不断增加,用户越来越多,数据库的压力也就越来越大,采用传统的方式,比如:数据库或者sql的优化基本已达不到要求,这个时候可以采用读写分离的策 略来改变现状。

  具体到开发中,如何方便的实现读写分离呢?目前常用的有两种方式:

  1 第一种方式是我们最常用的方式,就是定义2个数据库连接,一个是masterdatasource,另一个是slavedatasource。更新数据时我们读取masterdatasource,查询数据时我们读取slavedatasource。这种方式很简单,我就不赘述了。

,反射。下面会详细的介绍实现方式。

   在介绍实现方式之前,我们先准备一些必要的知识,spring 的abstractroutingdatasource 类

     abstractroutingdatasource这个类 是spring2.0以后增加的,我们先来看下abstractroutingdatasource的定义:

    public abstract class abstractroutingdatasource extends abstractdatasource implements initializingbean  {}

    abstractroutingdatasource继承了abstractdatasource ,而abstractdatasource 又是datasource

的子类。datasource   是javax.sql 的数据源接口,定义如下:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

  datasource 接口定义了2个方法,都是获取数据库连接。我们在看下abstractroutingdatasource 如何实现了datasource接口:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

  很显然就是调用自己的determinetargetdatasource()  方法获取到connection。determinetargetdatasource方法定义如下:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

   我们最关心的还是下面2句话:

    object lookupkey = determinecurrentlookupkey();

        datasource datasource = this.resolveddatasources.get(lookupkey);

    determinecurrentlookupkey方法返回lookupkey,resolveddatasources方法就是根据lookupkey从map中获得数据源。resolveddatasources 和determinecurrentlookupkey定义如下:

  private map<object, datasource> resolveddatasources;

  protected abstract object determinecurrentlookupkey()

  看到以上定义,我们是不是有点思路了,resolveddatasources是map类型,我们可以把masterdatasource和slavedatasource存到map中,如下:

    key        value

    master             masterdatasource

    slave                  slavedatasource

  我们在写一个类dynamicdatasource  继承abstractroutingdatasource,实现其determinecurrentlookupkey() 方法,该方法返回map的key,master或slave。

  好了,说了这么多,有点烦了,下面我们看下怎么实现。

   上面已经提到了我们要使用的技术,我们先看下annotation的定义:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

    我们还需要实现spring的抽象类abstractroutingdatasource,就是实现determinecurrentlookupkey方法:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

    从dynamicdatasource 的定义看出,他返回的是dynamicdatasourceholder.getdatasouce()值,我们需要在程序运行时调用dynamicdatasourceholder.putdatasource()方法,对其赋值。下面是我们实现的核心部分,也就是aop部分,datasourceaspect定义如下:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

    为了方便测试,我定义了2个数据库,shop模拟master库,test模拟slave库,shop和test的表结构一致,但数据不同,数据库配置如下:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

  在spring的配置中增加aop配置

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

   下面是mybatis的usermapper的定义,为了方便测试,登录读取的是master库,用户列表读取slave库:

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

   好了,运行我们的eclipse看看效果,输入用户名admin 登录看看效果

  

spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离
spring 实现数据库读写分离 1.Spring实现数据库的读写分离 2.MyBatis多数据源配置 实现读写分离

  从图中可以看出,登录的用户和用户列表的数据是不同的,也验证了我们的实现,登录读取master库,用户列表读取slave库。

首先说明,本文的配置使用的最直接的方式,实际用起来可能会很麻烦。

实际应用中可能存在多种结合的情况,你可以理解本文的含义,不要死板的使用。

针对这种情况,一个更好的解决方法可以参考(本人没有实际尝试过):

<a target="_blank" href="http://blog.csdn.net/lixiucheng005/article/details/17391857">http://blog.csdn.net/lixiucheng005/article/details/17391857</a>

还有一个通过spring<code>abstractroutingdatasource</code>路由接口的方式:

<a target="_blank" href="http://blog.csdn.net/xtj332/article/details/43953699">http://blog.csdn.net/xtj332/article/details/43953699</a>

这种情况可以参考本文,但是需要注意每一个数据库对应的mapper要在不同的包下方便区分和配置。

另外分库的情况下也会存在主从的情况,如果你的数据库从库过多,就参考上面提供的方法,或者寻找其他方式解决。

分库的情况下,不同的数据库的mapper一定放在不同的包下。

主从的情况下,同一个mapper会同时存在读写的情况,创建两个并不合适,使用同一个即可。但是这种情况下需要注意,spring对mapper自动生成的名字是相同的,而且类型也相同,这是就不能直接注入<code>mapper</code>接口。需要通过<code>sqlsession</code>来解决。

这个文件,主要是引入了<code>spring-datasource-master.xml</code>和<code>spring-datasource-slave.xml</code>。

和<code>master</code>区别不大,主要是<code>id</code>名字和数据源配置有区别。

这里需要注意<code>&lt;tx:method name="*" read-only="true"/&gt;</code>是只读的。如果不是从库,可以按主库进行配置。

在下面代码中:

必须通过<code>sqlsessionfactorybeanname</code>来指定不同的<code>sqlsessionfactory</code>。

这里是针对主从的情况进行设置的,两个配置扫描的<code>mapper</code>是一样的,所以没法直接注入,需要通过下面的麻烦方式注入。

因为<code>sqlsession</code>能通过<code>name</code>区分开,所以这里从<code>sqlsession</code>获取<code>mapper</code>。

另外如果需要考虑在同一个事务中写读的时候,需要使用相同的<code>writemapper</code>,这样在读的时候,才能获取事务中的最新数据。

以上是主从的情况。

在分库的情况时,由于不同mapper在不同的包下,所以可以直接使用<code>@resource</code>或者<code>@autowired</code>注入<code>mapper</code>,不需要通过<code>sqlsession</code>获取。