前言

随着业务量的不断增长,数据库的读写压力也越来越大。为了解决这个问题,我们可以采用读写分离的方案来分担数据库的读写负载。本文将介绍如何使用 Spring Boot + MyBatis Plus + MySQL 实现读写分离。

读写分离原理

读写分离是指将数据库的读操作和写操作分别放到不同的数据库实例上,从而达到分担数据库负载的目的。一般情况下,写操作的频率比读操作高,因此可以将写操作放到主库上,将读操作放到从库上。这样可以保证主库的写入性能,同时也可以提高从库的读取性能。

实现步骤

1. 主从复制搭建

首先,我们需要创建两个数据库实例,一个用于主库,一个用于从库。这里我们使用 MySQL 数据库作为示例。

参见搭建mysql主从复制一文。

2.配置pom.xml

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<!--aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.13</version>
</dependency>

3. 配置数据源

在 Spring Boot 中,我们可以使用 application.yml 文件来配置数据源。在这里,我们需要配置两个数据源,一个用于主库,一个用于从库。具体配置如下:

mysql:
datasource:
readNum: 1
type: com.alibaba.druid.pool.DruidDataSource
write:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.10.26.212:3306/test?useSSL=false&useUnicode=true
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat
read1:
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.10.26.213:3306/test?useSSL=false&useUnicode=true
minIdle: 5
maxActive: 100
initialSize: 10
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 50
removeAbandoned: true
filters: stat

4. 配置 MyBatis Plus

MyBatis Plus 是一个 MyBatis 的增强工具,它可以简化 MyBatis 的开发流程。在这里,我们需要在 application.yml 文件中配置 MyBatis Plus 的相关参数。具体配置如下:

mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
typeAliasesPackage: com.sandy.dyds.model

5. 实现读写分离

首先,我们需要创建一个 DbContextHolder 类,用于保存当前线程使用的数据源。具体实现如下:

@Log4j2
public class DbContextHolder {
public static final String WRITE = "write";
public static final String READ = "read";
private static ThreadLocal<String> contextHolder = new ThreadLocal<>(); public static void setDbType(String dbType) {
if(dbType == null) {
throw new NullPointerException();
}
contextHolder.set(dbType);
} public static String getDbType() {
return contextHolder.get() == null ? WRITE : contextHolder.get();
} public static void clearDbType() {
contextHolder.remove();
}
}

然后,我们需要创建一个 RoutingDataSource 类,用于动态切换数据源。具体实现如下:

@Log4j2
public class RoutingDataSource extends AbstractRoutingDataSource { @Value("${mysql.datasource.readNum}")
private int num;
@Override
protected Object determineCurrentLookupKey() {
String typeKey = DbContextHolder.getDbType();
if(typeKey.equals(DbContextHolder.WRITE)) {
return typeKey;
}
//使用随机数决定使用哪个读库
//在1-N之间生成整型随机数
int random = (int) (Math.random() * 1) + num;
return DbContextHolder.READ + random;
}
}

接着,我们需要创建一个 DataSourceConfig 类,用于配置数据源。具体实现如下:

@Configuration
public class DataSourceConfig { @Value("${mysql.datasource.type}")
private Class<? extends DataSource> dataSourceType; /**
* 写数据源
* Primary 标志这个 Bean 如果在多个同类 Bean 候选时,该 Bean 优先被考虑。
* 多数据源配置的时候注意,必须要有一个主数据源,用 @Primary 标志该 Bean
*/
@Primary
@Bean
@ConfigurationProperties(prefix = "mysql.datasource.write")
public DataSource writeDataSource() {
DataSource ds = DataSourceBuilder.create().type(dataSourceType).build();
return ds;
} @Bean
@ConfigurationProperties(prefix = "mysql.datasource.read1")
public DataSource readDataSource_1() {
DataSource ds = DataSourceBuilder.create().type(dataSourceType).build();
return ds;
} /**
* 设置数据源路由,通过该类中的determineCurrentLookupKey决定使用哪个数据源
*/
@Bean
public AbstractRoutingDataSource routingDataSource() {
RoutingDataSource proxy = new RoutingDataSource();
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbContextHolder.WRITE, writeDataSource());
targetDataSources.put(DbContextHolder.READ + "1", readDataSource_1());
proxy.setDefaultTargetDataSource(writeDataSource());
proxy.setTargetDataSources(targetDataSources);
return proxy;
} /**
* 由于Spring容器中现在有多个数据源,所以我们需要为事务管理器和MyBatis手动指定一个明确的数据源。
*/
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(routingDataSource());
MybatisConfiguration configuration = new MybatisConfiguration();
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
return sqlSessionFactory.getObject();
} @Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(routingDataSource());
}
}

最后,我们需要使用AOP以注解方式切换只读数据库。具体实现如下:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}
@Aspect
@Component
@Log4j2
public class DataSourceAop implements Ordered { @Around("@annotation(readOnly)")
public Object setRead(ProceedingJoinPoint joinPoint, ReadOnly readOnly) throws Throwable{
try {
DbContextHolder.setDbType(DbContextHolder.READ);
return joinPoint.proceed();
} finally {
//清除DbType一方面为了避免内存泄漏,更重要的是避免对后续在本线程上执行的操作产生影响
DbContextHolder.clearDbType();
log.info("清除threadLocal");
}
} @Override
public int getOrder() {
return 0;
}
}

总结

通过本文的介绍,我们了解了如何使用 Spring Boot + MyBatis Plus + MySQL 实现读写分离。读写分离可以有效地分担数据库的读写负载,提高数据库的性能和可用性。希望本文能对读写分离的实现有所帮助。

SpringBoot+MyBatisPlus实现读写分离的更多相关文章

  1. SpringBoot使用Sharding-JDBC读写分离

    本文介绍SpringBoot使用当当Sharding-JDBC进行读写分离. 1.有关Sharding-JDBC 本文还是基于当当网Sharding-Jdbc的依赖,与上一篇使用Sharding-Jd ...

  2. SpringBoot+MyBatis+MySQL读写分离

    1.  引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是依 ...

  3. SpringBoot+MyBatis+MySQL读写分离(实例)

    ​ 1. 引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是 ...

  4. Springboot + Mysql8实现读写分离

    在实际的生产环境中,为了确保数据库的稳定性,我们一般会给数据库配置双机热备机制,这样在master数据库崩溃后,slave数据库可以立即切换成主数据库,通过主从复制的方式将数据从主库同步至从库,在业务 ...

  5. SpringBoot + MyBatis + MySQL 读写分离实战

    1. 引言 读写分离要做的事情就是对于一条SQL该选择哪个数据库去执行,至于谁来做选择数据库这件事儿,无非两个,要么中间件帮我们做,要么程序自己做.因此,一般来讲,读写分离有两种实现方式.第一种是依靠 ...

  6. SpringBoot 玩转读写分离

    环境概览 前言介绍 Sharding-JDBC是当当网的一个开源项目,只需引入jar即可轻松实现读写分离与分库分表.与MyCat不同的是,Sharding-JDBC致力于提供轻量级的服务框架,无需额外 ...

  7. 分库分表(3) ---SpringBoot + ShardingSphere 实现读写分离

    分库分表(3)---ShardingSphere实现读写分离 有关ShardingSphere概念前面写了两篇博客: 1.分库分表(1) --- 理论 2. 分库分表(2) --- ShardingS ...

  8. 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离

    分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...

  9. 分库分表(7)--- SpringBoot+ShardingSphere实现分库分表 + 读写分离

    分库分表(7)--- ShardingSphere实现分库分表+读写分离 有关分库分表前面写了六篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理 ...

  10. Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离

    结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...

随机推荐

  1. ACM-NEFUOJ-P210畅通工程并查集

    题目:我已经明示到这个程度了你还不用并查集? #include<bits/stdc++.h> using namespace std; const int MAXN=1010; int F ...

  2. flask+gunicorn+nginx部署pytorch/python应用

    1. 基于flask实现python服务Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 .Flask使用 ...

  3. GitHub Pulse 是什么?它是否能衡量 OpenTiny 开源项目的健康程度?

    Pulse 是"脉搏"的意思,就像一个人要有脉搏才能算是一个活人,一个开源项目要有"脉搏"才能算是一个"活"的开源项目,这个单词非常形象地表 ...

  4. 中国省市区--地区SQL表

    SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for rc_district -- ---- ...

  5. ArrayList实现原理和自动扩容

    ArrayList在Java集合中的位置, ArrayList原理: transient Object[] elementData; ArrayList通过数组来实现. 默认构造方法会构造一个容量为1 ...

  6. Email发送邮件使用ical4j将时间同步日历中

    1.Maven依赖 <!--邮件--> <dependency> <groupId>org.springframework.boot</groupId> ...

  7. 如何建设一个用于编译 iOS App 的 macOS 云服务器集群?

    作者:京东零售 叶萌 现代软件开发一般会借助 CI/CD 来提升代码质量.加快发版速度.自动化重复的事情,iOS App 只能在 mac 机器上编译,CI/CD 工具因此需要有一个 macOS 云服务 ...

  8. 深度学习02-03(图像处理、OpenCV实验案例)

    OpenCV实验案例 文章目录 OpenCV实验案例 一.OpenCV安装 1. OpenCV介绍 2. 安装 二.OpenCV基本操作 1. 图像读取与保存 1)读取.图像.保存图像 2. 图像色彩 ...

  9. [Pytorch框架] 2.1.2 使用PyTorch计算梯度数值

    文章目录 使用PyTorch计算梯度数值 Autograd 简单的自动求导 复杂的自动求导 Autograd 过程解析 扩展Autograd import torch torch.__version_ ...

  10. Centos7安装JDK1.8详细步骤

    JDK 解压JDK安装文件.在终端中,进入你下载的JDK安装文件所在的目录,然后执行以下命令: tar -zxvf jdk-<版本号>-linux-x64.tar.gz 其中,<版本 ...