Spring Boot集成Mybatis-Plus多租户架构实战

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: Spring Boot集成Mybatis-Plus多租户架构实战

经过查阅资料,以及在后续研发功能时,不断的加深了对多租户的理解。

那么接下来让我们问自己几个问题:1.什么是多租户架构?2.多租户架构方案以及各自的优缺点?3.多租户架构的适用场景?

一. 什么是多租户

多租户技术或称多重租赁技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离。那么重点就很浅显易懂了,多租户的重点就是同一套程序下实现多用户数据的隔离。

二. 多租户架构以及数据隔离方案

多租户在数据存储上主要存在三种方案,分别是:

1. 独立数据库

即一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本较高。

  • 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求;如果出现故障,恢复数据比较简单。
  • 缺点:增多了数据库的安装数量,随之带来维护成本和购置成本的增加。

2. 共享数据库,独立 Schema

也就是说 共同使用一个数据库 使用表进行数据隔离多个或所有租户共享Database,但是每个租户一个Schema(也可叫做一个user)。底层库比如是:DB2、ORACLE等,一个数据库下可以有多个SCHEMA。

  • 优点:为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可支持更多的租户数量。
  • 缺点:如果出现故障,数据恢复比较困难,因为恢复数据库将牵涉到其他租户的数据;

3. 共享数据库,共享 Schema,共享数据表

也就是说 共同使用一个数据库一个表 使用字段进行数据隔离

即租户共享同一个Database、同一个Schema,但在表中增加TenantID多租户的数据字段。这是共享程度最高、隔离级别最低的模式。

简单来讲,即每插入一条数据时都需要有一个客户的标识。这样才能在同一张表中区分出不同客户的数据,这也是我们系统目前用到的(tenant_id)

  • 优点:三种方案比较,第三种方案的维护和购置成本最低,允许每个数据库支持的租户数量最多。
  • 缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量;数据备份和恢复最困难,需要逐表逐条备份和还原。

三.多租户架构适用场景?

衡量三种模式主要考虑的因素是隔离还是共享。1.成本角度因素隔离性越好,设计和实现的难度和成本越高,初始成本越高。共享性越好,同一运营成本下支持的用户越多,运营成本越低。

2.安全因素要考虑业务和客户的安全方面的要求。安全性要求越高,越要倾向于隔离。

3.从租户数量上考虑主要考虑下面一些因素

  • 系统要支持多少租户?上百?上千还是上万?可能的租户越多,越倾向于共享。
  • 平均每个租户要存储数据需要的空间大小。存贮的数据越多,越倾向于隔离。
  • 每个租户的同时访问系统的最终用户数量。需要支持的越多,越倾向于隔离。
  • 是否想针对每一租户提供附加的服务,例如数据的备份和恢复等。这方面的需求越多, 越倾向于隔离

4.技术储备共享性越高,对技术的要求越高。

四. 技术实现

技术选型: Mybatis-Plus这里我们选用了第三种方案(共享数据库,共享 Schema,共享数据表)来实现,也就意味着,每个数据表都需要有一个租户标识(tenant_id)

现在有数据库表(user)如下:

字段名 字段类型 描述
id int(11) 主键
name varchar(30) 姓名
tenant_id int(11) 多租户id

将tenant_id视为租户ID,用来隔离租户与租户之间的数据,如果要查询当前租户的用户,SQL大致如下:

SELECT * FROM user WHERE tenant_id = 1;

试想一下,除了一些系统共用的表以外,其他租户相关的表,我们都需要加上AND tenant_id = ?查询条件,数据表多的情况时就会漏加导致数据泄露。幸亏有mybatis-plus这个插件,可以极为方便的实现多租户SQL解析器,官方文档如下:多租户 SQL 解析器

正式进入主题 环境搭建演示

1. 创建Spring Boot项目

pom文件:

<?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 http://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.1.7.RELEASE</version>        <relativePath/>    </parent>    <groupId>com.xd</groupId>    <artifactId>mybatis-plus-multi-tenancy</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>mybatis-plus-multi-tenancy</name>    <description>基于Spring Boot Mybatis-Plus的多租户架构</description>    <properties>        <java.version>1.8</java.version>        <mybatis-plus.version>3.1.2</mybatis-plus.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <!--mysql-->        <dependency>            <groupId>mysql</groupId>            <artifactId>mysql-connector-java</artifactId>            <scope>runtime</scope>        </dependency>        <!--lombok-->        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <!--Mybatis-Plus依赖-->        <dependency>            <groupId>com.baomidou</groupId>            <artifactId>mybatis-plus-boot-starter</artifactId>            <version>${mybatis-plus.version}</version>        </dependency>        <!--测试相关依赖-->        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>junit</groupId>            <artifactId>junit</artifactId>        </dependency>        <dependency>            <groupId>org.springframework</groupId>            <artifactId>spring-test</artifactId>            <version>5.2.0.M1</version>            <scope>compile</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-test</artifactId>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>复制代码

application.properties

# 数据源配置spring.datasource.type=com.zaxxer.hikari.HikariDataSourcespring.datasource.hikari.minimum-idle=3spring.datasource.hikari.maximum-pool-size=10# 不能小于30秒,否则默认回到1800秒spring.datasource.hikari.max-lifetime=30000spring.datasource.hikari.connection-test-query=SELECT 1spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/multi?useUnicode=true&characterEncoding=UTF-8spring.datasource.username=rootspring.datasource.password=rootlogging.level.com.xd.mybatisplusmultitenancy=debug复制代码

对应的SQL数据库初始化schema文件

SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for user-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',  `name` varchar(30) DEFAULT NULL COMMENT '姓名',  `tenant_id` int(11) NOT NULL COMMENT '多租户ID',  PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;SET FOREIGN_KEY_CHECKS = 1;复制代码

MybatisPlusConfig核心配置:TenantSqlParser多租户处理器

package com.xd.mybatisplusmultitenancy.config;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;import lombok.extern.slf4j.Slf4j;import net.sf.jsqlparser.expression.Expression;import net.sf.jsqlparser.expression.LongValue;import net.sf.jsqlparser.expression.NullValue;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List;/** * @Classname PreTenantHandler * @Description 租户处理器 -主要实现mybatis-plus https://mp.baomidou.com/guide/tenant.html * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com * @Date 2019-08-09 23:34 * @Version 1.0 */@Slf4j@Componentpublic class MyTenantHandler implements TenantHandler {    /**     * 多租户标识     */    private static final String SYSTEM_TENANT_ID = "tenant_id";    /**     * 需要过滤的表     */    private static final List<String> IGNORE_TENANT_TABLES = new ArrayList<>();    @Autowired    private MyContext apiContext;    /**     * 租户Id     *     * @return     */    @Override    public Expression getTenantId() {        // 从当前系统上下文中取出当前请求的服务商ID,通过解析器注入到SQL中。        Long tenantId = apiContext.getCurrentTenantId();        log.debug("当前租户为{}", tenantId);        if (tenantId == null) {            return new NullValue();        }        return new LongValue(tenantId);    }    /**     * 租户字段名     *     * @return     */    @Override    public String getTenantIdColumn() {        return SYSTEM_TENANT_ID;    }    /**     * 根据表名判断是否进行过滤     * 忽略掉一些表:如租户表(sys_tenant)本身不需要执行这样的处理     *     * @param tableName     * @return     */    @Override    public boolean doTableFilter(String tableName) {        return IGNORE_TENANT_TABLES.stream().anyMatch((e) -> e.equalsIgnoreCase(tableName));    }}复制代码

MybatisPlus的配置

package com.xd.mybatisplusmultitenancy.config;import com.baomidou.mybatisplus.core.parser.ISqlParser;import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;import org.mybatis.spring.annotation.MapperScan;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import net.sf.jsqlparser.expression.Expression;import net.sf.jsqlparser.expression.LongValue;import java.util.ArrayList;import java.util.List;/** * @Classname MybatisPlusConfig * @Description TODO * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com * @Date 2019-08-09 22:44 * @Version 1.0 */@Configuration@MapperScan("com.xd.mybatisplusmultitenancy.mapper")public class MybatisPlusConfig {    @Autowired    private MyTenantHandler myTenantHandler;    @Bean    public PaginationInterceptor paginationInterceptor() {        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();        // SQL解析处理拦截:增加租户处理回调。        List<ISqlParser> sqlParserList = new ArrayList<>();        // 攻击 SQL 阻断解析器、加入解析链        sqlParserList.add(new BlockAttackSqlParser());        // 多租户拦截        TenantSqlParser tenantSqlParser = new TenantSqlParser();        tenantSqlParser.setTenantHandler(myTenantHandler);        sqlParserList.add(tenantSqlParser);        paginationInterceptor.setSqlParserList(sqlParserList);        return paginationInterceptor;    }    /**     * 性能分析拦截器,不建议生产使用     * 用来观察 SQL 执行情况及执行时长     */    @Bean(name = "performanceInterceptor")    public PerformanceInterceptor performanceInterceptor() {        return new PerformanceInterceptor();    }}复制代码

自定义系统的上下文

package com.xd.mybatisplusmultitenancy.config;import org.springframework.stereotype.Component;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/** * @Classname ApiContext * @Description 当前系统的上下文 * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com * @Date 2019-08-09 22:47 * @Version 1.0 */@Componentpublic class MyContext {    private static final String KEY_CURRENT_TENANT_ID = "KEY_CURRENT_PROVIDER_ID";    private static final Map<String, Object> M_CONTEXT = new ConcurrentHashMap<>();    public void setCurrentTenantId(Long tenantId) {        M_CONTEXT.put(KEY_CURRENT_TENANT_ID, tenantId);    }    public Long getCurrentTenantId() {        return (Long) M_CONTEXT.get(KEY_CURRENT_TENANT_ID);    }}复制代码

Entity、Mapper 省略...

*/
@Slf4j
@RunWith(SpringRunner.class)
aFixMethodOrder(MethodSorters.JVM)
@SpringBootTest(classes =MybatisPlusMultiTenancyApplication.class) public class MybatisPlusMultiTenancyApplicationTests{
@Autowired
private MyContext apiContext;
@Autowired
private UserMapper userMapper;
/**
* 模拟当前系统的多租户Id*/
@Before
public void before() {
// 在上下文中设置当前多租户id
apiContext.setCurrentTenantId(1L);}
@Test
public void insert() {
// 新增数据
User user = new User().setName("小明");//判断一个条件是true还是false
Assert.assertTrue( condition: userMapper.insert(user)>0); user=userMapper,selectById(user.getId()); log.info("插入数据:{}",user);//判断是否相等
Assert.assertEquals(apiContext.getCurrentTenantId(),user.getTenantId());
}
@Test
public void selectList(){
log.info("查询数据{}",e); userMapper.selectList( queryWrapper;null).forEach((e)->{李告车的博家
Assert.assertEquals(apiContext.getCurrentTenantId(),e.getTenantId()); 

2. 单元测试

package com.xd.mybatisplusmultitenancy.test;import com.baomidou.mybatisplus.core.toolkit.Wrappers;import com.xd.mybatisplusmultitenancy.MybatisPlusMultiTenancyApplication;import com.xd.mybatisplusmultitenancy.config.MyContext;import com.xd.mybatisplusmultitenancy.entity.User;import com.xd.mybatisplusmultitenancy.mapper.UserMapper;import lombok.extern.slf4j.Slf4j;import org.junit.Assert;import org.junit.Before;import org.junit.FixMethodOrder;import org.junit.Test;import org.junit.runner.RunWith;import org.junit.runners.MethodSorters;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.sql.Wrapper;/** * @Classname MybatisPlusMultiTenancyApplicationTests * @Description TODO * @Author Created by Lihaodong (alias:小东啊) lihaodongmail@163.com * @Date 2019-08-09 22:50 * @Version 1.0 */@Slf4j@RunWith(SpringRunner.class)@FixMethodOrder(MethodSorters.JVM)@SpringBootTest(classes = MybatisPlusMultiTenancyApplication.class)public class MybatisPlusMultiTenancyApplicationTests {    @Autowired    private MyContext apiContext;    @Autowired    private UserMapper userMapper;    /**     * 模拟当前系统的多租户Id     */    @Before    public void before() {        // 在上下文中设置当前多租户id        apiContext.setCurrentTenantId(1L);    }    @Test    public void insert() {        // 新增数据        User user = new User().setName("小明");        //判断一个条件是true还是false        Assert.assertTrue(userMapper.insert(user) > 0);        user = userMapper.selectById(user.getId());        log.info("插入数据:{}", user);        // 判断是否相等        Assert.assertEquals(apiContext.getCurrentTenantId(), user.getTenantId());    }    @Test    public void selectList() {        userMapper.selectList(null).forEach((e) -> {            log.info("查询数据{}", e);            Assert.assertEquals(apiContext.getCurrentTenantId(), e.getTenantId());        });    }}复制代码

运行结果插入数据

2019-08-23 22:32:52.755  INFO 77902 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'2019-08-23 22:32:53.210  INFO 77902 --- [           main] .MybatisPlusMultiTenancyApplicationTests : Started MybatisPlusMultiTenancyApplicationTests in 5.181 seconds (JVM running for 6.86)2019-08-23 22:32:53.613 DEBUG 77902 --- [           main] c.x.m.config.MyTenantHandler             : 当前租户为12019-08-23 22:32:53.614 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.insert           : ==>  Preparing: INSERT INTO user (name, tenant_id) VALUES (?, 1) 2019-08-23 22:32:53.648 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.insert           : ==> Parameters: 小明(String)2019-08-23 22:32:53.701 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.insert           : <==    Updates: 1 Time:64 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.insertExecute SQL:INSERT INTO user (name, tenant_id) VALUES ('小明', 1)2019-08-23 22:32:53.720 DEBUG 77902 --- [           main] c.x.m.config.MyTenantHandler             : 当前租户为12019-08-23 22:32:53.722 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.selectById       : ==>  Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 AND id = ? 2019-08-23 22:32:53.726 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.selectById       : ==> Parameters: 1(Long)2019-08-23 22:32:53.745 DEBUG 77902 --- [           main] c.x.m.mapper.UserMapper.selectById       : <==      Total: 1 Time:20 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.selectByIdExecute SQL:SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 AND id = 12019-08-23 22:32:53.746  INFO 77902 --- [           main] .MybatisPlusMultiTenancyApplicationTests : 插入数据:User(id=1, name=小明, tenantId=1)2019-08-23 22:32:53.762  INFO 77902 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'2019-08-23 22:32:53.764  INFO 77902 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...2019-08-23 22:32:53.777  INFO 77902 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.复制代码

查询数据

2019-08-23 22:34:26.700  INFO 77922 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'2019-08-23 22:34:27.100  INFO 77922 --- [           main] .MybatisPlusMultiTenancyApplicationTests : Started MybatisPlusMultiTenancyApplicationTests in 4.521 seconds (JVM running for 6.268)2019-08-23 22:34:27.412 DEBUG 77922 --- [           main] c.x.m.config.MyTenantHandler             : 当前租户为12019-08-23 22:34:27.414 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : ==>  Preparing: SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 1 2019-08-23 22:34:27.442 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : ==> Parameters: 2019-08-23 22:34:27.464 DEBUG 77922 --- [           main] c.x.m.mapper.UserMapper.selectList       : <==      Total: 1 Time:22 ms - ID:com.xd.mybatisplusmultitenancy.mapper.UserMapper.selectListExecute SQL:SELECT id, name, tenant_id FROM user WHERE user.tenant_id = 12019-08-23 22:34:27.467  INFO 77922 --- [           main] .MybatisPlusMultiTenancyApplicationTests : 查询数据User(id=1, name=小明, tenantId=1)2019-08-23 22:34:27.480  INFO 77922 --- [       Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'2019-08-23 22:34:27.482  INFO 77922 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...2019-08-23 22:34:27.492  INFO 77922 --- [       Thread-2] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.复制代码

从打印的日志不难看出,目前这个方案还是比较完美的,仅需简单的配置,让开发者极大方便的进行开发,同时又最大程度的保证了数据的安全性

源码下载: https://github.com/LiHaodong888/SpringBootLearn

具体项目:https://gitee.com/li_haodong/pre

16d8d6f9ed4d9ff9_tplv-t2oaga2asx-zoom-in-crop-mark_4536_0_0_0.jpg

参考资料

https://www.cnblogs.com/pingfan21/p/7478242.html  https://segmentfault.com/a/1190000017197768



相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
29天前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
110 6
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
104 1
|
1月前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
281 11
|
2月前
|
缓存 NoSQL Java
Springboot实战——黑马点评之秒杀优化
【9月更文挑战第27天】在黑马点评项目中,秒杀功能的优化对提升系统性能和用户体验至关重要。本文提出了多项Spring Boot项目的秒杀优化策略,包括数据库优化(如索引和分库分表)、缓存优化(如Redis缓存和缓存预热)、并发控制(如乐观锁、悲观锁和分布式锁)以及异步处理(如消息队列和异步任务执行)。这些策略能有效提高秒杀功能的性能和稳定性,为用户提供更佳体验。
159 6
|
2月前
|
存储 Java 数据库
Spring Boot 优雅实现多租户架构
本文详细介绍如何使用Spring Boot和Spring Cloud实现多租户架构。多租户架构允许多个租户共用一个应用,各自拥有独立资源和数据。其优势包括满足个性化需求、降低成本、复用代码以及增强可扩展性。文中探讨了架构选型、数据库设计、应用部署及租户管理等内容,并提供了具体实现步骤和技术细节。适用于SaaS应用和多租户云服务等场景。
|
1月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
100 0
|
3月前
|
Java API UED
【实战秘籍】Spring Boot开发者的福音:掌握网络防抖动,告别无效请求,提升用户体验!
【8月更文挑战第29天】网络防抖动技术能有效处理频繁触发的事件或请求,避免资源浪费,提升系统响应速度与用户体验。本文介绍如何在Spring Boot中实现防抖动,并提供代码示例。通过使用ScheduledExecutorService,可轻松实现延迟执行功能,确保仅在用户停止输入后才触发操作,大幅减少服务器负载。此外,还可利用`@Async`注解简化异步处理逻辑。防抖动是优化应用性能的关键策略,有助于打造高效稳定的软件系统。
74 2
|
3月前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
59 0
|
3月前
|
数据库 开发者 Java
颠覆传统开发:Hibernate与Spring Boot的集成,让你的开发效率飞跃式提升!
【8月更文挑战第31天】在 Java 开发中,Spring Boot 和 Hibernate 已成为许多开发者的首选技术栈。Spring Boot 简化了配置和部署过程,而 Hibernate 则是一个强大的 ORM 框架,用于管理数据库交互。将两者结合使用,可以极大提升开发效率并构建高性能的现代 Java 应用。本文将通过代码示例展示如何在 Spring Boot 项目中集成 Hibernate,并实现基本的数据库操作,包括添加依赖、配置数据源、创建实体类和仓库接口,以及在服务层和控制器中处理 HTTP 请求。这种组合不仅简化了配置,还提供了一套强大的工具来快速开发现代 Java 应用程序。
206 0

热门文章

最新文章