背景

我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,

其中一个是主库,负责写入数据,我们称之为:写库;

其它都是从库,负责读取数据,我们称之为:读库;

那么,对我们的要求是:

  • 读库和写库的数据一致;
  • 写数据必须写到写库;
  • 读数据必须到读库;

方案

目前公司已经对数据库进行了双机热备,保证了读库和写库的数据一致性,

所以目前要做的就是解决读写分离

解决读写分离的方案有两种:应用层解决和中间件解决。

应用层解决

优点:

1.多数据源切换方便,由程序自动完成;

2.不需要引入中间件;

3.理论上支持任何数据库;

缺点:

1.由程序员完成,运维参与不到;

2.不能做到动态增加数据源;

中间件解决

优点:

1.源程序不需要做任何改动就可以实现读写分离;

2.动态添加数据源不需要重启程序;

缺点:

1.程序依赖于中间件,会导致切换数据库变得困难;

2.由中间件做了中转代理,性能有所下降;

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

使用Spring基于应用层实现

数据源配置

1.读(jdbc.properties)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.8.19:3306/newagency?useUnicode=true&characterEncoding=UTF-8
username=***
password=***
#password=123
#定义初始连接数
initialSize=0
#定义最大连接数
maxActive=20
#定义最大空闲
maxIdle=20
#定义最小空闲
minIdle=1
#定义最长等待时间
maxWait=60000

2.写(jdbc.properties)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.8.20:3306/newagency?useUnicode=true&characterEncoding=UTF-8
username=***
password=***
#password=123
#定义初始连接数
initialSize=0
#定义最大连接数
maxActive=20
#定义最大空闲
maxIdle=20
#定义最小空闲
minIdle=1
#定义最长等待时间
maxWait=60000

xml文件需添加的相关配置

1.spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd ">
<bean id="masterdataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${masterdataSourceurl}" />
<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> <!-- removeAbandoned: 是否自动回收超时连接 -->
<property name="removeAbandoned" value="${removeAbandoned}"></property>
<!-- removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"></property>
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位 -->
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"></property>
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
<property name="numTestsPerEvictionRun" value="${numTestsPerEvictionRun}"></property>
<!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"></property>
<property name="validationQuery" value="${validationQuery}"></property>
<!-- 定时对线程池中的链接进行validateObject校验,对无效的链接进行关闭 -->
<!-- <property name="testWhileIdle" value="${testWhileIdle}"></property> -->
<!-- 指定在从连接池中拿连接时,要检查连接是否有效,若无效会将连接从连接池中移除掉 -->
<property name="testOnBorrow" value="${testOnBorrow}"></property>
</bean>
<bean id="slavedataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${slavedataSourceurl}" />
<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> <!-- removeAbandoned: 是否自动回收超时连接 -->
<property name="removeAbandoned" value="${removeAbandoned}"></property>
<!-- removeAbandonedTimeout: 超时时间(以秒数为单位) -->
<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}"></property>
<!-- 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位 -->
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"></property>
<!-- 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 -->
<property name="numTestsPerEvictionRun" value="${numTestsPerEvictionRun}"></property>
<!-- 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程 -->
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"></property>
<property name="validationQuery" value="${validationQuery}"></property>
<!-- 定时对线程池中的链接进行validateObject校验,对无效的链接进行关闭 -->
<!-- <property name="testWhileIdle" value="${testWhileIdle}"></property> -->
<!-- 指定在从连接池中拿连接时,要检查连接是否有效,若无效会将连接从连接池中移除掉 -->
<property name="testOnBorrow" value="${testOnBorrow}"></property>
</bean> <bean id="dataSource" class="com.cn.hjsj.base.SqlSource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<!-- write -->
<entry key="master" value-ref="masterdataSource"/>
<!-- read -->
<entry key="slave" value-ref="slavedataSource"/>
</map> </property>
<property name="defaultTargetDataSource" ref="masterdataSource"/>
</bean> <!-- 配置数据库注解aop -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
<bean id="manyDataSourceAspect" class="com.cn.hjsj.base.aop.DataSourceAspect" />
<aop:config>
<aop:aspect id="c" ref="manyDataSourceAspect">
<aop:pointcut id="tx" expression="execution(* com.cn.hjsj.dao.*.*(..))"/>
<aop:before pointcut-ref="tx" method="before"/>
</aop:aspect>
</aop:config>
<!-- 配置数据库注解aop -->

Java源码

1.DataSource

/**
* Created by LT on 2017-07-31.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
String value();
}

2.DataSourceAspect

/**
* Created by LT on 2017-07-31.
*/
public class DataSourceAspect {
public void before(JoinPoint point)
{
Object target = point.getTarget();
String method = point.getSignature().getName(); Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
.getMethod().getParameterTypes();
try {
Method m = classz[0].getMethod(method, parameterTypes);
if (m != null && m.isAnnotationPresent(DataSource.class)) {
DataSource data = m
.getAnnotation(DataSource.class);
DynamicDataSourceHolder.putDataSource(data.value());
System.out.println("datasource come from:"+data.value());
} } catch (Exception e) {
// TODO: handle exception
}
}
}

3.DynamicDataSource

/**
* Created by LT on 2017-07-31.
*/
public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceHolder.getDataSouce();
}
}

4.DynamicDataSourceHolder

/**
* Created by LT on 2017-07-31.
*/
public class DynamicDataSourceHolder {
public static final ThreadLocal<String> holder = new ThreadLocal<String>(); public static void putDataSource(String name) {
holder.set(name);
} public static String getDataSouce() {
return holder.get();
}
}

测试

1.mapper文件

<select id="getListCount" resultType="java.lang.Integer">
<![CDATA[
SELECT count(*) FROM system_user
]]>
</select>
<update id="userExit" parameterType="com.cn.hjsj.pojo.User">
update system_user
<set>
recordStatus = #{recordStatus,jdbcType=VARCHAR},
</set>
where userId= #{userId,jdbcType=INTEGER}
</update>

2.Dao层

   @DataSource("master")
public Integer getListCount();
@DataSource("slave")
public Integer userExit(User user);

3.Controller层

     int i = testService.getListCount();
int j = testService.userExit(user);

结果



根据 DataSourceAspect.java 打印出来的结果实现了读写分离。

Spring AOP 实现数据库读写分离的更多相关文章

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

    推荐第四种:https://github.com/shawntime/shawn-rwdb 方案1 通过MyBatis配置文件创建读写分离两个DataSource,每个SqlSessionFactor ...

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

    如何配置mysql数据库的主从? 单机配置mysql主从:http://my.oschina.net/god/blog/496 常见的解决数据库读写分离有两种方案 1.应用层 http://neore ...

  3. 从零开始学 Java - Spring AOP 实现主从读写分离

    深刻讨论为什么要读写分离? 为了服务器承载更多的用户?提升了网站的响应速度?分摊数据库服务器的压力?就是为了双机热备又不想浪费备份服务器?上面这些回答,我认为都不是错误的,但也都不是完全正确的.「读写 ...

  4. Spring boot实现数据库读写分离

    背景 数据库配置主从之后,如何在代码层面实现读写分离? 用户自定义设置数据库路由 Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样 ...

  5. 使用Spring AOP实现MySQL读写分离

    spring aop , mysql 主从配置 实现读写分离,下来把自己的配置过程,以及遇到的问题记录下来,方便下次操作,也希望给一些朋友带来帮助.mysql主从配置参看:http://blog.cs ...

  6. mysql+spring+mybatis实现数据库读写分离[代码配置] .

    场景:一个读数据源一个读写数据源. 原理:借助spring的[org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource] ...

  7. Spring aop应用之实现数据库读写分离

    Spring加Mybatis实现MySQL数据库主从读写分离 ,实现的原理是配置了多套数据源,相应的sqlsessionfactory,transactionmanager和事务代理各配置了一套,如果 ...

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

    一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案,更是最大限度了提高了应用中读取 (Read)数据的速度和并发量. 在进行数据库读写分离的时候,我们首先要进行数据库 ...

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

    使用Spring AOP实现MySQL数据库读写分离案例分析 原创 2016-12-29 徐刘根 Java后端技术 一.前言 分布式环境下数据库的读写分离策略是解决数据库读写性能瓶颈的一个关键解决方案 ...

随机推荐

  1. iOS CoreData 介绍和使用(以及一些注意事项)

    iOS CoreData介绍和使用(以及一些注意事项) 最近花了一点时间整理了一下CoreData,对于经常使用SQLite的我来说,用这个真的有点用不惯,个人觉得实在是没发现什么亮点,不喜勿喷啊.不 ...

  2. HDU 1506 Largest Rectangle in a Histogram【DP】

    题意:坐标轴上有连续的n个底均为1,高为h[i]的矩形,求能够构成的最大矩形的面积. 学习的别人的代码 @_@ 看底的坐标怎么找的看了好一会儿--- 记l[i]为矩形的底的左边的坐标,就将它一直向左扩 ...

  3. iOS系统的特点-iOS为什么运行更流畅

    1.进程管理机制-不允许后台进程: 2.用户事件响应优先级: 3.GPU加速: 4.系统内存管理机制: 5.运行机制-机器码直接运行-非虚拟机.

  4. CDR X6三折促销活动,可入

    继CDR X6双十二限量活动之后,CorelDRAW官方为庆祝2018新年新气象,折扣狂潮,又来一波.上次活动由于时间短,任务急,数量少,使得不少小伙伴抱憾而止,选择默默等待良机.现在,良机来了,即便 ...

  5. jdk1.8源码分析-hashMap

    在Java语言中使用的最多的数据结构大概右两种,第一种是数组,比如Array,ArrayList,第二种链表,比如ArrayLinkedList,基于数组的数据结构特点是查找速度很快,时间复杂度为 O ...

  6. 解析数据(正则,xpath)

    正则表达式拆分 import re # 1.拆分字符串 one = 'asdsfsgsh' # 标准 是 s 为拆分 pattern = re.compile('s') result = patter ...

  7. 服务器搭建域控与SQL Server的AlwaysOn环境过程(四)配置AlwaysOn

    0 引言 这一篇才真正开始搭建AlwaysOn,前三篇是为搭建AlwaysOn 做准备的. 步骤 1.3 配置AlwaysOn 请先使用本地用户Administrator登录这两个集群节点并执行下面的 ...

  8. chrome 获取移动端页面元素信息

    一:背景在使用appium进行app端自动化测试的时候,一般使用的是uiautomatorviewer来给页面元素做定位.但如果遇到页面元素类型是webview的时候,则只能定位整个页面,而不能更进一 ...

  9. UVA-1331 Minimax Triangulation 区间dp 计算几何 三角剖分 最大三角形最小化

    题目链接:https://cn.vjudge.net/problem/UVA-1331 题意 给一个任意多边形,把它分为多个三角形. 求某方案中最大的三角形是各方案中最小的面积的三角形面积. 思路 学 ...

  10. BZOJ 2794 [Poi2012]Cloakroom(离线+背包)

    2794: [Poi2012]Cloakroom Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 406  Solved: 241[Submit][St ...