一.读写分离原理:
上一篇文章我们已经讲解了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-mybatis.xml
至此其实我们的配置已经完毕 接下来就是测试读写分离是否成功。
4.测试读写分离配置是否成功:
接下我们来测试是否成功的实现了读写分离
我们插入往主数据库中插入一条数据 这个时候去查看从数据库 是否也有这条记录 有的话说明成功!
继续验证读 是否从从数据库中读取的 代码如下:
,注意查询方法是AOP中拦截到的 数据源才会去切换到slaverDataSource
此时主从数据库一样怎么验证查询的是从数据库呢 很简单 我们手动去修改从数据库的某一个字段值 除了按条件查询的值以外 都可以
此时我们主数据库数据如下:
从数据库数据如下:
修改从数据库的age 为28 此时主数据库还是原来的25
如果查询的结果是28那么读写分离就已经实现了
现在执行查询方法
sql语句打印的age是28 到此为止 就已经实现了读写分离
demo下载地址