百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 文章教程 > 正文

详细介绍一下如何在Spring Boot中实现数据库的读写分离?

xsobi 2024-12-14 15:45 1 浏览

在分布式高并发系统中我们可以通过数据库的读写分离操作来提升系统的性能以及可扩展性,在SpringBoot中也提供了很多快捷方便的配置来实现数据库的读写分离配置,通过读写分离配置将对数据的读写操作分配到不同的数据库实例上从而达到提升系统性能的目的。下面我们就来详细介绍一下如何在Spring Boot应用程序中实现数据的读写分离操作。

配置多数据源

为了实现读写分离配置,首先需要配置多个数据源的支持,一个数据源用来进行写操作,而另一个数据源用来进行读操作,在SpringBoot中可以通过DataSource来配置实现多数据源操作。

配置主库和从库的数据源

假设,我们有两个数据库一个是主库用来进行写操作,一个是从库用来进行读操作,然后我们需要在配置文件中分别配置两个数据库链接,如下所示。

spring:
  datasource:
    write:
      url: jdbc:mysql://localhost:3306/write_db
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        maximum-pool-size: 10
    read:
      url: jdbc:mysql://localhost:3307/read_db
      username: root
      password: password
      driver-class-name: com.mysql.cj.jdbc.Driver
      hikari:
        maximum-pool-size: 10

配置数据源Bean

接下来就需要在配置类中分别创建主从二库的DataSource Bean,为了更好的管理这些数据源操作,我们通过@Primary注解来指定主数据源(即写库)作为默认数据源。

@Configuration
public class DataSourceConfig {

    @Bean(name = "writeDataSource")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.write")
    public DataSource writeDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "readDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.read")
    public DataSource readDataSource() {
        return DataSourceBuilder.create().build();
    }
}

实现动态数据源路由

在数据库的读写分离操作中,我们需要根据不同的操作来选择不同的数据源来进行操作,一般情况下,写操作会从主库操作,而读操作则是由从库来实现,如下所示,我们可以通过动态数据路由操作来实现这个操作。

创建动态数据源

首先定义了一个继承自AbstractRoutingDataSource的DynamicDataSource动态数据源控制类类,然后重写了其中的determineCurrentLookupKey()方法来决定当前操作应该使用哪个数据源。

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // 获取当前操作的数据库类型(主库或从库)
        return DataSourceContextHolder.getDatabaseType();
    }
}

创建一个上下文持有者

为了能够在不同线程的上下文中,存储当前线程使用的数据库的类型,我们可以通过ThreadLocal来实现线程单独变量的存储操作,用来标识不同的数据源类型,如下所示。

public class DataSourceContextHolder {

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

    public static void setDatabaseType(String databaseType) {
        contextHolder.set(databaseType);
    }

    public static String getDatabaseType() {
        return contextHolder.get();
    }

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

配置动态数据源Bean

完成上面的操作之后,需要在SpringBoot中配置DynamicDataSource实现,并且将其注册到容器中来进行数据源操作的管理,如下所示。

@Configuration
public class DataSourceConfig {

    @Bean
    public DynamicDataSource dataSource(@Qualifier("writeDataSource") DataSource writeDataSource,
                                        @Qualifier("readDataSource") DataSource readDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("write", writeDataSource);
        targetDataSources.put("read", readDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(writeDataSource); // 默认使用主库
        return dynamicDataSource;
    }
}

配置事务管理器

完成了数据源的管理之后,需要对不同数据源的事务管理进行配置,来保证不同数据源中的数据事务的一致性和完整性,如下所示。需要配置一个事务管理器来对其进行管理配置。

@Configuration
public class TransactionConfig {

    @Bean
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

使用AOP切换数据源

为了在不同的数据操作的时候能够自动切换到不同的数据处理库中,我们可以通过AOP来实现动态数据源的切换操作。

创建一个切面类

为了实现动态数据源的自动切换,我们需要创建一个切面类来识别用户数据操作。如下所示。

@Aspect
@Component
public class DataSourceAspect {

    @Around("@annotation(ReadOnly)")  // 只读操作注解
    public Object setReadDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
        DataSourceContextHolder.setDatabaseType("read");  // 设置为从库
        try {
            return joinPoint.proceed();
        } finally {
            DataSourceContextHolder.clearDatabaseType();
        }
    }

    @Around("@annotation(WriteOnly)")  // 写操作注解
    public Object setWriteDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
        DataSourceContextHolder.setDatabaseType("write");  // 设置为主库
        try {
            return joinPoint.proceed();
        } finally {
            DataSourceContextHolder.clearDatabaseType();
        }
    }
}

创建自定义注解

然后我们可以创建自定义的注解用来标识那些方法是读操作,那些方法是写操作,如下所示,我们定义了两个注解分别来表示读操作和写操作。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReadOnly {
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WriteOnly {
}

总结

到这里我们就已经完成来了读写操作的数据库配置,下面我们就可以在服务层中通过上面定义好的注解来标识读写操作。

@Service
public class UserService {

    @ReadOnly
    public List<User> getUsers() {
        return userRepository.findAll();  // 从库读取数据
    }

    @WriteOnly
    public void saveUser(User user) {
        userRepository.save(user);  // 主库写入数据
    }
}

通过以上步骤,我们成功地在 Spring Boot 项目中实现了数据库的读写分离。

相关推荐

好用的云函数!后端低代码接口开发,零基础编写API接口

前言在开发项目过程中,经常需要用到API接口,实现对数据库的CURD等操作。不管你是专业的PHP开发工程师,还是客户端开发工程师,或者是不懂编程但懂得数据库SQL查询,又或者是完全不太懂技术的人,通过...

快速上手:Windows 平台上 cURL 命令的使用方法

在工作流程中,为了快速验证API接口有效性,团队成员经常转向直接执行cURL命令的方法。这种做法不仅节省时间,而且促进了团队效率的提升。对于使用Windows系统的用户来说,这里有一套详细...

使用 Golang net/http 包:基础入门与实战

简介Go的net/http包是构建HTTP服务的核心库,功能强大且易于使用。它提供了基本的HTTP客户端和服务端支持,可以快速构建RESTAPI、Web应用等服务。本文将介绍ne...

#小白接口# 使用云函数,人人都能编写和发布自己的API接口

你只需编写简单的云函数,就可以实现自己的业务逻辑,发布后就可以生成自己的接口给客户端调用。果创云支持对云函数进行在线接口编程,进入开放平台我的接口-在线接口编程,设计一个新接口,设计和配置好接口参...

极度精神分裂:我家没有墙面开关,但我虚拟出来了一系列开关

本内容来源于@什么值得买APP,观点仅代表作者本人|作者:iN在之前和大家说过,在iN的家里是没有墙面开关的。...

window使用curl命令的注意事项 curl命令用法

cmd-使用curl命令的注意点前言最近在cmd中使用curl命令来测试restapi,发现有不少问题,这里记录一下。在cmd中使用curl命令的注意事项json不能由单引号包括起来json...

Linux 系统curl命令使用详解 linuxctrl

curl是一个强大的命令行工具,用于在Linux系统中进行数据传输。它支持多种协议,包括HTTP、HTTPS、FTP等,用于下载或上传数据,执行Web请求等。curl命令的常见用法和解...

Tornado 入门:初学者指南 tornados

Tornado是一个功能强大的PythonWeb框架和异步网络库。它最初是为了处理实时Web服务中的数千个同时连接而开发的。它独特的Web服务器和框架功能组合使其成为开发高性能Web...

PHP Curl的简单使用 php curl formdata

本文写给刚入PHP坑不久的新手们,作为工具文档,方便用时查阅。CURL是一个非常强大的开源库,它支持很多种协议,例如,HTTP、HTTPS、FTP、TELENT等。日常开发中,我们经常会需要用到cur...

Rust 服务器、服务和应用程序:7 Rust 中的服务器端 Web 应用简介

本章涵盖使用Actix提供静态网页...

我给 Apache 顶级项目提了个 Bug apache顶级项目有哪些

这篇文章记录了给Apache顶级项目-分库分表中间件ShardingSphere提交Bug的历程。说实话,这是一次比较曲折的Bug跟踪之旅。10月28日,我们在GitHub上提...

linux文件下载、服务器交互(curl)

基础环境curl命令描述...

curl简单使用 curl sh

1.curl--help#查看关键字2.curl-A“(添加user-agent<name>SendUser-Agent<name>toserver)”...

常用linux命令:curl 常用linux命令大全

//获取网页内容//不加任何选项使用curl时,默认会发送GET请求来获取内容到标准输出$curlhttp://www.baidu.com//输出<!DOCTYPEh...

三十七,Web渗透提高班之hack the box在线靶场注册及入门知识

一.注册hacktheboxHackTheBox是一个在线平台,允许测试您的渗透技能和代码,并与其他类似兴趣的成员交流想法和方法。它包含一些不断更新的挑战,并且模拟真实场景,其风格更倾向于CT...