SpringBoot集成mybatis实现mysql读写分离

前言

最近项目中需要实现mysql读写分离,参考了网上的一些例子,终于搞明白了怎么回事。特做笔记以供日后参考,也希望给有需要的朋友们提供些帮助。由于本人知识有限,如有不对的地方请大家指正。

为什么要读写分离

日常开发中,读数据库可能占了业务的80%,写数据库操作需要的时间要远远高于读数据库的时间。因此读写分离大大提高系统查询的效率,在数据量大的情景下效果更加明显。读写分离的原理主要是让master(主数据库)来响应事务性操作,让slave(从数据库)来响应select非事务性操作,然后再采用主从复制来把master上的事务性操作同步到slave数据库中。

开发环境

windows10
mysql 5.8
Intelli IDEA 2020.1.1

配置mysql

设置一主两从数据库。具体的配置过程可以参考这位大佬的博客,如果配置数据库不顺利可以留言交流。我看到网上有人设置主从数据库分别是同一个监听端口下的不同的database,我真是不知道同一个监听端口下如何实现主从分离和同步,有人知道的请告诉我。

项目配置

主工程目录
SpringBoot集成mybatis实现mysql读写分离_第1张图片
pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.qiansheng</groupId>
    <artifactId>web2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>web2</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>


        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>


        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.17</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.11</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.11</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

applicaotion.properties文件

server.servlet.context-path=/boot
server.port=8082
spring.datasource.readSize = 2

spring.datasource.username=root
spring.datasource.password=1234
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3307/test?serverTimezone=UTC&useSSL=false&characterEncoding=UTF-8

#Mybatis 配置
mybatis.config-location=mybatis/mybatis.xml
#mapper配置
mybatis.mapper-locations=mapper/*.xml

spring.main.allow-bean-definition-overriding=true
#主数据源配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
spring.datasource.useGlobalDataSourceStat=true


# 从数据源
#spring.slave.type=com.alibaba.druid.pool.DruidDataSource
spring.slave.driverClassName=com.mysql.cj.jdbc.Driver
spring.slave.url=jdbc:mysql://127.0.0.1:3308/test?serverTimezone=UTC&useSSL=false&characterEncoding=UTF-8
spring.slave.username=root
spring.slave.password=1234
spring.slave.initialSize=5
spring.slave.minIdle=5
spring.slave.maxActive=20
# 配置获取连接等待超时的时间
spring.slave.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.slave.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.slave.minEvictableIdleTimeMillis=300000
spring.slave.validationQuery=SELECT 1 FROM DUAL
spring.slave.testWhileIdle=true
spring.slave.testOnBorrow=false
spring.slave.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.slave.poolPreparedStatements=true
spring.slave.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.slave.filters=stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.slave.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
spring.slave.useGlobalDataSourceStat=true

#spring.read2.type=com.alibaba.druid.pool.DruidDataSource
spring.read2.driverClassName=com.mysql.cj.jdbc.Driver
spring.read2.url=jdbc:mysql://127.0.0.1:3309/test?serverTimezone=UTC&useSSL=false&characterEncoding=UTF-8
spring.read2.username=root
spring.read2.password=1234
spring.read2.initialSize=5
spring.read2.minIdle=5
spring.read2.maxActive=20
# 配置获取连接等待超时的时间
spring.read2.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.read2.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.read2.minEvictableIdleTimeMillis=300000
spring.read2.validationQuery=SELECT 1 FROM DUAL
spring.read2.testWhileIdle=true
spring.read2.testOnBorrow=false
spring.read2.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.read2.poolPreparedStatements=true
spring.read2.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.read2.filters=stat,wall,slf4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.read2.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
spring.read2.useGlobalDataSourceStat=true

面向切面

@Slf4j
@Aspect
@Component
public class DataSourceAop {
    @Before("execution(* com.qiansheng.web2.modules.test.mapper..*.find*(..)) "
            + " || execution(* com.qiansheng.web2.modules.test.mapper..*.get*(..)) "
            + " || execution(* com.qiansheng.web2.modules.test.mapper..*.query*(..))")
    public void setReadDataSourceType() {
        DataSourceContextHolder.read();
        log.info("dataSource 切换到:Read");
    }

    @Before("execution(* com.qiansheng.web2.modules.test.mapper..*.insert*(..)) "
            + " || execution(* com.qiansheng.web2.modules.test.mapper..*.update*(..))"
            + " || execution(* com.qiansheng.web2.modules.test.mapper..*.add*(..))")
    public void setWriteDataSourceType() {
        DataSourceContextHolder.write();
        log.info("dataSource 切换到:Write");
    }

    @After("execution(* com.qiansheng.web2.modules.test.mapper..*.*(..))")
    public void remove(){
        DataSourceContextHolder.clear();
        log.info("dataSource clear");
    }
}

DataSourceContextHolder文件

@Slf4j
public class DataSourceContextHolder {

    @Getter
    private static final ThreadLocal<String> local = new ThreadLocal<String>();

    public static void read() {
        log.debug("读操作");
        local.set(DataSourceType.read.getType());
    }

    public static void write() {
        log.debug("写操作");
        local.set(DataSourceType.write.getType());
    }

    public static String getReadOrWrite() {
        return local.get();
    }

    public static void clear(){
        local.remove();
    }
}

写上mapper接口就可以查看效果了。

总结:之前试了好久才成功,注意原因是数据库主从设置一直没设置好,Mybits文件设置一定要路径正确。
源代码

参考连接:https://blog.csdn.net/wang_shuyu/article/details/79304364
https://www.cnblogs.com/cjsblog/p/9712457.html

你可能感兴趣的:(spring,boot)