1. 先聊聊缓存这件事:为什么大家都在用 Redis
我第一次接触 Redis,是因为线上数据库扛不住了。那时候业务量涨起来,MySQL 的查询压力肉眼可见地往上飙,慢查询日志里全是同一批热数据的影子——用户刷首页,每次都去数据库查一遍配置、查一遍商品信息、查一遍用户状态,明明这些数据几个小时都不变,却要被反复折磨几千次。老板心疼数据库,我也心疼接口延迟,于是第一次认真研究了 Redis。
Redis 说白了就是一个跑在内存里的键值数据库。它的全称是 Remote Dictionary Server,远程字典服务,字典这个词很形象:你用 key 找 value,就像查字典。数据放在内存里,读写速度是微秒级别的,单机 QPS 可以轻松跑到十万以上,比磁盘数据库快两三个数量级。所以它最核心、最广泛的应用就是做缓存——把那些频繁读取、变动少的数据放到 Redis 里,让请求先打 Redis,打不到了才回源数据库,数据库压力瞬间小一个量级。
这篇指南适合谁?如果你刚接触 Redis,不知道从哪下手;或者你已经在用 Redis 但只熟悉 set/get,想系统补齐数据类型、缓存设计、分布式锁、可视化工具这些实战内容;又或者你在准备面试,想把这些知识点串成一条线——那这篇内容应该对你有用。
我不会堆概念,而是按照我实际摸索出来的路径来写:先讲清楚 Redis 要解决什么问题,然后动手部署一个可用的环境,再把五种核心数据类型逐个过一遍,接着进入缓存治理的经典场景,最后把可视化工具和典型报错的排查经验也分享出来。整个过程你跟着敲命令,一台电脑就能完成。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:把 Redis 装起来并验证可用
很多教程上来就讲命令,但我的经验是,先把运行环境搞扎实,后面学什么都顺畅。装 Redis 不难,难的是装完以后不知道怎么验证、不知道怎么改配置,最后出了问题连日志都不看。这一节我把几种常用安装方式都列出来,你按自己的操作系统选一种就行。
2.1 三种主流安装方式对比
简单说,Redi s的安装分为三种路线:直接下载源码编译、用系统包管理器安装、用 Docker 拉镜像运行。
| 安装方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 源码编译 | Linux 服务器、生产环境 | 版本可控、可定制编译参数 | 需要编译环境、步骤多 |
| 包管理器 | macOS、Ubuntu 等开发机 | 一条命令搞定 | 版本可能偏旧 |
| Docker | 所有环境、快速验证 | 秒级启动、不影响宿主机 | 需要理解容器概念 |
源码编译我只在生产环境用,平时开发完全没必要。Linux 上的标准做法是:
bash复制wget https://download.redis.io/releases/redis-7.2.4.tar.gz
tar xzf redis-7.2.4.tar.gz
cd redis-7.2.4
make
make install
这里有个细节,编译前系统里必须有 gcc 和 make,Ubuntu 可以用 apt install build-essential 装上。编译时间不长,大概几分钟。装完以后 Redis 的可执行文件会被放到 /usr/local/bin 下,直接敲 redis-server 就能启动。
macOS 开发机我更推荐用 Homebrew,一条命令:
bash复制brew install redis
brew services start redis
Linux 的 Ubuntu/Debian 系列也可以用 apt:
bash复制sudo apt update
sudo apt install redis-server
sudo systemctl enable --now redis-server
这里我要提醒一句,apt 仓库里的版本经常滞后官方一两个大版本。Redis 7.x 带来的很多新特性,比如 Function、多线程等,旧版里是没有的。所以如果你有版本洁癖,还是建议源码装。
Windows 注意一下,Redis 官方目前不提供 Windows 原生版本,你在 Windows 上看到的安装包基本都是微软老早之前的分支或者第三方移植版本。我个人的建议是,在 Windows 上开发直接用 Docker,或者启用 WSL(Windows Subsystem for Linux),在里面装 Linux 版。别去下那些来路不明的 exe,坑很多。
2.2 Docker 部署:最省心的方案
Docker 是我现在最常用的方式,尤其是搭主从集群、做实验,几分钟就能起一套完整环境。你要做的事情只有三步:
bash复制docker pull redis:7.2
docker run -d --name redis-local \
-p 6379:6379 \
-v /myredis/data:/data \
-v /myredis/conf/redis.conf:/etc/redis/redis.conf \
redis:7.2 \
redis-server /etc/redis/redis.conf
拆开看这几行含义:-d 后台运行;--name redis-local 给容器起名;-p 6379:6379 把容器里的 6379 端口映射到宿主机,这样外部工具才能连;两个 -v 是挂载目录,分别把数据目录和配置文件挂出来,不然容器一删数据就没了。最后那一串 redis-server /etc/redis/redis.conf 是容器启动时执行的命令,指定用挂载进去的配置文件启动。
如果只是快速体验,不加配置文件也行:
bash复制docker run -d --name redis-local -p 6379:6379 redis:7.2
容器启动后,可以用 docker logs redis-local 看启动日志,看到 Ready to accept connections 就说明起来了。
注意:Docker 方式跑 Redis,端口映射一定要确认没被占用。6379 是 Redis 默认端口,很多机器上可能已经被之前装过的 Redis 实例占了,这时会启动失败,日志里会明确写
Address already in use。
2.3 启动、验证和三个必须知道的配置
不管你用哪种方式装,启动之后第一步永远是验证它真的活了。打开终端,执行:
bash复制redis-cli ping
返回 PONG 就是正常的。这一步我建议每换一台机器都先敲一次,很多所谓的“连不上 Redis”问题,其实在第一步就暴露了。
接下来要认识 redis.conf 里的三个高频配置,生产环境必调:
bind:默认是127.0.0.1,只允许本机访问。想让别的机器连,就得改成0.0.0.0或者指定 IP。这里有个安全提醒,改成 0.0.0.0 等于把 Redis 暴露到网络上,如果同时没设密码,你的 Redis 就是一台裸奔的服务器,被挖矿脚本盯上是分分钟的事。requirepass:设置访问密码。比如requirepass mypass123,之后客户端连接必须用AUTH mypass123或者连接串里带密码。daemonize:改成yes后 Redis 会后台运行,终端关了也不受影响。用 Docker 跑的话不需要管它。
还有一个让新手非常困惑的点:如果你用 redis-cli 连接本机 Redis 没设密码就直接进去了,千万不要觉得“Redis 好像很安全”。它之所以默认不设密码,是因为默认只绑定了本机回环地址,外面根本访问不到。一旦你要开放访问,密码必须同步跟上。
配置改完,重启 Redis 让它生效。源码方式可以用 redis-cli shutdown 关闭再重新 redis-server redis.conf 启动;Docker 方式就直接 docker restart redis-local。
3. 五种核心数据类型:命令背后的使用场景
Redis 之所以强大,是因为它不是只存字符串。它提供了五种基础数据结构,每种都有对应的命令体系和典型场景。我见过不少同学只会 set/get,然后遇到稍微复杂的需求就去数据库查——这其实浪费了 Redis 一大半的能力。
3.1 String:最基础但最容易忽略的细节
String 是 Redis 里最通用的类型,value 最大能存 512MB。它不仅能存文本,还能存数字、JSON 序列化后的对象、甚至二进制数据。常用命令有:
bash复制SET user:1:name "张三"
GET user:1:name
SET login:count 100
INCR login:count
SETEX code:13800138000 60 123456
第三行开始的那个 INCR 是很多场景的利器。计数器、限流、库存扣减都可以用它原子自增。这里有个新手很容易踩的坑:INCR 要求原来的值必须能解释成整数,如果你往里面存了 "abc",再 INCR 就会报错。看似简单的 String,在分布式锁、计数器场景里是绝对的主力。
3.2 Hash:对象存储的最佳选择
业务里我们经常要存一个对象的多个字段,比如用户信息:id、name、age、email。用 String 存,你得把整个对象塞成一个 JSON 字符串,每次改一个字段就得全量覆盖。用 Hash 就不一样了:
bash复制HSET user:1001 name "张三" age 25 email "zhang@example.com"
HGET user:1001 name
HGETALL user:1001
HINCRBY user:1001 age 1
Hash 的字段级操作意味着你可以只读写一个对象的某一个属性,这在内存占用和带宽上都更优。对于“对象详情页”这种场景——商品、订单、用户——Hash 几乎是标准答案。我在项目里封装过的通用缓存工具,就是基于 Hash 做的对象级缓存,序列化一次,之后按字段更新,非常顺手。
3.3 List:消息队列和最新列表
List 底层是双向链表,两头都能压能弹。Redis 的列表配合 LPUSH 和 BRPOP 可以做轻量级消息队列:
bash复制LPUSH msg:queue "task-1"
BRPOP msg:queue 0
BRPOP 的 B 是 Blocking 的意思,它会阻塞等待队列里有数据才返回,这样可以避免用定时轮询浪费资源。List 的另一个典型场景是最新排行榜、最新文章列表:用 LPUSH 把新数据压到头部,用 LTRIM 只保留前 N 条,就能实现“取最近 100 条”的需求。
不过要提醒一点,List 做消息队列是轻量级玩法,不具备消息确认、重试、死信队列这些特性。系统里需要可靠的异步处理,还是用专业的 MQ(如 RabbitMQ、Kafka)更稳妥。
3.4 Set 和 ZSet:去重、标签和排行榜
Set 是无序、不可重复的字符串集合,底层是哈希表。它最经典的应用是去重计算,比如统计一个用户访问过哪些页面:
bash复制SADD user:1001:visited "home" "product" "home"
SCARD user:1001:visited
SISMEMBER user:1001:visited "home"
同样一个 home 加两次,但集合里只有一条。SISMEMBER 判断是否包含,时间复杂度是 O(1),所以 Set 很适合做“是否已读”“是否已收藏”这类判断。
ZSet 在 Set 基础上加了 score(分数),元素按分数排序。这个结构简直是排行榜神器:
bash复制ZADD rank:game 1000 "player_a"
ZADD rank:game 1500 "player_b"
ZINCRBY rank:game 200 "player_a"
ZREVRANGE rank:game 0 9 WITHSCORES
ZREVRANGE ... WITHSCORES 取分数最高的前 10 名。改动分数用 ZINCRBY,自带原子性,不需要先查再改。我在一个抽奖活动里用过 ZSet 做积分排行榜,几百万用户的数据,查询和更新都是毫秒级,数据库完全无压力。
五种类型的选型我给个速查参考:
| 数据类型 | 底层结构 | 典型场景 | 常用命令 |
|---|---|---|---|
| String | 动态字符串 | 缓存、计数器、分布式锁 | SET / GET / INCR / SETNX |
| Hash | 哈希表 | 对象缓存、购物车 | HSET / HGET / HGETALL |
| List | 双向链表 | 消息队列、最新列表 | LPUSH / RPOP / LRANGE |
| Set | 哈希集合 | 去重、共同好友 | SADD / SISMEMBER / SINTER |
| ZSet | 跳表 + 哈希表 | 排行榜、优先级队列 | ZADD / ZRANGE / ZSCORE |
记住一句话:选类型不是凭喜好,而是看数据结构和查询模式。你要记住 zset 的跳表实现,这也是面试里常考的“为什么 ZSet 能同时支持范围查询和单点查询”的答案基础。
4. 缓存设计实战:从穿透到分布式锁
装好 Redis、会敲命令只是第一步。真正在工作中考验你的,是缓存架构的设计。这一节我挑几个最高频的实战场景展开讲。
4.1 缓存穿透、击穿、雪崩:三兄弟要分清楚
这三个词是面试高频,也是线上事故的高发区。我先把定义说清楚,再讲各自的解法。
缓存穿透:查询一个根本不存在的数据。请求来了,Redis 没查到,于是回源数据库,数据库也没有,返回空。如果攻击者故意拿一堆不存在的 ID 刷接口,每次都会打到数据库,缓存形同虚设。
缓存击穿:某个热点 key 到了过期时间,一瞬间大量请求同时回源数据库,数据库被这波并发打垮。
缓存雪崩:大量 key 在同一时间集体过期,或者 Redis 整个挂了,流量全部涌向数据库,数据库瞬间崩溃。
三种问题的本质完全不同:穿透是不存在的数据,击穿是单个热点过期,雪崩是批量过期或整体不可用。但解法有交叉。
穿透的三种解法:
- 缓存空值。查询结果为空时,也往 Redis 写一个空标记,过期时间设短一点,比如 5 分钟。这样同样的无效请求第二次就会命中缓存。
- 布隆过滤器。把所有存在的 ID 提前布隆过滤器里,查询前先判断 ID 是否存在,不存在直接返回,根本不进 Redis。布隆过滤器有误判率,但可以接受。
- 参数校验兜底。在接口层面对参数做合法性校验,比如 ID 必须是正整数、必须在某个范围内,非法的直接拒绝。
击穿的核心解法是互斥锁:
code复制热点 key 失效后,第一个请求拿不到缓存,不是直接查库,而是先尝试加锁(Redis 的 SETNX);
加锁成功的那个请求去查数据库,把结果写回缓存;
其他请求拿不到锁,就等待一段时间后重试,从缓存里取。
这个方案逻辑不复杂,但要注意锁的过期时间要和数据库查询时间匹配。如果数据库慢查询要 3 秒,你锁 1 秒就过期,那第 2 秒的时候别的请求又会进来打库,锁形同虚设。我在实际中习惯把锁时间设成 5 秒,并配合续期逻辑。
雪崩的两个方向:
- key 过期时间的处理:不要把过期时间都设成一个固定值,而是加一个随机抖动,比如
expire = 基础时间 + random(0~300) 秒。这样即使批量写入,过期时间也会自然散开。 - Redis 高可用:部署主从 + 哨兵模式,主节点挂了自动切换到从节点。这是架构层面的保障。
4.2 分布式锁:从 SETNX 到 Redisson
另一个绕不开的实战场景是分布式锁。在分布式系统里,多个服务实例可能同时处理同一个任务,比如定时任务、库存扣减、订单创建。如果没锁,就可能出现并发冲突。
最原始的分布式锁实现是 SETNX:
bash复制SETNX lock:order 1
返回 1 说明拿到了锁,返回 0 说明锁已被别人持有。但这里有个坑:如果拿到锁的进程崩了,锁永远不会释放,其他进程就一直等下去。所以 SETNX 必须配合过期时间:
bash复制SET lock:order 1 EX 30 NX
这一个命令同时完成“不存在才设置”和“设置过期时间”两个操作,避免了分开执行时崩溃导致的死锁。这是老版本命令 SETNX + EXPIRE 组合的坑,新版已经不建议单独用 SETNX 了。
但即便是 SET ... EX ... NX,仍然存在一个隐患:锁的持有时间估计不准。业务逻辑执行超过 30 秒,锁过期了,另一个线程拿到锁进来,两个线程同时执行,锁就失效了。解决的方案是看门狗(WatchDog)机制——持有锁的线程每隔一段时间自动续期,只要线程没执行完,锁就不过期。这个逻辑自己写很繁琐,行业里的标准做法是用 Redisson 客户端:
java复制RLock lock = redisson.getLock("lock:order");
lock.lock(30, TimeUnit.SECONDS);
try {
// 业务逻辑
} finally {
lock.unlock();
}
Redisson 默认开启看门狗,锁的超时时间默认 30 秒,看门狗每 10 秒会检查一次,如果业务没执行完就自动续期 30 秒。这套机制比你手写 SETNX 靠谱得多。我在项目里的建议是:能用 Redisson 就别自己造轮子,分布式锁的边界情况太多了。
还有一点要单独说:分布式锁并不能解决所有并发问题。如果你的目的是保护数据库层面的数据一致性,更推荐用数据库自身的乐观锁(版本号机制)。分布式锁适合跨服务的临界资源保护,两者使用边界要分清楚。
4.3 键空间设计与序列化方案
这两个问题看似小,其实影响深远。
键命名规范:我习惯用 业务名:模块名:ID:属性 的分层结构。比如:
code复制user:profile:1001
order:detail:20240101001
product:stock:sku123
好处一目了然——可读性强,运维看一眼 key 就知道它属于什么业务;方便模糊匹配,用 KEYS user:* 能列出所有用户相关的 key。这里提醒一句,KEYS 命令在生产环境要慎用,它会阻塞 Redis 单线程服务,数据量大时会让整个 Redis 卡顿。替代方案是 SCAN 命令,游标式遍历,不阻塞。
序列化方案是 Java 项目里的重灾区。Spring Data Redis 默认的 JDK 序列化方式会把对象序列化成一堆不可读的乱码,在 Redis Desktop Manager 里看到的就是 \xAC\xED\x00\x05t\x00 这种。我的建议是,缓存场景统一用 JSON 序列化,可读性最好,跨语言兼容性也最好。Spring Boot 下可以自定义 RedisTemplate 的序列化器:
java复制@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// key 用 StringRedisSerializer,value 用 GenericJackson2JsonRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
这么配置以后,key 在 Redis 里就是人类可读的字符串,value 是标准 JSON,查问题的时候肉眼直接看。有的人会担心 JSON 序列化性能不如 JDK 原生,说实话,在绝大多数业务场景下这个差距可以忽略不计,而可读性和排查效率的提升是实打实的。
5. 可视化工具与连接配置:从命令行管理到日常工作流
命令行熟归熟,但日常排查问题、看数据长什么样子,可视化工具能省不少时间。这一节聊聊我实际用过的工具和连接踩坑。
5.1 Redis Desktop Manager 与 Another Redis Desktop Manager
提到 Redis 可视化客户端,老牌的是 Redis Desktop Manager(RDM)。界面左上角是连接列表,中间是 key 的列表,右侧是 value 内容。你可以浏览所有 key、按模式搜索、直接编辑 String 和 Hash 的数据,还能查看 TTL(剩余过期时间)并手动修改过期时间。对排查线上问题来说非常够用。
后来 RDM 从免费转成了部分收费,于是社区里出现了 Another Redis Desktop Manager(简称 ARDM),完全免费开源。它的功能和 RDM 基本一致,还加了一些 RDM 没有的细节,比如支持 Redis 集群节点的树形展示、内置命令行的终端模式、内存分析排序等。我现在用的就是 ARDM,日常操作完全满足。
如果追求官方血统,Redis 官方也出了 RedisInsight,界面现代、功能全,支持可视化查看键值、性能分析、慢日志查询。三者怎么选?我的建议是:免费先用 ARDM,RDM 如果你买过正版或者公司有 License 也好用,RedisInsight 更适合做深入性能分析。
5.2 连接常见坑:端口、密码、绑定地址
可视化工具连不上 Redis,99% 的原因是这三个:
- 连接地址填成了
localhost,但你的 Redis 跑在 Docker 容器里,端口没映射出来。 bind 127.0.0.1没改,外部机器根本进不来。- 密码没填对,或者 Redis 根本没设密码而你填了密码。
排查顺序我一般是这样:先在本机 redis-cli ping,确认服务正常;再用 redis-cli -h 目标IP -p 6379 ping 确认网络层面能通;最后才回去检查客户端的连接参数。很多人第一步都不做直接点连接,报错了就懵,其实问题往往就在防火墙或者 bind 配置上。
有一条安全底线我反复说:不要把没设密码的 Redis 直接绑定到 0.0.0.0 公网地址。一个没有认证的 Redis 暴露在公网,黑客可以用扫描工具在几小时内发现并植入挖矿程序。生产环境一定要设置强密码,并且最好启用 Redis 的 ACL 机制,按用户分配权限。
5.3 日志和慢查询:日常运维的两把钥匙
Redis 的日志文件路径默认是 stdout(控制台),用 Docker 跑时会打到容器日志里,用 docker logs redis-local 就能看。源码方式启动时可以在 redis.conf 里配置 logfile /var/log/redis/redis.log,这样日志会持续写文件。
比日志更常用的是慢查询日志。Redis 可以记录执行时间超过阈值的命令:
bash复制CONFIG SET slowlog-log-slower-than 10000
CONFIG SET slowlog-max-len 128
10000 单位是微秒,也就是超过 10 毫秒的命令就会被记录。用 SLOWLOG GET 查看。排查线上问题的时候,慢查询日志能直接告诉你哪个命令在拖后腿,通常答案集中在 KEYS、SMEMBERS、大 value 的 GET 这些命令上。这也是我反复强调为什么生产环境要慎用 KEYS 的原因——它是慢查询的常客。
6. 高频报错与排查技巧实录
最后分享几个我实际遇到过的典型问题,都是从零开始学 Redis 的人最容易被卡住的点。
6.1 Command timed out 报错:Lettuce 连接池的坑
如果你用的是 Spring Boot 2.x 的 Redis 和 Lettuce 客户端,可能遇到过这样一条报错:
code复制redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException
第一次看到这个报错时我很慌,以为是 Redis 挂了,但 redis-cli ping 完全正常。后来才发现问题出在 Lettuce 的默认配置上——它默认没有连接池!每次都新建连接,一旦并发量上来,连接创建速度跟不上,等待队列堆积,就超时了。
解决方案是给 Lettuce 配上连接池。在 Spring Boot 里这样配置:
yaml复制spring:
redis:
host: localhost
port: 6379
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 2
max-wait: 3000ms
max-active 是最大连接数,max-idle 是最大空闲连接,max-wait 是从池中获取连接的超时时间。配好之后超时问题基本消失。这也算是一个“框架默认配置没有兼顾高并发”的典型例子。
6.2 缓存和数据库的一致性问题
这是另一个大坑。缓存更新的顺序不对,就会出现脏数据。最常见的错误做法是:先更新数据库,再删除缓存,如果删除缓存失败,数据库和缓存就会不一致。
我常用的方案是“先更新数据库,再删除缓存”,加上一个兜底:删除失败的 key 放到一个重试队列里,由另一个线程负责重删。如果还是失败了,就靠过期时间兜底,缓存总有失效的时候,最终一致性还是能保证的。
这里有个细节值得展开:为什么不先删缓存再更新数据库?因为你先删缓存,一个请求查缓存未命中回源数据库,而数据库还没更新完,它就会把旧数据写进缓存,把刚删的缓存又污染了。反过来先更新数据库,至少新数据已经落库,就算删除缓存失败,也只是短暂的不一致,让过期时间去兜底,问题不大。
6.3 面试里常考的几张“王牌”
准备面试的同学,这几个点一定要能讲清楚:
- Redis 为什么快?答:纯内存操作,单线程避免了上下文切换和锁竞争,IO 多路复用模型。
- Redis 单线程为什么还能那么快?答:Redis 的瓶颈是网络 IO 和内存,不是 CPU;单线程模型简单、无锁、无上下文切换。不过 Redis 6.0 以后引入了多线程处理网络 IO,但命令执行仍然是单线程。
- Redis 和 Memcached 的区别?答:Redis 支持多种数据结构、支持持久化、支持主从复制和集群,Memcached 基本只有 String,纯缓存,不支持持久化。
- Redis 持久化机制 RDB 和 AOF 的区别?答:RDB 是定时快照,恢复快但可能丢数据;AOF 是追加日志,最多丢 1 秒数据但文件大、恢复慢;生产环境通常两者结合。
这些问题背后考察的其实是你对 Redis 设计哲学的理解,而不是死记硬背。我在面试新人时,更看重的不是背得多熟,而是能不能解释“为什么单线程反而快”这种反直觉的问题背后的原理,以及有没有真正动手踩过坑的经历。
7. 写给我自己的几点实操心得
Redis 学到一定阶段,你会发现它不只是一个缓存工具,更是一套关于“如何用内存换时间”的思维方法。
我在实际项目里最大的体会是:Redis 的坑大多数不是 Redis 本身的问题,而是使用姿势的问题。搞不清楚数据类型的特性、忽略了序列化方式、没设密码就暴露公网、不配置连接池、依赖一堆 KEYS 命令——这些才是线上事故的真正根源。Redis 本身极其稳定,它把正确性都留给了使用者。
另外一个经验是,遇到问题先看日志,再看慢查询,最后用可视化工具连上去看真实数据。排查顺序对了,效率能差十倍。别一上来就重启 Redis——那只能掩盖问题,不能解决问题。
最后分享一个小技巧:在项目里写一套统一的缓存工具类,把 key 前缀、过期时间、序列化方式都封装好,团队里谁都不能直接去裸写 RedisTemplate 操作。规范这东西,只有落成代码才是真的规范。这也是我比较推荐新手进阶时做的第一件“工程化”的事。
