Redis 作为最主流的分布式缓存几乎是 SpringBoot 项目的“标配”——无论是减轻数据库压力、提升接口响应速度还是实现会话共享、分布式锁都离不开它。本篇文章就来介绍一下 SpringBoot 整合 Redis的操作步骤 同时讲讲Redis中String、Hash、List、Set、ZSet 五种核心数据类型的增删改查还有缓存失效、序列化配置、注意事项等。一、为什么用 Redis 做缓存在项目中引入 Redis核心解决两个核心问题•减轻数据库压力把高频查询数据如用户信息、商品列表缓存到 Redis避免每次请求都查数据库接口响应速度从毫秒级提升到微秒级•支持多种场景除了缓存还能实现分布式锁、计数器、限流、消息队列简单场景、排行榜等功能•高性能、高可用Redis 基于内存操作读写速度极快支持主从复制、哨兵模式稳定性拉满。二、第一步引入依赖SpringBoot 提供了专门的 Redis 启动器无需手动配置复杂依赖直接引入即可!-- SpringBoot Redis 启动器 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- 连接池 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency说明spring-boot-starter-data-redis底层已经集成了 Redis 客户端默认 Lettuce替代了早期的 Jedis配合commons-pool2连接池性能更优。三、第二步application.yml 核心配置配置 Redis 连接信息、连接池、序列化方式spring: redis: # Redis 服务器地址本地/线上地址 host: localhost # 端口默认6379 port: 6379 # 密码如果没设置密码注释掉即可 password: 123456 # 数据库索引Redis 默认有16个库0-15可按需切换 database: 0 # 超时时间连接、读取、写入超时单位毫秒 timeout: 5000 # 连接池配置Lettuce 连接池 lettuce: pool: # 最大连接数核心根据业务压测调整默认8 max-active: 100 # 最大空闲连接 max-idle: 20 # 最小空闲连接 min-idle: 5 # 连接等待超时时间毫秒 max-wait: 1000 # 自定义 Redis 序列化配置避免缓存乱码可选但推荐 redis: serialization: key-prefix: springboot:redis: # 缓存key前缀避免不同项目key冲突 expire-default: 3600 # 默认缓存过期时间秒1小时四、第三步Redis 序列化配置SpringBoot 默认的 Redis 序列化方式会导致缓存的 key、value 乱码不利于调试我们自定义配置使用 JSON 序列化兼顾可读性和性能package com.demo.config; import org.springframework.boot.context.properties.ConfigurationProperties; 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; import java.util.HashMap; import java.util.Map; Configuration ConfigurationProperties(prefix redis.serialization) public class RedisConfig { // 缓存key前缀 private String keyPrefix; // 默认缓存过期时间秒 private Long expireDefault; // 1. 配置 RedisTemplate操作Redis的核心工具类 Bean public RedisTemplateString, Object redisTemplate(RedisConnectionFactory factory) { RedisTemplateString, Object redisTemplate new RedisTemplate(); redisTemplate.setConnectionFactory(factory); // 配置key序列化String类型避免乱码 StringRedisSerializer stringRedisSerializer new StringRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); // 配置value序列化JSON类型可读性强支持对象 GenericJackson2JsonRedisSerializer jsonSerializer new GenericJackson2JsonRedisSerializer(); redisTemplate.setValueSerializer(jsonSerializer); redisTemplate.setHashValueSerializer(jsonSerializer); // 初始化参数 redisTemplate.afterPropertiesSet(); return redisTemplate; } // 2. 配置 RedisCacheManager配合Cacheable注解使用缓存管理 Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { // 基础配置序列化、过期时间 RedisCacheConfiguration config RedisCacheConfiguration.defaultCacheConfig() // 过期时间 .entryTtl(Duration.ofSeconds(expireDefault)) // key序列化 .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // value序列化 .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) // 允许缓存null值可选根据业务调整 .disableCachingNullValues(); // 自定义不同缓存的过期时间可选 MapString, RedisCacheConfiguration cacheConfigs new HashMap(); // 比如用户缓存过期时间2小时商品缓存过期时间1小时 cacheConfigs.put(userCache, config.entryTtl(Duration.ofSeconds(7200))); cacheConfigs.put(productCache, config.entryTtl(Duration.ofSeconds(3600))); // 构建缓存管理器 return RedisCacheManager.builder(factory) .cacheDefaults(config) .withInitialCacheConfigurations(cacheConfigs) .build(); } // getter/setterLombok可省略 public String getKeyPrefix() { return keyPrefix; } public void setKeyPrefix(String keyPrefix) { this.keyPrefix keyPrefix; } public Long getExpireDefault() { return expireDefault; } public void setExpireDefault(Long expireDefault) { this.expireDefault expireDefault; } }五、Redis 五种数据类型操作我们通过RedisTemplate操作 Redis下面分别实战String、Hash、List、Set、ZSet五种核心数据类型每种类型都包含「增、删、改、查」完整用法可直接复制到项目中使用。1. String 类型最常用适合单个值、字符串、对象缓存适用场景用户信息、验证码、令牌、单个数值如计数器是最基础、最常用的类型。package com.demo.service; import com.demo.entity.User; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; Service RequiredArgsConstructor public class RedisStringService { private final RedisTemplateString, Object redisTemplate; // 从配置文件读取key前缀 private final RedisConfig redisConfig; // 拼接key避免重复规范写法 private String getKey(String key) { return redisConfig.getKeyPrefix() key; } // 1. 新增String缓存无过期时间 public void set(String key, Object value) { redisTemplate.opsForValue().set(getKey(key), value); } // 2. 新增String缓存带过期时间 public void setWithExpire(String key, Object value, Long expireTime, TimeUnit timeUnit) { redisTemplate.opsForValue().set(getKey(key), value, expireTime, timeUnit); } // 3. 获取String缓存 public Object get(String key) { return redisTemplate.opsForValue().get(getKey(key)); } // 4. 修改String缓存直接覆盖 public void update(String key, Object value) { this.set(key, value); } // 5. 删除String缓存 public Boolean delete(String key) { return redisTemplate.delete(getKey(key)); } // 6. 自增计数器场景如文章阅读量、点赞数 public Long increment(String key, Long delta) { return redisTemplate.opsForValue().increment(getKey(key), delta); } // 7. 自减 public Long decrement(String key, Long delta) { return redisTemplate.opsForValue().decrement(getKey(key), delta); } // 实战示例缓存用户信息 public void cacheUser(User user) { // 缓存keyspringboot:redis:user:11是用户ID String key user: user.getId(); // 过期时间2小时7200秒 this.setWithExpire(key, user, 7200L, TimeUnit.SECONDS); } // 实战示例获取缓存的用户信息 public User getCachedUser(Long userId) { String key user: userId; return (User) this.get(key); } }2. Hash 类型适合存储对象可单独操作对象字段适用场景用户信息、商品信息等对象缓存无需修改整个对象可单独修改某个字段如用户昵称、商品库存节省内存和带宽。package com.demo.service; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; Service RequiredArgsConstructor public class RedisHashService { private final RedisTemplateString, Object redisTemplate; private final RedisConfig redisConfig; private String getKey(String key) { return redisConfig.getKeyPrefix() key; } // 获取Hash操作对象 private HashOperationsString, String, Object getHashOps() { return redisTemplate.opsForHash(); } // 1. 新增Hash缓存存储整个对象key是对象标识hashKey是字段名 public void putAll(String key, MapString, Object hashMap) { String redisKey getKey(key); getHashOps().putAll(redisKey, hashMap); // 设置过期时间 redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); } // 2. 新增单个Hash字段 public void put(String key, String hashKey, Object value) { String redisKey getKey(key); getHashOps().put(redisKey, hashKey, value); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); } // 3. 获取单个Hash字段值 public Object get(String key, String hashKey) { return getHashOps().get(getKey(key), hashKey); } // 4. 获取整个Hash对象所有字段和值 public MapString, Object getAll(String key) { return getHashOps().entries(getKey(key)); } // 5. 获取所有Hash字段名 public SetString getHashKeys(String key) { return getHashOps().keys(getKey(key)); } // 6. 修改单个Hash字段 public void update(String key, String hashKey, Object value) { this.put(key, hashKey, value); } // 7. 删除单个Hash字段 public Long delete(String key, String... hashKeys) { return getHashOps().delete(getKey(key), (Object[]) hashKeys); } // 实战示例缓存用户信息Hash类型 public void cacheUserHash(Long userId, MapString, Object userMap) { // 缓存keyspringboot:redis:user:hash:1 String key user:hash: userId; this.putAll(key, userMap); } // 实战示例修改用户昵称单独修改Hash字段 public void updateUserName(Long userId, String newName) { String key user:hash: userId; this.put(key, username, newName); } }3. List 类型有序、可重复适合队列、列表场景适用场景消息队列简单场景、最新列表、历史记录如浏览记录、消息列表支持从头部、尾部插入/删除数据。package com.demo.service; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.TimeUnit; Service RequiredArgsConstructor public class RedisListService { private final RedisTemplateString, Object redisTemplate; private final RedisConfig redisConfig; private String getKey(String key) { return redisConfig.getKeyPrefix() key; } // 获取List操作对象 private ListOperationsString, Object getListOps() { return redisTemplate.opsForList(); } // 1. 从列表头部插入数据 public Long leftPush(String key, Object value) { String redisKey getKey(key); Long result getListOps().leftPush(redisKey, value); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); return result; } // 2. 从列表尾部插入数据 public Long rightPush(String key, Object value) { String redisKey getKey(key); Long result getListOps().rightPush(redisKey, value); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); return result; } // 3. 从列表头部弹出数据弹出后删除 public Object leftPop(String key) { return getListOps().leftPop(getKey(key)); } // 4. 从列表尾部弹出数据弹出后删除 public Object rightPop(String key) { return getListOps().rightPop(getKey(key)); } // 5. 获取列表指定范围的数据start0end-1 表示所有数据 public ListObject range(String key, Long start, Long end) { return getListOps().range(getKey(key), start, end); } // 6. 获取列表长度 public Long size(String key) { return getListOps().size(getKey(key)); } // 7. 删除列表中指定值count0删除所有count1删除第一个count-1删除最后一个 public Long remove(String key, Long count, Object value) { return getListOps().remove(getKey(key), count, value); } // 实战示例存储用户浏览记录从尾部插入保留最新10条 public void addBrowseRecord(Long userId, String productId) { String key browse:record: userId; // 从尾部插入最新浏览的商品ID this.rightPush(key, productId); // 限制列表长度只保留最新10条 Long size this.size(key); if (size 10) { // 从头部删除多余数据 this.leftPop(key); } } // 实战示例获取用户最新浏览记录 public ListObject getBrowseRecords(Long userId) { String key browse:record: userId; // 获取所有浏览记录从第0条到最后一条 return this.range(key, 0L, -1L); } }四、Set 类型无序、不可重复的集合Set是 Redis 中一种无序且元素不可重复的数据结构。它非常适合处理需要去重、计算交集/并集的场景。 核心适用场景•标签系统用户标签、商品标签等。•数据去重例如去重的用户ID集合。•社交关系好友列表、共同好友交集、推荐好友差集。下面是一个完整的RedisSetService服务类封装了 Set 的常用操作。package com.demo.service; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; import org.springframework.stereotype.Service; import java.util.Set; import java.util.concurrent.TimeUnit; Service RequiredArgsConstructor public class RedisSetService { private final RedisTemplateString, Object redisTemplate; private final RedisConfig redisConfig; private String getKey(String key) { return redisConfig.getKeyPrefix() key; } // 获取Set操作对象 private SetOperationsString, Object getSetOps() { return redisTemplate.opsForSet(); } // 1. 向Set中添加数据可添加多个 public Long add(String key, Object... values) { String redisKey getKey(key); Long result getSetOps().add(redisKey, values); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); return result; } // 2. 获取Set中所有数据 public SetObject members(String key) { return getSetOps().members(getKey(key)); } // 3. 判断数据是否在Set中去重校验 public Boolean isMember(String key, Object value) { return getSetOps().isMember(getKey(key), value); } // 4. 删除Set中的指定数据 public Long remove(String key, Object... values) { return getSetOps().remove(getKey(key), values); } // 5. 获取Set的长度 public Long size(String key) { return getSetOps().size(getKey(key)); } // 6. 交集两个Set的共同数据如共同好友 public SetObject intersect(String key1, String key2) { return getSetOps().intersect(getKey(key1), getKey(key2)); } // 7. 并集两个Set的所有数据去重 public SetObject union(String key1, String key2) { return getSetOps().union(getKey(key1), getKey(key2)); } // 实战示例给用户添加标签去重 public void addUserTag(Long userId, String... tags) { String key user:tag: userId; this.add(key, tags); } // 实战示例获取用户所有标签 public SetObject getUserTags(Long userId) { String key user:tag: userId; return this.members(key); } // 实战示例获取两个用户的共同标签 public SetObject getCommonTags(Long userId1, Long userId2) { String key1 user:tag: userId1; String key2 user:tag: userId2; return this.intersect(key1, key2); } }五、ZSet 类型有序集合ZSet (Sorted Set)在 Set 的基础上为每个元素关联了一个分数score并根据分数进行排序。它是实现排行榜功能的绝佳选择。 核心适用场景•各类排行榜商品销量榜、文章点赞榜、用户积分榜、游戏高分榜。以下RedisZSetService展示了 ZSet 的核心操作和排行榜实现。package com.demo.service; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ZSetOperations; import org.springframework.stereotype.Service; import java.util.Set; import java.util.concurrent.TimeUnit; Service RequiredArgsConstructor public class RedisZSetService { private final RedisTemplateString, Object redisTemplate; private final RedisConfig redisConfig; private String getKey(String key) { return redisConfig.getKeyPrefix() key; } // 获取ZSet操作对象 private ZSetOperationsString, Object getZSetOps() { return redisTemplate.opsForZSet(); } // 1. 向ZSet中添加数据指定分数 public Boolean add(String key, Object value, Double score) { String redisKey getKey(key); Boolean result getZSetOps().add(redisKey, value, score); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); return result; } // 2. 批量添加ZSet数据 public Long addBatch(String key, SetZSetOperations.TypedTupleObject tuples) { String redisKey getKey(key); Long result getZSetOps().add(redisKey, tuples); redisTemplate.expire(redisKey, redisConfig.getExpireDefault(), TimeUnit.SECONDS); return result; } // 3. 增加元素的分数如点赞数1、积分10 public Double incrementScore(String key, Object value, Double delta) { return getZSetOps().incrementScore(getKey(key), value, delta); } // 4. 按分数升序排序获取指定范围的数据 public SetZSetOperations.TypedTupleObject rangeByScore(String key, Double min, Double max) { return getZSetOps().rangeByScoreWithScores(getKey(key), min, max); } // 5. 按分数降序排序获取指定范围的数据排行榜常用如前10名 public SetZSetOperations.TypedTupleObject reverseRangeByScore(String key, Double min, Double max, Long start, Long end) { return getZSetOps().reverseRangeByScoreWithScores(getKey(key), min, max, start, end); } // 6. 获取元素的分数 public Double score(String key, Object value) { return getZSetOps().score(getKey(key), value); } // 7. 获取ZSet的长度 public Long size(String key) { return getZSetOps().size(getKey(key)); } // 8. 删除ZSet中的指定元素 public Long remove(String key, Object... values) { return getZSetOps().remove(getKey(key), values); } // 实战示例商品销量排行榜降序取前10名 public SetZSetOperations.TypedTupleObject getProductSalesTop10() { String key product:sales:rank; // 分数从0到无穷大取前10名降序排列 return this.reverseRangeByScore(key, 0.0, Double.MAX_VALUE, 0L, 9L); } // 实战示例更新商品销量销量1分数1 public void updateProductSales(String productId) { String key product:sales:rank; this.incrementScore(key, productId, 1.0); } }六、Cacheable 注解缓存如果业务场景只是简单的“查询-缓存”无需复杂的 Redis 操作强烈推荐使用 Spring 提供的Cacheable注解。它能自动实现“查询数据库 → 存入缓存 → 后续命中缓存”的完整流程极大简化代码。 核心注解说明•Cacheable在方法执行前检查缓存命中则直接返回未命中则执行方法并将结果存入缓存。•CachePut无论缓存是否存在都会执行方法并用结果更新缓存。常用于更新操作。•CacheEvict删除指定缓存。常用于删除操作。package com.demo.service; import com.demo.entity.User; import com.demo.mapper.UserMapper; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; Service RequiredArgsConstructor public class UserCacheService { private final UserMapper userMapper; // 1. Cacheable查询时缓存key是用户ID缓存名称是userCache对应RedisCacheManager配置 Cacheable(value userCache, key #userId, unless #result null) public User getUserById(Long userId) { // 若缓存中没有才会执行此方法查询数据库 return userMapper.selectById(userId); } // 2. CachePut更新数据时同步更新缓存保证缓存与数据库一致 CachePut(value userCache, key #user.id) public User updateUser(User user) { userMapper.updateById(user); return user; } // 3. CacheEvict删除数据时删除对应的缓存 CacheEvict(value userCache, key #userId) public void deleteUser(Long userId) { userMapper.deleteById(userId); } }重要提示使用Cacheable等注解必须在 SpringBoot 启动类上添加EnableCaching注解来开启缓存功能。七、注意事项从开发到上线以下要点能帮你避开大多数“坑”。问题现象与风险解决方案1. 缓存乱码读取出的值是乱码或无法反序列化。必须配置自定义序列化如 Jackson2JsonRedisSerializer避免使用默认的 JdkSerializationRedisSerializer。2. 缓存穿透查询一个数据库中根本不存在的数据导致请求直达数据库。1. 缓存空值null并设置较短过期时间。2. 使用布隆过滤器预先校验。3. 缓存击穿某个热点Key突然过期大量并发请求瞬间涌向数据库。1. 设置热点Key永不过期。2. 使用互斥锁Mutex只让一个线程去查库其他线程等待。4. 缓存雪崩大量Key在同一时间过期导致所有请求落库数据库压力骤增。1. 给缓存过期时间加上随机值分散过期时间。2. 核心数据永不过期通过后台任务异步更新。5. Key命名规范不同项目或模块的Key冲突导致数据错乱。统一添加前缀如项目名:模块名:业务名:id。6. 过期时间设置过长占用内存过短导致缓存命中率低。根据业务特点设置高频变化数据时间短低频稳定数据时间长。7. Redis安全线上环境未设密码导致被恶意攻击或数据泄露。必须设置强密码并配置防火墙限制访问IP。8. 连接池配置高并发下连接池耗尽出现获取连接超时错误。根据压测结果合理调整max-active、max-idle、min-idle等参数。大家在项目中用 Redis 时有没有遇到过缓存穿透、雪崩的“惊险”时刻或者有什么独家的 Redis 调优技巧欢迎在评论区留言交流分享你的实战经验关注我后续持续更新 SpringBoot 实战教程从基础到高级带你轻松搞定后端开发少踩坑、多高效我们下期再见