Spring-boot实现数据库读写分离

近来参与了spring-boot主从数据库的设计,参考了一些网上资料,发现不完整.

整理了个完整版的^ ^!

spring-boot主从数据库

    是从springmvc的思路上来做的,主要就是配置主,从DataSource,

再继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,

通过Context结合AOP进行数据主,从库的切换.


Spring boot提供了AbstractRoutingDataSource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。

1.实现可动态路由的数据源,在每次数据库查询操作前执行

ReadWriteSplitRoutingDataSource.java

package com.bag.config.mybatis;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class ReadWriteSplitRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DbContextHolder.getDbType();
    }

}


2.线程私有路由配置,用于ReadWriteSplitRoutingDataSource动态读取配置

DbContextHolder.java

package com.bag.config.mybatis;

public class DbContextHolder {

    public enum DbType {
        MASTER, SLAVE
    }

    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    public static void setDbType(DbType dbType) {
        if (dbType == null) throw new NullPointerException();
        contextHolder.set(dbType);
    }

    public static DbType getDbType() {
        return contextHolder.get() == null ? DbType.MASTER : contextHolder.get();
    }

    public static void clearDbType() {
        contextHolder.remove();
    }

}


AOP优化代码
    利用AOP将设置数据库的操作从代码中抽离,这里的粒度控制在方法级别,所以利用注解的

    形式标注这个方法涉及的数据库事务只读,走从库.

3.只读注解,用于标注方法的数据库操作只走从库.

ReadOnlyConnectionction.java

package com.bag.config.mybatis;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnlyConnection {
}



ReadOnlyConnectionInterceptor.java

package com.bag.config.mybatis;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

/**
 * Spring AOP对注解的拦截
 */
@Aspect
@Component
public class ReadOnlyConnectionInterceptor implements Ordered {

    public static final Logger logger = LoggerFactory.getLogger(ReadOnlyConnectionInterceptor.class);

    /**
     * 切换到从库
     *
     * @param proceedingJoinPoint
     * @param readOnlyConnection
     * @return
     * @throws Throwable
     */
    @Around("@annotation(readOnlyConnection)")
    public Object proceed(ProceedingJoinPoint proceedingJoinPoint, ReadOnlyConnection readOnlyConnection) throws Throwable {
        try {
//            logger.info("设置只读数据库");
            DbContextHolder.setDbType(DbContextHolder.DbType.SLAVE);
            Object result = proceedingJoinPoint.proceed();
            return result;
        } finally {
            DbContextHolder.clearDbType();
            logger.info("restore database connection");
        }
    }


    @Override
    public int getOrder() {
        return 0;
    }
}


使用案例:

UserService.java

@ReadOnlyConnection
public List getUsers(Integer page, Integer limit) {
    return repository.findAll(new PageRequest(page, limit));
}


配置文件:

application.properties

# 主数据库
spring.datasource.url=
spring.datasource.username=
spring.datasource.password=
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.testOnBorrow=true
spring.datasource.validation-query=SELECT 1
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.initial-size=5
spring.datasource.min-idle=5
spring.datasource.max-active=20
spring.datasource.maxWait=200000
spring.datasource.timeBetweenEvictionRunsMillis=30000
spring.datasource.minEvictableIdleTimeMillis=30000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#超过时间限制是否回收
spring.datasource.removeAbandoned=true
#超时时间;单位为秒。180秒=3分钟
spring.datasource.removeAbandonedTimeout=5

# 从数据源
spring.slave.type=com.alibaba.druid.pool.DruidDataSource
spring.slave.driver-class-name=com.mysql.jdbc.Driver
spring.slave.url=
spring.slave.username=
spring.slave.password=
spring.slave.initial-size=5
spring.slave.min-idle=5
spring.slave.max-active=20


你可能感兴趣的:(数据库)