从 0 到 1:基于 Spring Boot 4 + Redis + MySQL 构建高可用电商后端系统

引言

对于中级 Java 开发者而言,搭建一套高可用、高性能的电商后端系统是提升技术实操能力的核心场景。本文将以项目驱动 的方式,从环境搭建到核心功能落地,完整演示基于 Spring Boot 4、Redis、MySQL 构建电商后端的全过程,所有代码均可直接复现,聚焦实战落地,避开纯理论堆砌。

  • 引言
  • 目录
    • 一、项目背景与架构概览
      • [1. 电商后端核心模块](#1. 电商后端核心模块)
      • [2. 技术栈选型原因](#2. 技术栈选型原因)
    • 二、系统架构流程图
    • 三、关键技术实现
      • 前置准备
      • [1. 配置文件(application.yml):Redis 与 MySQL 配置](#1. 配置文件(application.yml):Redis 与 MySQL 配置)
      • [2. 数据库表设计:商品表与订单表](#2. 数据库表设计:商品表与订单表)
        • [(1)商品实体(Product)→ 商品表(product)](#(1)商品实体(Product)→ 商品表(product))
        • [(2)订单实体(Order)→ 订单表(t_order,避免与 MySQL 关键字冲突)](#(2)订单实体(Order)→ 订单表(t_order,避免与 MySQL 关键字冲突))
      • [3. Spring Data JPA 数据访问层](#3. Spring Data JPA 数据访问层)
        • [(1)商品 Repository(ProductRepository)](#(1)商品 Repository(ProductRepository))
        • [(2)订单 Repository(OrderRepository)](#(2)订单 Repository(OrderRepository))
      • [4. 商品查询接口:集成 Redis 缓存(防穿透/雪崩)](#4. 商品查询接口:集成 Redis 缓存(防穿透/雪崩))
      • [5. 订单下单接口:Redis 分布式锁防止超卖](#5. 订单下单接口:Redis 分布式锁防止超卖)
    • 四、部署与验证建议
      • [1. 本地启动服务步骤](#1. 本地启动服务步骤)
      • [2. 接口验证(curl 或 Postman)](#2. 接口验证(curl 或 Postman))
        • [(1)商品查询接口验证(curl 命令)](#(1)商品查询接口验证(curl 命令))
        • [(2)订单下单接口验证(curl 命令)](#(2)订单下单接口验证(curl 命令))
      • [3. 性能优化方向](#3. 性能优化方向)
    • 五、总结

目录

一、项目背景与架构概览

1. 电商后端核心模块

典型电商后端的核心业务模块围绕用户购物流程展开,核心包括:

  • 用户模块:负责用户注册、登录、信息维护(本文聚焦核心流程,暂不深入实现);
  • 商品模块:商品信息查询、库存维护,是电商系统的基础,也是高并发场景的核心入口;
  • 购物车模块:临时存储用户待购商品,依赖缓存提升访问性能;
  • 订单模块:处理下单、订单状态流转,存在超卖等并发问题,是系统高可用的关键验证点。

本文将以**商品模块(查询+库存)订单模块(下单防超卖)**为核心,落地全套技术栈。

2. 技术栈选型原因

为何选择 Spring Boot 4 + Redis + MySQL 这套组合?

  • Spring Boot 4:基于 Spring 6.1 构建,兼容 Java 17+,提供了更轻量化的自动配置、更快的启动速度和更好的原生虚拟线程支持,大幅降低分布式应用的开发门槛,是当前 Java 后端微服务开发的首选框架;
  • Redis :高性能的内存数据库,支持多种数据结构,既可以作为热点数据缓存 提升查询接口吞吐量,也可以实现分布式锁解决分布式环境下的并发问题(如超卖),同时支持持久化,兼顾性能与数据安全性;
  • MySQL:成熟稳定的关系型数据库,支持事务和复杂查询,适合存储商品、订单等需要持久化、强一致性的核心业务数据,是电商系统持久化存储的标配。

三者组合实现了「高性能缓存 + 强一致性持久化 + 高效开发框架」的黄金搭配,能够满足电商系统高并发、高可用的核心需求。

二、系统架构流程图

以下使用 Mermaid 绘制系统架构图,清晰展示客户端、Spring Boot 应用、Redis、MySQL 之间的数据流向和交互关系:
存在
不存在
获取失败
获取成功
库存不足
库存充足
客户端(App/前端)
Spring Boot 4 应用
商品查询接口
订单下单接口
Redis 缓存是否存在?
从 Redis 读取商品数据
从 MySQL 读取商品数据
将商品数据写入 Redis 缓存(防穿透/雪崩)
通过 Redis 分布式锁获取下单权限
返回下单失败/请重试
校验 MySQL 商品库存
释放 Redis 分布式锁
扣减 MySQL 商品库存
创建 MySQL 订单记录
释放 Redis 分布式锁
返回下单成功
MySQL 数据库(商品表/订单表)
Redis 缓存(热点商品/分布式锁)

三、关键技术实现

前置准备

  1. 环境要求:Java 17+、Maven 3.8+、Redis 6.0+、MySQL 8.0+;
  2. 项目依赖(pom.xml 核心依赖,Spring Boot 4 版本以 4.0.0 为例):
xml 复制代码
<!-- Spring Boot 父工程 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.0</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Web 启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring Data JPA 启动器(操作 MySQL) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <!-- Redis 启动器(缓存+分布式锁) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- Lombok(简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 测试启动器 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1. 配置文件(application.yml):Redis 与 MySQL 配置

src/main/resources 下创建 application.yml,完成 Redis 和 MySQL 的配置,同时开启 Spring 缓存注解支持:

yaml 复制代码
# 服务器配置
server:
  port: 8080
  tomcat:
    threads:
      max: 200 # 调整 Tomcat 最大线程数,提升并发能力

# Spring 配置
spring:
  # 数据源(MySQL)配置
  datasource:
    url: jdbc:mysql://localhost:3306/ecommerce_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
    username: root # 你的 MySQL 用户名
    password: root # 你的 MySQL 密码
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 连接池配置(性能优化)
    hikari:
      maximum-pool-size: 20 # 最大连接数
      minimum-idle: 5 # 最小空闲连接数
      connection-timeout: 30000 # 连接超时时间(ms)
  # JPA 配置
  jpa:
    hibernate:
      ddl-auto: update # 自动创建/更新数据表(生产环境建议改为 none)
    show-sql: true # 打印 SQL 语句
    properties:
      hibernate:
        format_sql: true # 格式化 SQL 语句
        dialect: org.hibernate.dialect.MySQL8Dialect # MySQL 8 方言
  # Redis 配置
  data:
    redis:
      host: localhost # Redis 主机地址
      port: 6379 # Redis 端口
      password: "" # Redis 密码(无密码则留空)
      database: 0 # 操作的 Redis 数据库索引
      timeout: 3000 # 连接超时时间(ms)
      # Redis 连接池配置
      lettuce:
        pool:
          max-active: 16 # 最大连接数
          max-idle: 8 # 最大空闲连接数
          min-idle: 4 # 最小空闲连接数

# 缓存配置(自定义 Redis 缓存过期时间,防缓存雪崩)
spring:
  cache:
    redis:
      time-to-live: 3600000 # 缓存默认过期时间 1 小时(ms)
      cache-null-values: true # 缓存 null 值,防缓存穿透

# 日志配置
logging:
  level:
    org.springframework.web: INFO
    com.ecommerce: DEBUG # 自定义项目包日志级别

2. 数据库表设计:商品表与订单表

基于 JPA 注解自动生成数据表,核心实体类如下(对应 MySQL 表结构):

(1)商品实体(Product)→ 商品表(product)
java 复制代码
package com.ecommerce.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

/**
 * 商品实体类,对应 MySQL product 表
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "product")
public class Product {
    /**
     * 商品主键 ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    /**
     * 商品名称
     */
    @Column(name = "product_name", nullable = false, length = 100)
    private String productName;
    
    /**
     * 商品价格
     */
    @Column(name = "price", nullable = false, precision = 10, scale = 2)
    private BigDecimal price;
    
    /**
     * 商品库存
     */
    @Column(name = "stock", nullable = false)
    private Integer stock;
    
    /**
     * 商品描述
     */
    @Column(name = "description", length = 500)
    private String description;
}
(2)订单实体(Order)→ 订单表(t_order,避免与 MySQL 关键字冲突)
java 复制代码
package com.ecommerce.entity;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 订单实体类,对应 MySQL t_order 表
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "t_order")
public class Order {
    /**
     * 订单主键 ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    /**
     * 订单编号(唯一)
     */
    @Column(name = "order_no", nullable = false, unique = true, length = 32)
    private String orderNo;
    
    /**
     * 商品 ID
     */
    @Column(name = "product_id", nullable = false)
    private Long productId;
    
    /**
     * 购买数量
     */
    @Column(name = "buy_count", nullable = false)
    private Integer buyCount;
    
    /**
     * 订单总金额
     */
    @Column(name = "total_amount", nullable = false, precision = 10, scale = 2)
    private BigDecimal totalAmount;
    
    /**
     * 创建时间
     */
    @Column(name = "create_time", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date createTime;
}

3. Spring Data JPA 数据访问层

创建 Repository 接口,继承 JpaRepository,无需手动实现 CRUD 方法:

(1)商品 Repository(ProductRepository)
java 复制代码
package com.ecommerce.repository;

import com.ecommerce.entity.Product;
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.Optional;

/**
 * 商品数据访问层
 */
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    
    /**
     * 查询商品并加悲观锁(防止库存并发修改)
     * @param productId 商品 ID
     * @return 商品信息
     */
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT p FROM Product p WHERE p.id = :productId")
    Optional<Product> findByIdWithPessimisticLock(Long productId);
}
(2)订单 Repository(OrderRepository)
java 复制代码
package com.ecommerce.repository;

import com.ecommerce.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * 订单数据访问层
 */
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
}

4. 商品查询接口:集成 Redis 缓存(防穿透/雪崩)

使用 Spring 缓存注解 @Cacheable 快速实现 Redis 缓存,同时加入缓存穿透、雪崩的防护策略。

(1)商品服务(ProductService):带缓存注解的业务方法
java 复制代码
package com.ecommerce.service;

import com.ecommerce.entity.Product;
import com.ecommerce.repository.ProductRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * 商品服务层
 */
@Service
@RequiredArgsConstructor
public class ProductService {
    private final ProductRepository productRepository;
    
    /**
     * 根据商品 ID 查询商品信息(集成 Redis 缓存)
     * @param productId 商品 ID
     * @return 商品信息
     */
    @Cacheable(value = "productCache", key = "#productId", unless = "#result == null")
    // 注解说明:
    // value:缓存名称(对应 Redis 中的 key 前缀)
    // key:缓存键(使用商品 ID 作为唯一标识)
    // unless:结果为 null 时不缓存(配合 application.yml 中 cache-null-values: true,实现空值缓存防穿透)
    public Product getProductById(Long productId) {
        // 缓存不存在时,从 MySQL 查询
        Optional<Product> productOptional = productRepository.findById(productId);
        // 若商品不存在,返回 null(会被 Redis 缓存空值,防止缓存穿透)
        return productOptional.orElse(null);
    }
}
(2)商品控制层(ProductController):RESTful 接口
java 复制代码
package com.ecommerce.controller;

import com.ecommerce.entity.Product;
import com.ecommerce.service.ProductService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 商品 RESTful 接口控制层
 */
@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {
    private final ProductService productService;
    
    /**
     * 根据商品 ID 查询商品信息
     * @param productId 商品 ID
     * @return 商品信息响应
     */
    @GetMapping("/{productId}")
    public ResponseEntity<Product> getProductById(@PathVariable Long productId) {
        Product product = productService.getProductById(productId);
        if (product != null) {
            return ResponseEntity.ok(product);
        } else {
            return ResponseEntity.notFound().build();
        }
    }
}
(3)缓存防护思路说明
  • 缓存穿透 :通过 spring.cache.redis.cache-null-values: true 缓存 null 值,避免恶意请求(查询不存在的商品 ID)直接穿透到 MySQL,同时给空值缓存设置较短过期时间(可自定义扩展);
  • 缓存雪崩 :通过 spring.cache.redis.time-to-live: 3600000 设置缓存默认过期时间,同时可在 @Cacheable 注解中为不同商品设置随机过期时间(避免大量缓存同时失效),另外依赖 Redis 集群提高缓存服务可用性;
  • 缓存击穿:针对热点商品(如秒杀商品),可采用「互斥锁」或「永不过期+主动更新」策略,本文后续分布式锁可复用解决该问题。

5. 订单下单接口:Redis 分布式锁防止超卖

超卖问题是电商下单场景的典型并发问题,分布式环境下单台服务器的本地锁(synchronizedLock)无法生效,需通过 Redis 分布式锁实现跨服务的并发控制。

(1)Redis 分布式锁工具类(RedisDistributedLockUtil)

基于 RedisTemplate 实现,利用 Redis 的 setnx 命令(原子操作)实现锁的获取,同时加入过期时间防止死锁:

java 复制代码
package com.ecommerce.util;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Redis 分布式锁工具类
 */
@Component
@RequiredArgsConstructor
public class RedisDistributedLockUtil {
    private final RedisTemplate<String, Object> redisTemplate;
    
    // 锁的前缀(区分不同业务的锁)
    private static final String LOCK_PREFIX = "ecommerce:lock:";
    // 锁的默认过期时间(30s,防止死锁)
    private static final long DEFAULT_LOCK_EXPIRE_TIME = 30;
    // 锁的默认获取超时时间(5s)
    private static final long DEFAULT_ACQUIRE_TIMEOUT = 5;
    
    /**
     * 获取分布式锁
     * @param lockKey 锁的业务标识(如商品 ID)
     * @return 锁的唯一标识(用于释放锁),获取失败返回 null
     */
    public String acquireLock(String lockKey) {
        return acquireLock(lockKey, DEFAULT_ACQUIRE_TIMEOUT, DEFAULT_LOCK_EXPIRE_TIME, TimeUnit.SECONDS);
    }
    
    /**
     * 带参数获取分布式锁
     * @param lockKey 锁的业务标识
     * @param acquireTimeout 获取锁的超时时间
     * @param lockExpireTime 锁的过期时间
     * @param timeUnit 时间单位
     * @return 锁的唯一标识,获取失败返回 null
     */
    public String acquireLock(String lockKey, long acquireTimeout, long lockExpireTime, TimeUnit timeUnit) {
        // 生成唯一标识(防止误删其他线程的锁)
        String lockValue = UUID.randomUUID().toString();
        String realLockKey = LOCK_PREFIX + lockKey;
        long acquireEndTime = System.currentTimeMillis() + timeUnit.toMillis(acquireTimeout);
        
        // 循环尝试获取锁,直到超时
        while (System.currentTimeMillis() < acquireEndTime) {
            // Redis set 命令:NX(不存在才设置)、PX(过期时间单位 ms)
            Boolean success = redisTemplate.opsForValue().setIfAbsent(
                    realLockKey,
                    lockValue,
                    lockExpireTime,
                    TimeUnit.SECONDS
            );
            
            // 成功获取锁
            if (Boolean.TRUE.equals(success)) {
                return lockValue;
            }
            
            // 获取失败,短暂休眠后重试
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }
        
        // 超时未获取到锁
        return null;
    }
    
    /**
     * 释放分布式锁(使用 Lua 脚本保证原子操作)
     * @param lockKey 锁的业务标识
     * @param lockValue 锁的唯一标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String lockValue) {
        if (lockValue == null) {
            return false;
        }
        
        String realLockKey = LOCK_PREFIX + lockKey;
        // Lua 脚本:判断锁的 value 是否与传入的一致,一致则删除(避免误删)
        String luaScript = """
                if redis.call('get', KEYS[1]) == ARGV[1] then
                    return redis.call('del', KEYS[1])
                else
                    return 0
                end
                """;
        
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(luaScript);
        redisScript.setResultType(Long.class);
        
        // 执行 Lua 脚本
        Long result = redisTemplate.execute(
                redisScript,
                Collections.singletonList(realLockKey),
                lockValue
        );
        
        // 返回释放结果(1 表示成功,0 表示失败)
        return result != null && result > 0;
    }
}
(2)订单服务(OrderService):下单业务逻辑(含分布式锁)
java 复制代码
package com.ecommerce.service;

import com.ecommerce.entity.Order;
import com.ecommerce.entity.Product;
import com.ecommerce.repository.OrderRepository;
import com.ecommerce.repository.ProductRepository;
import com.ecommerce.util.RedisDistributedLockUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.UUID;

/**
 * 订单服务层
 */
@Service
@RequiredArgsConstructor
public class OrderService {
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    private final RedisDistributedLockUtil redisDistributedLockUtil;
    
    /**
     * 下单接口(防止超卖)
     * @param productId 商品 ID
     * @param buyCount 购买数量
     * @return 下单结果
     */
    @Transactional(rollbackFor = Exception.class) // 事务注解,保证库存扣减和订单创建的原子性
    public String createOrder(Long productId, Integer buyCount) {
        // 1. 参数校验
        if (productId == null || buyCount == null || buyCount <= 0) {
            return "参数错误,请输入有效的商品 ID 和购买数量";
        }
        
        // 2. 构建分布式锁的 key(以商品 ID 为标识,同一商品同一时间只允许一个线程下单)
        String lockKey = "product:order:" + productId;
        String lockValue = null;
        
        try {
            // 3. 获取分布式锁
            lockValue = redisDistributedLockUtil.acquireLock(lockKey);
            if (lockValue == null) {
                return "下单过于火爆,请稍后再试";
            }
            
            // 4. 查询商品(加悲观锁,防止库存并发修改)
            Product product = productRepository.findByIdWithPessimisticLock(productId)
                    .orElseThrow(() -> new RuntimeException("商品不存在"));
            
            // 5. 校验库存
            if (product.getStock() < buyCount) {
                return "商品库存不足,当前库存:" + product.getStock();
            }
            
            // 6. 扣减商品库存
            product.setStock(product.getStock() - buyCount);
            productRepository.save(product);
            
            // 7. 创建订单
            Order order = new Order();
            order.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
            order.setProductId(productId);
            order.setBuyCount(buyCount);
            order.setTotalAmount(product.getPrice().multiply(new BigDecimal(buyCount)));
            order.setCreateTime(new Date());
            orderRepository.save(order);
            
            // 8. 返回下单成功结果
            return "下单成功,订单编号:" + order.getOrderNo();
        } catch (Exception e) {
            return "下单失败:" + e.getMessage();
        } finally {
            // 9. 释放分布式锁(无论下单成功与否,都要释放锁)
            if (lockValue != null) {
                redisDistributedLockUtil.releaseLock(lockKey, lockValue);
            }
        }
    }
}
(3)订单控制层(OrderController):RESTful 下单接口
java 复制代码
package com.ecommerce.controller;

import com.ecommerce.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 订单 RESTful 接口控制层
 */
@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {
    private final OrderService orderService;
    
    /**
     * 创建订单(下单接口)
     * @param productId 商品 ID
     * @param buyCount 购买数量
     * @return 下单结果响应
     */
    @PostMapping("/create")
    public ResponseEntity<String> createOrder(
            @RequestParam Long productId,
            @RequestParam Integer buyCount
    ) {
        String result = orderService.createOrder(productId, buyCount);
        return ResponseEntity.ok(result);
    }
}

四、部署与验证建议

1. 本地启动服务步骤

  1. 启动 Redis:确保 Redis 服务正常运行(可通过 redis-cli ping 验证,返回 PONG 即为正常);
  2. 启动 MySQL:创建数据库 ecommerce_db(与 application.yml 中配置一致),无需手动创建数据表,JPA 会自动生成;
  3. 启动 Spring Boot 应用:运行项目的主启动类(添加 @SpringBootApplication@EnableCaching 注解):
java 复制代码
package com.ecommerce;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

/**
 * 项目主启动类
 */
@SpringBootApplication
@EnableCaching // 开启 Spring 缓存注解支持
public class EcommerceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EcommerceApplication.class, args);
    }
}
  1. 验证服务启动:访问 http://localhost:8080/api/products/1(若未添加商品,返回 404;可先通过 MySQL 手动插入商品数据)。

2. 接口验证(curl 或 Postman)

(1)商品查询接口验证(curl 命令)
bash 复制代码
# 查询商品 ID 为 1 的商品信息
curl -X GET http://localhost:8080/api/products/1
  • 第一次请求:Redis 缓存无数据,从 MySQL 读取并写入 Redis,控制台打印 SQL 语句;
  • 第二次请求:Redis 缓存有数据,直接从 Redis 读取,控制台不打印 SQL 语句,响应速度更快。
(2)订单下单接口验证(curl 命令)
bash 复制代码
# 购买商品 ID 为 1 的商品,购买数量为 2
curl -X POST "http://localhost:8080/api/orders/create?productId=1&buyCount=2"
  • 正常情况:返回「下单成功,订单编号:xxx」,MySQL 中商品库存扣减 2,同时创建订单记录;
  • 库存不足:返回「商品库存不足,当前库存:xxx」;
  • 并发下单:可通过 JMeter 模拟多线程并发请求,验证无超卖问题(库存不会出现负数)。

3. 性能优化方向

  1. 连接池优化
    • MySQL 连接池(HikariCP):根据服务器配置调整 maximum-pool-size(建议不超过 CPU 核心数 * 2 + 1),避免连接过多导致数据库压力过大;
    • Redis 连接池(Lettuce):调整 max-activemax-idle 参数,避免频繁创建/销毁连接,提升 Redis 操作效率。
  2. 缓存策略调优
    • 分层缓存:热点商品采用「本地缓存(Caffeine)+ Redis 缓存」双层缓存,进一步降低 Redis 压力;
    • 缓存预热:系统启动时主动将热点商品数据写入 Redis 缓存,避免高峰期缓存穿透;
    • 过期时间优化:不同商品设置不同过期时间(如秒杀商品过期时间较短,普通商品过期时间较长),避免缓存雪崩。
  3. 分布式锁优化
    • 采用 Redisson 替代手动实现的 Redis 分布式锁,Redisson 支持锁的自动续期(解决长业务执行时锁过期问题)、公平锁等高级特性;
    • 锁粒度细化:将商品级别的锁细化为库存区域级别的锁,提升并发下单能力。
  4. 数据库优化
    • 索引优化:为商品表的 id、订单表的 product_idorder_no 建立索引,提升查询效率;
    • 分库分表:针对海量订单数据,采用分库分表(如 Sharding-JDBC),提升数据库存储和查询能力;
    • 读写分离:采用 MySQL 主从复制,读操作走从库,写操作走主库,缓解主库压力。

五、总结

本文以项目驱动的方式,完整实现了基于 Spring Boot 4 + Redis + MySQL 的电商后端核心功能,涵盖了环境搭建、配置文件编写、数据库设计、缓存集成、分布式锁防超卖等关键技术点。通过这套方案,能够满足电商系统高并发、高可用的核心需求,同时所有代码均可直接复现,便于中级 Java 开发者落地实践。

后续可在此基础上扩展用户模块、支付模块、物流模块等,逐步构建完整的电商后端系统,同时深入研究微服务、消息队列(如 RocketMQ、RabbitMQ)等技术,进一步提升系统的可扩展性和容错能力。


✨ 坚持用 清晰易懂的图解 + 代码语言, 让每个知识点都 简单直观 !

🚀 个人主页不呆头 · CSDN

🌱 代码仓库不呆头 · Gitee

📌 专栏系列

💬 座右铭 : "不患无位,患所以立。"

相关推荐
汤姆yu3 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶3 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
·云扬·4 小时前
MySQL 8.0 Redo Log 归档与禁用实战指南
android·数据库·mysql
biyezuopinvip4 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
惊讶的猫5 小时前
redis分片集群
数据库·redis·缓存·分片集群·海量数据存储·高并发写
JavaGuide5 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
期待のcode5 小时前
Redis的主从复制与集群
运维·服务器·redis
jiunian_cn5 小时前
【Redis】渐进式遍历
数据库·redis·缓存
figo10tf5 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva5 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端