一.读写分离原理:


 上一篇文章我们已经讲解了mysql主从复制的原理并且实现了   只要往主服务器中插入数据  那么从数据库slaver将会跟着同步主服务器master的数据




 那么我们java 代码来实现的话  只要动态切换数据库 就达到了读写分离的目的。本文中是用spring + mybatis 来整合案例的  那么我们如果能够做到动态


 的切换spring的数据源 从而就可以达到切换数据库的目的


  demo下载地址




二.spring实现切换数据库原理:



     通过Spring的AOP思想来实现   根据你访问的service方法名来判断此方法执行查询还是执行更改操作


     一般查询切换到从数据源    更改插入删除操作在主数据库   因为主数据库会同步到从数据库




     此时我们spring-mybatis.xml 文件中至少应该有两个数据源 masterDataSource  和slaverDataSource  稍后贴出 




     masterDataSource数据源配置 和我们普通的配置没什么差别(数据库信息从配置文件读取)


    



<!-- 主库 用来写数据 --> 
 
<bean id="dataSourceWriter" class="org.apache.commons.dbcp.BasicDataSource" 
 
destroy-method="close"> 
 
<property name="driverClassName" value="${driver}" /> 
 
<property name="url" value="${url}" /> 
 
<property name="username" value="${username}" /> 
 
<property name="password" value="${password}" /> 
 
<!-- 初始化连接大小 --> 
 
<property name="initialSize" value="${initialSize}"></property> 
 
<!-- 连接池最大数量 --> 
 
<property name="maxActive" value="${maxActive}"></property> 
 
<!-- 连接池最大空闲 --> 
 
<property name="maxIdle" value="${maxIdle}"></property> 
 
<!-- 连接池最小空闲 --> 
 
<property name="minIdle" value="${minIdle}"></property> 
 
<!-- 获取连接最大等待时间 --> 
 
<property name="maxWait" value="${maxWait}"></property> 
 
</bean> 
 
 
 
 

       slaverDataSource  数据源  

 
 <!-- 从库用来读数据 --> 
 
<bean id="dataSourceReader" class="org.apache.commons.dbcp.BasicDataSource" 
 
destroy-method="close"> 
 
<property name="driverClassName" value="${slaver.driver}" /> 
 
<property name="url" value="${slaver.url}" /> 
 
<property name="username" value="${slaver.username}" /> 
 
<property name="password" value="${slaver.password}" /> 
 
<!-- 初始化连接大小 --> 
 
<property name="initialSize" value="${slaver.initialSize}"></property> 
 
<!-- 连接池最大数量 --> 
 
<property name="maxActive" value="${slaver.maxActive}"></property> 
 
<!-- 连接池最大空闲 --> 
 
<property name="maxIdle" value="${slaver.maxIdle}"></property> 
 
<!-- 连接池最小空闲 --> 
 
<property name="minIdle" value="${slaver.minIdle}"></property> 
 
<!-- 获取连接最大等待时间 --> 
 
<property name="maxWait" value="${slaver.maxWait}"></property> 
 
</bean>

     我们原来sqlSessionFactory的bean中的属性dataSource 应该指向我们包含主数据源和从数据源的bean


     


     所以我们要定义一个dynamicDataSource 来管理masterDatas 和slaverDataSource   代码如下


     


<!-- 动态数据源 -->   
 
    <bean id="dynamicDataSource" class="com.cn.wx.db.DynamicDataSource">   
 
        <!-- 通过key-value关联数据源 -->   
 
        <property name="targetDataSources">   
 
            <map>   
 
                <entry value-ref="dataSourceWriter" key="dataSourceWriter"></entry>   
 
                <entry value-ref="dataSourceReader" key="dataSourceReader"></entry>   
 
            </map>   
 
        </property> 
 
        <!-- 默认的DataSource配置--> 
 
        <property name="defaultTargetDataSource" ref="dataSourceWriter" />       
 
    </bean>



重写一下方法


    


@Override 
 
protected Object determineCurrentLookupKey() { 
 
// TODO Auto-generated method stub 
 
return DBContextHolder.getDbType(); 
 
}


DBContextHolder 类是我们自己定义的  用来返回与当前线程绑定的数据源的名称




核心代码如下:


/** 
* 线程threadlocal 
*/  
     private static ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
   
     private static String DEFAUL_DB_TYPE_WRITER = "dataSourceWriter";   
     
     /* 获取本线程的dbtype
     * @return
     */
    public static String getDbType() {  
        String db = contextHolder.get();  
        if (db == null) {  
            db = DEFAUL_DB_TYPE_WRITER;// 默认是读写库  
        }  
        return db;  
      
 }


3. spring AOP配置实现数据源切换:


 我们访问以 select*  get* find* query* 开头的Service方法都会切换到读的数据源    直接上代码


   


<!-- 数据源读写分离  aop --> 
 
     <bean id="dynamicDataSourceAOP" class="com.cn.wx.db.DynamicDataSourceAOP"> 
 
         <property name="methods">  
 
              <map>                   
 
                  <entry key="select*" value="dataSourceReader" /> 
 
                  <entry key="get*" value="dataSourceReader" /> 
 
                  <entry key="find*" value="dataSourceReader" /> 
 
                  <entry key="page*" value="dataSourceReader" />             
 
                  <entry key="query*" value="dataSourceReader" /> 
 
              </map> 
 
            </property> 
 
         <property name="defaultDataSource" value="dataSourceWriter"/> 
 
      </bean>


      


     DynamicDataSourceAOP 是我们自己来根据service方法不同来切换数据源的核心逻辑 如下:




      

spring cloud 链接 mysql 主备 主从 spring 主从数据库_数据源




spring-mybatis.xml



spring cloud 链接 mysql 主备 主从 spring 主从数据库_数据源_02




至此其实我们的配置已经完毕 接下来就是测试读写分离是否成功。




4.测试读写分离配置是否成功:


接下我们来测试是否成功的实现了读写分离



spring cloud 链接 mysql 主备 主从 spring 主从数据库_数据源_03




我们插入往主数据库中插入一条数据    这个时候去查看从数据库 是否也有这条记录  有的话说明成功!


         继续验证读  是否从从数据库中读取的 代码如下:

spring cloud 链接 mysql 主备 主从 spring 主从数据库_读写分离_04

,注意查询方法是AOP中拦截到的  数据源才会去切换到slaverDataSource  

此时主从数据库一样怎么验证查询的是从数据库呢   很简单 我们手动去修改从数据库的某一个字段值 除了按条件查询的值以外 都可以

此时我们主数据库数据如下:

spring cloud 链接 mysql 主备 主从 spring 主从数据库_读写分离_05

spring cloud 链接 mysql 主备 主从 spring 主从数据库_读写分离_05



从数据库数据如下:

spring cloud 链接 mysql 主备 主从 spring 主从数据库_数据源_07



修改从数据库的age 为28 此时主数据库还是原来的25 

如果查询的结果是28那么读写分离就已经实现了

现在执行查询方法


spring cloud 链接 mysql 主备 主从 spring 主从数据库_数据源_08


     sql语句打印的age是28  到此为止 就已经实现了读写分离

     demo下载地址