zoukankan      html  css  js  c++  java
  • spring结合mybatis实现数据库读写分离

    随着系统用户访问量的不断增加,数据库的频繁访问将成为我们系统的一大瓶颈之一。由于项目前期用户量不大,我们实现单一的数据库就能完成。但是后期单一的数据库根本无法支撑庞大的项目去访问数据库,那么如何解决这个问题呢?



    实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据通常耗时比较长,占用数据库服务器的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接口上面


    源码地址

    http://ccblog.oss-cn-hangzhou.aliyuncs.com/file/dbShard_demo.rar


    数据库表就一张 根据mybatis的xml大家自己建一下




    另外这里还有一个瑕疵就是,当你使用注解事务的时候系统只能读取默认的数据源,这个问题主要是因为spring的事务和自定义的aop存在一个先后顺序的问题



     Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟spring的事务aop执行的先后顺序问题,比如说动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了Order(排序)这个关键字.

            我们可以通过在@AspectJ的方法中实现org.springframework.core.Ordered 这个接口来定义order的顺序,order 的值越小,说明越先被执行。







  • 相关阅读:
    UNION ALL
    jquery.validate的使用
    通过匹配绑定select option的文本值 模糊匹配
    .net 时间操作[摘抄]
    判断Table表中是否含有某一列
    将数组,表的某一列转换为string字符串的方法
    选出某一列不重复,某一列作为选择条件,其他列正常输出(摘抄)
    SQLite 解决:Could not load file or assembly 'System.Data.SQLite ... 试图加载格式不正确的程序(转)
    金山毒霸占用80端口
    DropDownList 绑定数据后 插入一条不属于表中的数据
  • 原文地址:https://www.cnblogs.com/jeffen/p/6274038.html
Copyright © 2011-2022 走看看