从 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

📌 专栏系列

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

相关推荐
小蜗牛的路2 小时前
MySQL-连接很慢,10秒钟才有响应、Temporary failure in name resolution
数据库·mysql
小马爱打代码2 小时前
Redis 集群选型:主从 / 哨兵 / Cluster 怎么选
redis
drebander2 小时前
Cursor IDE 中 Spring Boot 项目启动内存不足问题解决方案
ide·spring boot·cursor
蜀中孤鹰2 小时前
从秒级到毫秒级:一次Redis限流脚本的深度优化实战
redis·spring cloud·lua
weixin_439706252 小时前
spring boot+nacos+gateway+sentinel的简单例子
spring boot·gateway·sentinel
程序猿_极客2 小时前
【node期末作业开发】Node.js+MySQL 实现销售信息管理系统的增删改查(附源码)
数据库·mysql·node.js
IT届小白3 小时前
探讨:20 万数据量下ROW_NUMBER和GROUP BY两条 SQL 性能差异分析(查 10 条 / 查所有)
数据库·mysql
小小星球之旅3 小时前
SpringBoot实现WebSocket实现用户一对一和一对多信息的发送
spring boot·后端·websocket
wusp19943 小时前
Django 迁移系统全指南:从模型到数据库的魔法之路
数据库·mysql·django