SpringBoot 集成 Redis 实战(缓存与分布式锁):提升系统性能与并发能力

在高并发、高流量后端系统中,数据库往往是性能瓶颈 ------ 频繁的数据库查询会导致响应延迟、数据库压力过大。Redis 作为高性能键值对缓存数据库,支持内存存储、持久化、分布式部署,可通过缓存热点数据、实现分布式锁等方式,大幅提升系统响应速度、降低数据库压力,同时解决分布式环境下的并发安全问题,是企业级系统性能优化的核心工具。

本文聚焦 SpringBoot 与 Redis 的实战落地,从环境搭建、缓存注解使用、手动缓存操作,到分布式锁实现、缓存问题解决方案,全程嵌入 Java 代码教学,帮你快速掌握 Redis 核心应用,打造高性能、高并发的后端系统。

一、核心认知:Redis 核心价值与适用场景

1. 核心优势

  • 极致性能:基于内存操作,读写速度可达每秒百万级,响应延迟毫秒级,远超传统数据库;
  • 数据结构丰富:支持 String、Hash、List、Set、Sorted Set 等多种数据结构,适配复杂业务场景;
  • 分布式兼容:支持主从复制、哨兵模式、集群部署,适配分布式系统架构;
  • 功能强大:可实现缓存、分布式锁、消息队列、计数器、限流等多种功能;
  • 持久化机制:支持 RDB、AOF 两种持久化方式,确保数据不丢失(兼顾性能与可靠性)。

2. 核心适用场景

  • 热点数据缓存:用户信息、商品详情、首页数据等高频查询数据,减轻数据库压力;
  • 分布式锁:解决分布式系统中并发修改、资源竞争问题(如库存扣减、订单创建);
  • 会话存储:分布式环境下的用户会话共享(替代 Session 本地存储);
  • 异步通信:基于 List 实现简单消息队列,处理异步任务(如日志收集、通知推送);
  • 计数器与限流:接口访问次数统计、接口限流(防止恶意请求)。

3. Redis 核心概念

  • 键(Key):唯一标识,支持字符串、哈希值等格式,建议设计统一命名规范(如 user:info:1001);
  • 值(Value):支持多种数据结构,存储业务数据,需合理设置过期时间避免内存溢出;
  • 过期策略:支持定期删除、惰性删除,可设置键的过期时间,自动释放内存;
  • 分布式锁:基于 SETNX(SET if Not Exists)命令,确保同一时间只有一个线程操作资源。

二、核心实战一:环境搭建(Docker 快速部署)

1. Docker 部署 Redis(单节点,开发测试场景)

bash

运行

复制代码
# 1. 拉取 Redis 镜像(最新稳定版)
docker pull redis:latest

# 2. 启动 Redis 容器(配置密码、持久化、端口映射)
docker run -d --name redis -p 6379:6379 \
  -v redis-data:/data \ # 挂载数据卷,持久化数据
  -e REDIS_PASSWORD=redis123 \ # 设置访问密码
  redis:latest \
  redis-server --requirepass redis123 \ # 开启密码验证
  --appendonly yes # 开启 AOF 持久化(数据更可靠)
  • 连接测试:使用 Redis 客户端(如 Redis Desktop Manager)连接 localhost:6379,密码 redis123,验证服务可用性;
  • 核心配置说明:AOF 持久化会记录所有写操作,重启后可恢复数据,适合对数据可靠性要求高的场景。

三、核心实战二:SpringBoot 集成 Redis 基础配置

1. 引入依赖(Maven)

xml

复制代码
<!-- Spring Data Redis 依赖(简化 Redis 操作) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 连接池依赖(提升 Redis 连接效率) -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!-- Web 依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

2. 配置文件(application.yml)

yaml

复制代码
# Redis 配置
spring:
  redis:
    host: localhost # Redis 服务地址
    port: 6379 # 端口
    password: redis123 # 访问密码
    database: 0 # 数据库索引(默认 0,Redis 支持 16 个数据库)
    timeout: 10000 # 连接超时时间(毫秒)
    lettuce: # 连接池配置(Lettuce 是 SpringBoot 2.x 默认 Redis 客户端)
      pool:
        max-active: 16 # 最大连接数
        max-idle: 8 # 最大空闲连接数
        min-idle: 4 # 最小空闲连接数
        max-wait: -1 # 最大等待时间(-1 表示无限制)

# 服务端口
server:
  port: 8085

3. Redis 配置类(自定义序列化方式)

默认序列化方式会导致 Redis 中存储的数据可读性差,需自定义序列化(JSON 格式),同时配置缓存管理器。

java

运行

复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;

@Configuration
public class RedisConfig {
    // 自定义 RedisTemplate(JSON 序列化,支持复杂对象存储)
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 字符串序列化器(Key 用字符串序列化)
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // JSON 序列化器(Value 用 JSON 序列化,保留对象结构)
        GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer();

        // 配置 Key、Value 序列化方式
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(jsonSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(jsonSerializer);

        // 初始化 RedisTemplate
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    // 配置缓存管理器(支持缓存注解)
    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 缓存配置(设置默认过期时间 30 分钟,JSON 序列化)
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30)) // 默认缓存过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .disableCachingNullValues(); // 禁止缓存 null 值(避免缓存穿透)

        // 创建缓存管理器
        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(cacheConfig)
                .build();
    }
}

四、核心实战三:Redis 缓存操作(注解式 + 手动式)

1. 注解式缓存(简化开发,适用于简单场景)

通过 @Cacheable@CachePut@CacheEvict 等注解,无需手动编写缓存逻辑,自动实现数据缓存与更新。

(1)Service 层使用示例

java

运行

复制代码
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.example.redis.entity.User;
import com.example.redis.mapper.UserMapper;
import javax.annotation.Resource;

@Service
public class UserService {
    @Resource
    private UserMapper userMapper;

    // ✅ 缓存查询结果:key 为 "user:info:" + id,缓存过期时间 30 分钟
    @Cacheable(value = "user", key = "'info:' + #id", unless = "#result == null")
    public User getUserById(Long id) {
        // 缓存不存在时,查询数据库(仅执行一次)
        System.out.println("查询数据库,用户ID:" + id);
        return userMapper.selectById(id);
    }

    // ✅ 更新缓存:更新数据库后,同步更新缓存(避免缓存与数据库不一致)
    @CachePut(value = "user", key = "'info:' + #user.id", unless = "#user == null")
    public User updateUser(User user) {
        userMapper.updateById(user);
        return user;
    }

    // ✅ 清除缓存:删除数据库数据后,删除对应缓存
    @CacheEvict(value = "user", key = "'info:' + #id")
    public void deleteUser(Long id) {
        userMapper.deleteById(id);
    }

    // ✅ 清除所有缓存(如批量更新时)
    @CacheEvict(value = "user", allEntries = true)
    public void clearUserCache() {
        // 仅清除缓存,无业务逻辑
    }
}
(2)Controller 层接口

java

运行

复制代码
import org.springframework.web.bind.annotation.*;
import com.example.redis.entity.User;
import com.example.redis.result.Result;
import com.example.redis.service.UserService;
import javax.annotation.Resource;

@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    private UserService userService;

    @GetMapping("/{id}")
    public Result<User> getUserById(@PathVariable Long id) {
        User user = userService.getUserById(id);
        return Result.success(user);
    }

    @PutMapping
    public Result<User> updateUser(@RequestBody User user) {
        User updatedUser = userService.updateUser(user);
        return Result.success(updatedUser);
    }

    @DeleteMapping("/{id}")
    public Result<Void> deleteUser(@PathVariable Long id) {
        userService.deleteUser(id);
        return Result.success();
    }
}

2. 手动式缓存(灵活控制,适用于复杂场景)

通过 RedisTemplate 手动操作 Redis,支持多种数据结构,适配复杂业务场景(如 Hash、List 操作)。

(1)Redis 工具类封装

java

运行

复制代码
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // ✅ 存储 String 类型数据(设置过期时间)
    public void setString(String key, Object value, Long expireTime, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
    }

    // ✅ 获取 String 类型数据
    public Object getString(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    // ✅ 删除数据
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    // ✅ 判断键是否存在
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    // ✅ 设置键过期时间
    public Boolean expire(String key, Long expireTime, TimeUnit timeUnit) {
        return redisTemplate.expire(key, expireTime, timeUnit);
    }

    // ✅ 存储 Hash 类型数据(示例:存储用户详情,字段拆分)
    public void setHash(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    // ✅ 获取 Hash 类型数据
    public Object getHash(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
}
(2)工具类使用示例(热点商品缓存)

java

运行

复制代码
import org.springframework.stereotype.Service;
import com.example.redis.entity.Goods;
import com.example.redis.mapper.GoodsMapper;
import com.example.redis.utils.RedisUtils;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Service
public class GoodsService {
    @Resource
    private GoodsMapper goodsMapper;
    @Resource
    private RedisUtils redisUtils;

    // 热点商品缓存(手动控制缓存逻辑)
    public Goods getHotGoodsById(Long id) {
        String key = "goods:hot:" + id;
        // 1. 先查缓存
        Goods goods = (Goods) redisUtils.getString(key);
        if (goods != null) {
            return goods;
        }
        // 2. 缓存不存在,查数据库
        goods = goodsMapper.selectById(id);
        if (goods != null) {
            // 3. 存入缓存,设置过期时间 10 分钟(热点数据可缩短过期时间)
            redisUtils.setString(key, goods, 10L, TimeUnit.MINUTES);
        }
        return goods;
    }
}

五、核心实战四:分布式锁实现(解决并发安全问题)

分布式环境下,多个服务实例同时操作同一资源(如库存扣减)会导致数据不一致,通过 Redis 分布式锁可确保同一时间只有一个线程操作资源。

1. 分布式锁工具类

java

运行

复制代码
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public class RedisDistributedLock {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 锁前缀(避免键冲突)
    private static final String LOCK_PREFIX = "lock:";
    // 锁过期时间(避免死锁,默认 30 秒)
    private static final Long LOCK_EXPIRE = 30L;

    /**
     * 获取分布式锁
     * @param lockKey 锁标识(如 "stock:1001")
     * @param requestId 唯一标识(如 UUID,确保释放锁时是自己的锁)
     * @return 是否获取成功
     */
    public Boolean tryLock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        // SETNX 命令:当键不存在时设置值,原子操作(确保并发安全)
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(key, requestId, LOCK_EXPIRE, TimeUnit.SECONDS);
        return locked != null && locked;
    }

    /**
     * 释放分布式锁
     * @param lockKey 锁标识
     * @param requestId 唯一标识
     */
    public void unlock(String lockKey, String requestId) {
        String key = LOCK_PREFIX + lockKey;
        try {
            String value = (String) redisTemplate.opsForValue().get(key);
            // 仅释放自己的锁(避免释放其他线程的锁)
            if (requestId.equals(value)) {
                redisTemplate.delete(key);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2. 分布式锁使用示例(库存扣减)

java

运行

复制代码
import org.springframework.stereotype.Service;
import com.example.redis.mapper.StockMapper;
import com.example.redis.utils.RedisDistributedLock;
import javax.annotation.Resource;
import java.util.UUID;

@Service
public class StockService {
    @Resource
    private StockMapper stockMapper;
    @Resource
    private RedisDistributedLock distributedLock;

    // 库存扣减(分布式环境下安全操作)
    public Boolean deductStock(Long goodsId, Integer num) {
        String lockKey = "stock:" + goodsId;
        String requestId = UUID.randomUUID().toString();
        try {
            // 1. 获取分布式锁(最多等待 5 秒,每隔 1 秒重试)
            Boolean locked = false;
            int count = 0;
            while (!locked && count < 5) {
                locked = distributedLock.tryLock(lockKey, requestId);
                if (!locked) {
                    Thread.sleep(1000);
                    count++;
                }
            }
            if (!locked) {
                // 获取锁失败(限流)
                return false;
            }
            // 2. 扣减库存(业务逻辑)
            int row = stockMapper.deductStock(goodsId, num);
            return row > 0;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            // 3. 释放锁(确保无论是否异常,都释放锁)
            distributedLock.unlock(lockKey, requestId);
        }
    }
}

六、进阶:缓存常见问题解决方案

1. 缓存穿透(查询不存在的数据,缓存无效,直击数据库)

  • 问题表现:恶意请求不存在的 ID,缓存无法命中,频繁查询数据库,导致数据库压力过大;
  • 解决方案:缓存 null 值(设置较短过期时间),或使用布隆过滤器过滤不存在的 ID。

java

运行

复制代码
// 缓存 null 值解决方案(修改查询方法)
public Goods getGoodsById(Long id) {
    String key = "goods:info:" + id;
    Goods goods = (Goods) redisUtils.getString(key);
    if (goods != null) {
        return goods;
    }
    // 数据库查询也不存在,缓存 null 值(过期时间 5 分钟)
    goods = goodsMapper.selectById(id);
    if (goods == null) {
        redisUtils.setString(key, null, 5L, TimeUnit.MINUTES);
        return null;
    }
    redisUtils.setString(key, goods, 30L, TimeUnit.MINUTES);
    return goods;
}

2. 缓存击穿(热点数据缓存过期,大量请求同时直击数据库)

  • 问题表现:热点数据缓存过期瞬间,大量并发请求同时查询数据库,导致数据库压力骤增;
  • 解决方案:互斥锁(同一时间只有一个线程查询数据库并更新缓存),或热点数据永不过期。

3. 缓存雪崩(大量缓存同时过期,或 Redis 服务宕机,所有请求直击数据库)

  • 问题表现:缓存服务器宕机,或大量缓存设置同一过期时间,导致所有请求直接访问数据库,数据库崩溃;
  • 解决方案:Redis 集群部署(高可用),缓存过期时间加随机值(避免同时过期),服务降级(熔断保护数据库)。

七、避坑指南

坑点 1:缓存与数据库数据不一致

表现:更新数据库后,缓存未同步更新,导致查询到旧数据;✅ 解决方案:采用「更新数据库 + 同步更新缓存」或「更新数据库 + 删除缓存」策略,避免单独操作数据库或缓存。

坑点 2:Redis 序列化后数据可读性差

表现:Redis 中存储的数据为乱码,无法直接查看;✅ 解决方案:自定义序列化方式(如 JSON 序列化),避免使用默认的 JdkSerializationRedisSerializer。

坑点 3:分布式锁死锁

表现:获取锁后线程异常退出,未释放锁,导致其他线程无法获取锁;✅ 解决方案:设置锁过期时间,确保锁自动释放;使用 try-finally 确保锁一定会被释放。

坑点 4:Redis 连接池耗尽

表现:系统报错「Could not get a resource from the pool」,无法获取 Redis 连接;✅ 解决方案:合理配置连接池参数(max-active、max-idle),避免频繁创建销毁连接,排查是否有连接未释放的情况。

八、终极总结:Redis 实战的核心是「缓存高效 + 并发安全」

Redis 实战的核心价值,是通过缓存热点数据提升系统响应速度,通过分布式锁解决分布式并发问题,同时兼顾数据可靠性与系统稳定性。企业级开发中,需根据业务场景选择合适的缓存策略与数据结构,规避常见缓存问题,平衡性能与一致性。

核心原则总结:

  1. 缓存策略适配业务:简单场景用注解式缓存,复杂场景用手动式缓存,热点数据缩短过期时间;
  2. 并发安全优先:分布式环境下操作共享资源,必须使用分布式锁,避免数据不一致;
  3. 高可用不可少:生产环境必用 Redis 集群(主从 + 哨兵),避免单点故障导致缓存雪崩;
  4. 问题提前预防:针对缓存穿透、击穿、雪崩,提前部署解决方案,而非事后补救。
相关推荐
信创天地2 小时前
国产化消息中间件双雄:东方通TongLINK/Q与华为RabbitMQ的运维核心技术全解析
运维·华为·rabbitmq
韩立学长2 小时前
基于Springboot琴行学生课程信息管理系统2gt392wb(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
Pyeako2 小时前
opencv计算机视觉--传参方法&银行卡识别&身份证识别案例
人工智能·python·opencv·计算机视觉·身份证识别·银行卡识别·传参方法
夔曦2 小时前
【无标题】
python·强化学习算法
XXOOXRT2 小时前
基于SpringBoot的用户登录
java·spring boot·后端
齐 飞2 小时前
springboot整合shardingsphere-jdbc5.1.1-按月分表
数据库·spring boot
先做个垃圾出来………2 小时前
Python 中 kwargs.get() 方法详解
开发语言·python
大闲在人2 小时前
Trae builder 实战: 让 C++ 函数像 Python 一样返回多个值
c++·python·ai编程
柠檬丶抒情2 小时前
Rust深度学习框架Burn 0.20是否能超过python?
python·深度学习·rust·vllm