随着系统用户访问量的不断增加, 数据库的频繁访问将成为我们系统的一大瓶颈之一. 由于项目前期用户量不大, 我们实现单一的数据库就能完成. 但是后期单一的数据库根本无法支撑庞大的项目去访问数据库, 那么如何解决这个问题呢?
实际的应用中, 数据库都是读多写少(读取数据的频率高, 更新数据的频率相对较少), 而读取数据通常耗时比较长, 占用数据库服务器的 CPU 较多, 从而影响用户体验. 我们通常的做法就是把查询从主库中抽取出来, 采用多个从库, 使用负载均衡, 减轻每个从库的查询压力.
采用读写分离技术的目标: 有效减轻 Master 库的压力, 又可以把用户查询数据的请求分发到不同的 Slave 库, 从而保证系统的健壮性. 我们看下采用读写分离的背景.
我们在项目开发初期的时候就设计了一个简单的读写分离, 现在我把 demo 分享给大家.
采用技术 Spring + mybatis
首先定义一个 annotation
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Target;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface DataSource {
- public String value();
- }
再定义一个 HandleDataSource
- public class HandleDataSource {
- public static final ThreadLocal holder = new ThreadLocal();
- public static void putDataSource(String datasource) {
- holder.set(datasource);
- }
- public static String getDataSource() {
- return holder.get();
- }
- }
定义一个切面
- import java.lang.reflect.Method;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.reflect.MethodSignature;
- public class DataSourceAspect {
- public void pointCut() {
- };
- 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);
- HandleDataSource.putDataSource(data.value());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
获取数据源
- import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
- public class ChooseDataSource extends AbstractRoutingDataSource {
- protected Object determineCurrentLookupKey() {
- return HandleDataSource.getDataSource();
- }
- }
配置 xml
classpath*:MySQL.propertiescom.MySQL.jdbc.Driver${jdbc.url}${jdbc.user}${jdbc.password}SELECT 1 FROM DUAL32510010000true60com.MySQL.jdbc.Driver${jdbc.url.read}${jdbc.user.read}${jdbc.password.read}SELECT 1 FROM DUAL32510010000true60
注解到 service 接口上面
数据库表就一张 根据 mybatis 的 xml 大家自己建一下
另外这里还有一个瑕疵就是, 当你使用注解事务的时候系统只能读取默认的数据源, 这个问题主要是因为 spring 的事务和自定义的 aop 存在一个先后顺序的问题
Spring 中的事务是通过 aop 来实现的, 当我们自己写 aop 拦截的时候, 会遇到跟 spring 的事务 aop 执行的先后顺序问题, 比如说动态切换数据源的问题, 如果事务在前, 数据源切换在后, 会导致数据源切换失效, 所以就用到了 Order(排序)这个关键字.
我们可以通过在 @AspectJ 的方法中实现 org.springframework.core.Ordered 这个接口来定义 order 的顺序, order 的值越小, 说明越先被执行.
本文源码获取: 关注我, 感谢大家支持. 点击这里加入获取
我们有自己的高清思维方向导图以及阿里架构师讲解的架构视频分享 (包括高可用, 高并发, spring 源码, mybatis 源码, JVM, 大数据, Netty 等多个技术知识的架构视频资料和各种电子书籍阅读) 视频资料获取方式感谢大家支持. 点击这里加入获取
免费的资料都是免费分享的, 信则有, 不信则无, 名额有限, 先到先得!!!
精讲架构视频资料获取方式感谢大家支持. 点击这里加入获取
以及一些一线互联网公司的面试题解析含答案
来源: http://www.jianshu.com/p/6d0abfae6192