做后端开发的,大概没有几个人不知道Redis这个单词。我面试过不少人,简历上写着“熟练使用Redis做缓存”,但真问下去,往往就卡在“Redis到底快在哪里”“为什么用Redis不用HashMap”“如果Redis挂了怎么办”这类问题上。作为《Redis系列》的第一篇,我想先带你把地基打扎实:Redis是什么、能做什么、怎么安装、有哪些数据类型,以及学习路线应该怎么规划。这篇文章不追求几句命令让你“看起来会了”,而是尽量把原理和场景讲透,让你确实搞懂核心概念,后面再深入时就不会一头雾水。
我也提前说明一下,这个系列会按“初识 -> 命令 – > 持久化与高可用 -> 缓存治理与实战”的路子往下走。如果你是刚接触Redis的初学者,或者用了一段时间但总感觉哪里没想明白,这篇文章就是给你准备的。
1. Redis到底是什么:先建立正确的底层认知
1.1 一句话定位:它是跑在内存里的远程字典服务
Redis全称叫Remote Dictionary Server,直译过来是“远程字典服务器”。这个词其实已经把它的本质说清楚了:一个部署在服务器上的、通过网络访问的“字典”。什么是字典?就是键值对(Key-Value)集合。你在程序里写的HashMap、Dictionary,本质上也是键值对存储,Redis干的就是这件事,只是它把数据放到了一台独立服务器上,让多个应用可以共同访问同一份数据。
用个更生活化的比喻:Redis就像一个开在商场里的共享储物柜。你把东西(value)放进标了号码(key)的柜子,锁好后自己保管钥匙。以后想取想存,拿着号码开柜子就行。和本地HashMap最大的区别是,这个储物柜是所有应用共享的,你的Java服务、Python服务、前端网关,只要配置对了,都能访问同一个key。这才是缓存、分布式锁、Session共享等技术能成立的前提:数据不在某个进程里,而在一个独立的公共位置。
所以你可以把它理解成:一个跑在内存里、支持网络访问、数据格式远比普通字典丰富的共享字典服务。它既是缓存,也能当数据库用,还能兼职消息队列。这就是为什么大家都说Redis是后端技能树的必修课。
1.2 为什么Redis快:内存、单线程与精心设计的数据结构
很多人第一次接触Redis,第一个问题就是“它为什么会这么快”。官方给出的数据是,单节点QPS可以突破10万,比传统数据库高几个数量级。快的原因,归纳起来有三层。
第一,数据都在内存里。内存的随机访问延迟是纳秒级别,而磁盘的随机访问延迟是毫秒级别,这两者之间差了好几个数量级。当然,市面上不是只有Redis一个内存数据库,但Redis把“内存存储”这件事做到了极致简洁。第二,命令执行是单线程模型。你可能会觉得“并发才快,单线程不慢吗”,恰恰相反,单线程省掉了多线程中的上下文切换、锁竞争、CPU调度这些开销。Redis的核心操作都是纯内存计算,CPU根本来不及成为瓶颈,网络IO才是大头。所以单线程反而让系统更简单、更高效。Redis在6.0之后引入了多线程IO来提升网络吞吐,但命令本身的执行仍然是单线程,这是为了保持操作的原子性和实现上的简单可靠。第三,底层数据结构不是一把梭。Redis为不同类型设计了专用结构,比如SDS动态字符串、跳表、压缩列表、quicklist等等,每个结构都针对典型场景做了内存和性能上的优化。这不是用通用方案硬扛,而是“因材施教”。
1.3 初识阶段必须记住的6个特性
既然要系统学,先把特性清单列出来。初识阶段,你只需要记住下面这几条,后面的文章会逐条展开。
- 纯内存存储,读写性能极高,单节点十万级QPS。
- 支持丰富的数据类型,不止是String,还有List、Hash、Set、ZSet等。
- 支持为每个key单独设置过期时间,天然契合缓存场景。
- 支持持久化(RDB快照和AOF日志),重启后数据不会全部丢失。
- 支持主从复制、哨兵和集群部署,可以搭建高可用的生产架构。
- 提供事务、Lua脚本、发布订阅、管道等高级特性,能做很多中间件能做的事。
把这6条记在脑子里,你已经比很多人对Redis的理解深入一层了。紧接着的问题就是:怎么把它跑起来?下面讲安装,这部分我尽量把不同系统下的坑都给你点出来。
2. 第一台Redis:Windows、Linux、Mac和Docker怎么选
2.1 Windows安装:5.0.14.1与开机启动
先说一个很多人不知道的冷知识:Redis官方并不提供Windows原生版本,因为它的核心实现依赖fork这类在Linux下表现更好的系统调用。你在Windows上能装到的,基本都是微软维护的旧版本或者民间移植版,目前社区里流传最广的就是5.0.14.1这个版本。日常学习和本地开发够用了,但生产环境不要用Windows版,这是底线。
下载解压之后,目录结构大概是这样的:redis-server.exe是服务端,redis-cli.exe是客户端,redis.windows.conf是配置文件。启动服务先在目录下开一个命令行窗口,执行:
redis-server.exe如果看到监听6379端口的日志输出,就说明已经起来了。再开一个窗口验证一下:
redis-cli.exe -h 127.0.0.1 -p 6379 ping返回PONG,说明状态正常。
这里有个小坑:Windows下的解压路径不要带中文和空格,否则可能出现各种奇怪的路径读取问题。另外,如果你安装的版本带了配置文件,建议启动时显式指定它:
redis-server.exe redis.windows.conf这样配置文件里的参数才会生效,比如设置密码、修改端口、开启AOF持久化等等。
想把它注册成Windows服务,实现开机自启,也比较简单:
redis-server.exe --service-install redis.windows.conf --loglevel verbose redis-server.exe --service-start以后服务就在Windows服务列表里了,不用每次手动开窗口。需要卸载时执行:
redis-server.exe --service-uninstall2.2 Linux下源码编译安装Redis
生产环境的Redis基本都是跑在Linux上的,所以Linux下的安装你一定得会。最快的方式是用包管理器:
# Ubuntu/Debian sudo apt install redis-server # CentOS/RHEL sudo yum install redis包管理器安装的好处是省心,坏处是版本往往偏旧——我见过不少CentOS自带源里还是4.x、5.x的版本,一些新特性用不上。想要官方最新稳定版,走一遍源码编译流程更靠谱,整个过程也不复杂:
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编译器和相关依赖,CentOS下可以提前执行sudo yum install gcc make。如果make过程报内存分配相关的错,可以带上:
make MALLOC=libc这能绕开jemalloc在某些服务器环境下的兼容问题。编译安装完成后,redis-server和redis-cli默认装到了/usr/local/bin,任何目录下都能直接执行。
接着把配置文件放到规范的位置,方便后续管理:
mkdir -p /etc/redis /var/lib/redis /var/log/redis cp redis.conf /etc/redis/然后编辑配置文件,按生产需要调整几个关键项:daemonize yes让Redis在后台运行,requirepass设置访问密码,appendonly yes开启AOF持久化,logfile /var/log/redis/redis.log指定日志路径。启动时用:
/usr/local/bin/redis-server /etc/redis/redis.conf这套流程走一遍,你对“编译、安装、配置、启动”这个链路就有了整体概念,后面在云服务器上部署也会很顺手。
2.3 Mac用户最省事的安装方式
Mac下装Redis,Homebrew是唯一推荐的方式:
brew install redis redis-server /opt/homebrew/etc/redis.conf如果是Apple Silicon芯片,路径一般是/opt/homebrew/etc/redis.conf;Intel芯片则是/usr/local/etc/redis.conf。安装完默认不会后台运行,想开机自启就用:
brew services start redis用brew services管理的好处是日志和进程都交给系统统一管,重启电脑也不用手动启动,排查问题时直接看日志就行。
2.4 Docker一条命令完成安装与主从
现在我个人带新人,最推荐的学习方式其实是Docker。一条命令就能把Redis跑起来,环境干净,坏了就删掉重建,根本不需要跟本机依赖作斗争:
docker run -d --name redis -p 6379:6379 redis:7.0-d表示后台运行,--name redis给容器命名,-p 6379:6379把容器内的6379端口映射到宿主机。启动后验证:
docker exec -it redis redis-cli ping如果想要带密码启动,在镜像后面直接追加参数:
docker run -d --name redis -p 6379:6379 redis:7.0 --requirepass 123456后面学主从复制时,Docker的优势更明显。比如先启动一个主节点,再启动一个从节点,通过--link或者自定义网络就能在几秒钟内组成一主一从:
docker run -d --name redis-slave -p 6378:6379 redis:7.0 --replicaof redis-master 6379这比你在一台机器上装两个Redis实例省太多事了。所以我常跟新人说:学习Redis的第一步不是背命令,是先把环境问题解决掉。环境越简单,你越能专注于Redis本身。
3. 五种数据类型:把String、List、Hash、Set、ZSet一次讲透
Redis解决业务问题的核心武器,就是它丰富的数据类型。下面逐个讲清楚它们的底层逻辑和适用场景。注意,这里我讲的是设计思路,具体的命令实操放到后面专门章节,这样知识结构更清晰。
3.1 String:不只是存字符串,它还是计数器
String是Redis最基础的类型,value最大能存512MB。你平时用的SET key value、GET key就属于这类。但它远不止“存字符串”这么简单——它还提供了原子自增命令,用来做计数器非常顺手。
比如写文章网站的阅读量统计,典型操作是:
INCR article:read:1001 INCRBY article:read:1001 10这两个命令是原子操作,高并发下不会丢更新,这比“先GET再SET”的写法安全得多。网上经常有人问“Redis的incr不准”,其实INCR本身不会不准,真正不准的情况往往是:你用了GET拿到值,在程序里加1,再SET回去,这个“读改写”三步操作在并发下会互相覆盖;或者多个应用实例同时写同一个计数器,但没做同步。解决方式很简单:计数类的操作,永远走Redis的原子命令,不要绕开它。
String的典型场景包括:缓存序列化后的对象、计数器、分布式ID生成、Session共享等。
3.2 List:栈、队列、消息流都能做
List在底层是一个双向链表结构,头部和尾部操作都是O(1)级别。你可以用LPUSH往左塞,RPUSH往右塞,再配合LPOP、RPOP弹出数据,于是栈、队列、消息流它都能做。
一个很常见的用法是做轻量级消息队列。生产者只需要:
LPUSH task:queue task-1消费者用阻塞式的BRPOP取消息,没有数据时客户端会阻塞等待,不会空转消耗CPU:
BRPOP task:queue 0这里的0表示永不超时。拿List做队列有个前提:你要能接受消息可能丢失的缺陷。因为List本身没有消费者确认机制,消费者取走消息之后进程崩了,这条消息就再也找不回来了。如果想要更可靠的消息队列,还是要去用专业中间件,比如RabbitMQ、RocketMQ这些。
List还适合做“最新N条”这类业务,比如用户下拉刷新要看到最新5条公告,用LRANGE list 0 4就能直接取到。为什么List合适?因为新数据都是往队头或队尾插入的,天然有时间顺序。
3.3 Hash:一个key能存多个字段,天然适合对象
Hash类型在Redis内部是一个字段到值的映射表,相当于“key里套了一个小字典”。看命令就明白了:
HSET user:1001 name "张三" age 25 city "深圳" HGET user:1001 name HGETALL user:1001和String存一个JSON字符串相比,Hash的优势是:你可以只修改其中一个字段,不需要整个对象读出来反序列化再写回去。比如修改年龄,一条HSET就搞定。另外Hash还能对单个字段做原子自增:
HINCRBY user:1001 age 1业务对象信息、购物车、配置项这类“一个实体多个属性”的数据,用Hash非常合适。
3.4 Set:去重、抽奖、好友关系就靠它
Set是一个无序的字符串集合,元素不能重复。它天然就适合去重场景:用户访问的IP、参与活动抽奖的用户ID、文章的标签,都可以往Set里塞。
常用命令也很直观:
SADD tag:article:1001 "Redis" "后端" "教程" SISMEMBER tag:article:1001 "Redis" SCARD tag:article:1001SISMEMBER判断元素是否存在是O(1)级别的,适合做“某用户是否已经参与过活动”这类校验。更强大的是集合运算能力:取两个Set的交集、并集、差集。比如求两个用户的好友共同关注,一行SINTER user:1:follow user:2:follow就出结果了。不用在程序里拉全量数据再过滤,在Redis内部就完成了。
3.5 ZSet:排行榜这类“带权排序”的首选
ZSet又叫有序集合,它在Set的基础上给每个元素关联了一个分数(score)。分数允许重复,但元素本身不重复。Redis内部用跳跃表加哈希表实现了这个结构,既能通过元素快速查询分数,又能按分数范围高效遍历。
排行榜业务是ZSet的经典应用。以游戏战力排行榜为例:
ZADD leaderboard 100 "player:1" ZADD leaderboard 98 "player:2" ZREVRANGE leaderboard 0 2 WITHSCORESZREVRANGE按分数从高到低取出前三名。玩家战力发生变化时,用ZINCRBY leaderboard 5 "player:1"直接改分数,排行榜顺序自动维护,完全不需要你重排。ZSet还有很多其他玩法,比如把score当成时间戳,就能实现延迟队列。
3.6 类型选型速查表
初学时最容易纠结的就是“这个业务到底该用哪个类型”。我整理一张速查表,建议直接收藏:
| 类型 | 底层结构 | 典型场景 | 核心命令 | 时效性 |
|---|---|---|---|---|
| String | SDS动态字符串 | 缓存、计数器、Session | SET、GET、INCR | 永久可用 |
| List | 双向链表/quicklist | 消息队列、最新列表 | LPUSH、BRPOP、LRANGE | 数据多时注意内存 |
| Hash | listpack/hashtable | 对象存储、购物车 | HSET、HGET、HINCRBY | 字段少省内存 |
| Set | intset/hashtable | 去重、抽奖、标签 | SADD、SISMEMBER、SINTER | 集合运算灵活 |
| ZSet | 跳表+哈希表 | 排行榜、延迟队列 | ZADD、ZINCRBY、ZREVRANGE | 排序需求强烈推荐 |
选型的大原则很简单:有排序用ZSet,要去重用Set,对象属性频繁改动用Hash,消息流用List,纯缓存和计数器用String。把这个原则记牢,大部分场景都不会选错。
4. 客户端工具选型:命令行永远是底线,GUI只是锦上添花
4.1 redis-cli:学会它,你将无所畏惧
不管你装了哪个版本的Redis,redis-cli一定是自带的核心客户端。它支持连接远程服务:
redis-cli -h 192.168.1.10 -p 6379 -a 你的密码里面的-a指定密码,但要注意这会暴露在命令行历史里,生产环境建议用REDISCLI_AUTH环境变量传密码更稳妥。还有一个很实用的小细节:默认情况下redis-cli在中文Windows控制台里输出中文可能乱码,加--raw参数可以以原始格式输出。
需要了解Redis运行状态时,INFO命令能输出一大票指标,比如内存占用、客户端连接数、命中率;排查线上连接问题用CLIENT LIST;MONITOR可以实时看所有请求,但生产环境慎用,它会拖低性能。
连接不上时最常见的原因有几个:Redis没启动、端口没放通、配置文件里bind限制了访问、设置了密码但没带-a。排查顺序就按这个来。
4.2 Redis Desktop Manager与Another Redis Desktop Manager
图形化客户端这块,老牌选手是Redis Desktop Manager,社区习惯简称RDM。它经历了开源和商业化几次变动,早期版本大家用得比较多,后来不少人转向了开源的Another Redis Desktop Manager,界面更现代,支持Windows、Mac、Linux全平台,连接配置、key浏览、类型查看、命令执行都做得很成熟。
这些GUI工具核心能干什么?一是连接管理,你可以把开发、测试、生产环境的不同Redis实例按环境分组保存;二是可视化浏览key,按前缀过滤,查看每个key的类型和值;三是执行命令的窗口;四是一些简单的分析工具,比如查看key数量、内存占用。对日常开发来说,这些功能已经足够。
但我要提醒一句,GUI工具只是辅助,不要完全依赖它。原因有二:一是生产环境出于安全考虑往往禁止GUI直连,你最终还是要靠命令行;二是GUI的自动刷新、格式化显示会掩盖一些细节,比如某个key的TTL到底还剩多少,不如命令输出直观。所以我建议新人的工具学习顺序是:先把redis-cli用熟,再配一个GUI当可视化辅助。
4.3 我给新人的工具选型建议
总结一下我的推荐方案:
- 本地学习:Windows或Mac上用Redis Desktop Manager这类GUI,直观理解数据和类型。
- 日常开发调试:优先
redis-cli,写脚本或者快速验证时效率极高。 - 线上环境:只用命令行,配合监控平台查看指标。
工具本质上只是“看数据”的手段,真正值钱的是你脑子里的模型,比如决定用ZSet而不是List、用Hash而不是String。这些判断能力,GUI给你提供不了。
5. 高频命令实操:用4个典型业务把常用命令串一遍
这一章换一种学法,不按命令分类背,而是模拟真实业务场景,把高频命令都用上。你跟着敲一遍,比死记命令列表强得多。
5.1 业务一:缓存用户信息
用户登录后,通常要把用户信息缓存起来,避免每次请求都查数据库。最简单的方式是用String,把用户对象序列化成JSON再存:
SET user:info:1001 '{"name":"张三","age":25,"city":"深圳"}'取的时候直接GET user:info:1001,在服务端反序列化回对象。这种方式写起来简单,适合“整体读取、整体覆盖”的场景。
但如果某个字段经常变动,比如用户的积分、等级,用Hash更好:
HSET user:info:1001 name "张三" age 25 city "深圳" points 100 HINCRBY user:info:1001 points 10一条命令只更新一个字段,不用重新序列化整个对象,性能和代码复杂度都会更优。这里给你一个经验法则:整体缓存用String,局部更新用Hash。
5.2 业务二:排行榜
排行榜是ZSet的舞台。以新人积分榜为例:
ZADD rank:newbee 200 "user:1" ZADD rank:newbee 150 "user:2" ZADD rank:newbee 188 "user:3"查看前三名:
ZREVRANGE rank:newbee 0 2 WITHSCORES给某个人加10分:
ZINCRBY rank:newbee 10 "user:1"查看某个人的当前排名:
ZRANK rank:newbee "user:1"一套操作下来,你会发现“排序”这件事Redis全给你代劳了。换成用MySQL实现,每次查询都要走一次ORDER BY,还要处理索引,性能和代码复杂度都不可同日而语。
5.3 业务三:IP去重与集合运算
假设要统计某天的独立访客IP,Set是天然适合的类型:
SADD ip:2025-01-01 "10.0.0.1" SADD ip:2025-01-01 "10.0.0.2" SADD ip:2025-01-02 "10.0.0.2"看一眼当天独立IP数:
SCARD ip:2025-01-01判断某个IP是否访问过:
SISMEMBER ip:2025-01-01 "10.0.0.1"如果还要分析“元旦和第二天都访问过的人”,一个交集命令就搞定:
SINTER ip:2025-01-01 ip:2025-01-02再看“1月1日访问过但1月2日没访问的人”:
SDIFF ip:2025-01-01 ip:2025-01-02这类集合运算在用户标签、好友关系、权限控制里用得极多,建议动手敲一遍。
5.4 业务四:轻量级消息队列
用List做简单任务队列,生产者往左边塞:
LPUSH task:queue "task-1" LPUSH task:queue "task-2"消费者阻塞取:
BRPOP task:queue 0BRPOP会一直阻塞到队列里有数据才返回,这样消费者就避免了一个空的while true循环在那里疯狂自旋。你也可以指定超时时间,比如BRPOP task:queue 3,等3秒没数据就返回null,方便做心跳检测。
这套轻量队列适合异步发短信、发邮件、下游系统解耦这类对可靠性要求不高的场景。如果要严格不丢消息,请换专业消息队列。
5.5 通用操作:过期时间、TTL与key管理
Redis所有类型的key都支持设置过期时间,这也是它作为缓存最重要的一块拼图:
EXPIRE user:info:1001 3600 # 一小时后过期 TTL user:info:1001 // 查看剩余存活时间,-1表示永不过期,-2表示key不存在清除过期时间让key永存:
PERSIST user:info:1001删除key和类型检查:
DEL user:info:1001 EXISTS user:info:1001 TYPE user:info:1001还有一个必须强调的坑:生产环境禁止用KEYS命令。KEYS *会把所有key拉一遍,数据量大时直接卡死Redis。线上环境需要用SCAN命令进行增量遍历:
SCAN 0 MATCH user:* COUNT 100它每次返回一批key,还带一个游标,循环遍历直到游标归0。这是安全和性能之间的平衡,虽然稍微啰嗦一点,但绝不会拖垮Redis,这也是“初识”阶段就有必要知道的红线。
6. 进阶概念扫盲:知道这些名词,面试不露怯
6.1 持久化:RDB与AOF
Redis是内存数据库,没有持久化的话,服务器一重启数据就全没了。官方提供了两条持久化路线:RDB快照和AOF日志。
RDB的思路是定时把当前内存里的全量数据生成一个二进制快照文件(dump.rdb)。它恢复速度快、文件紧凑,适合做备份和灾难恢复。缺点是快照之间这段时间的数据会丢。AOF的思路则是把每一条写命令追加到日志文件里,恢复时重放日志就行。AOF能实现更细粒度的持久化,你可以配置always每次写都刷盘,或者everysec每秒刷一次,数据安全性更高,但文件体积更大,恢复速度也相对慢。
Redis 4.0之后还提供了混合持久化:RDB快照加AOF增量日志,兼顾了重启速度和数据完整性。初识阶段,你只需要记住一句话:RDB管快照,AOF管日志,两者可以共存,重启时优先加载AOF来恢复数据。至于具体配置和踩坑,后面的专项文章会展开。
6.2 缓存穿透、缓存击穿与缓存雪崩
这三个名词是Redis在高并发场景下绕不开的问题,也是面试高频题。我用自己的理解给你讲明白。
缓存穿透:查询一个根本不存在的key,缓存里没有,数据库里也没有,每次请求都会一路打到数据库。如果这个key被恶意刷,数据库压力直接爆炸。解决方案有两个方向:一是布隆过滤器,提前把所有可能存在的数据放到过滤器里,查不到就直接拦截;二是把“空结果”也缓存起来,给个很短的过期时间,比如5分钟,避免相同查询重复打库。
缓存击穿:一个热点key正好在某个瞬间过期,与此同时大量请求打过来,全部穿透到数据库。解决思路是加互斥锁,让第一个请求去数据库加载并重建缓存,其他请求等一会儿再重试;或者用“逻辑过期”方案,给缓存值里塞一个过期时间戳,发现逻辑过期了再回源更新,返回旧值先顶住。
缓存雪崩:大量key在同一段时间集中过期,导致一波请求全部穿透到数据库。解决方法是给过期时间加随机抖动,比如基准时间加0到5分钟的随机值,让过期时间分散开;同时可以做多级缓存分摊压力。
这三个问题,你需要能用自己的话说清楚,并且说出至少一种应对方案。到了后续的《缓存治理》篇,我会再给完整的落地方案。
6.3 主从复制、哨兵与集群的区别
很多人把这几个概念混在一起,我直接给一张对比表,看完就清楚了:
| 维度 | 主从复制 | 哨兵模式 | 集群模式 |
|---|---|---|---|
| 核心能力 | 数据备份、读写分离 | 在主从基础上自动故障转移 | 数据分片存储、水平扩展 |
| 部署结构 | 一个主节点多个从节点 | 主从加哨兵进程 | 多个主节点,每个主节点可带从节点 |
| 故障处理 | 需要人工干预 | 哨兵自动提升从节点为主 | 哈希槽迁移,自动容错 |
| 数据分片 | 无 | 无 | 有,16384个哈希槽 |
| 适用场景 | 中小规模起步 | 对可用性要求高的单分片 | 大数据量、高并发场景 |
主从复制解决的问题是“一台Redis挂了数据就没了”的恐惧,从节点持有完整副本,可以承接读流量。但主节点故障时,从节点不会自动上位,所以有人设计了哨兵,专门盯着主从的状态,发现主节点挂了就自动把一个从节点提升为主节点,应用通过哨兵感知新地址。当数据量大到单节点内存装不下时,就要上集群:数据按key算哈希槽,分散到多个主节点上,每个主节点再配从节点保证高可用。这个演进过程,就是Redis从“单机”走向“分布式”的完整路径。
6.4 分布式锁:从SETNX到Redisson
分布式锁是Redis在微服务架构里最重要的应用之一。核心需求是:多个服务实例同时操作同一个资源时,只能有一个实例获得执行权。Redis实现分布式锁的基石是SET命令带上NX和EX参数:
SET lock:order 9529 NX EX 30NX表示“只有当key不存在时才设置成功”,谁设置成功了,谁就拿到了锁;EX 30表示这把锁30秒后自动过期,防止持有锁的实例挂了导致死锁。释放锁时要注意,不能简单地DEL,因为你可能把别人刚获取到的锁误删了。正确做法是用Lua脚本,先校验锁的value是不是自己设置的,再决定是否删除:
if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end这个“先比较再删除”的过程必须原子执行,Lua脚本正好保证原子性。实际生产项目里,更推荐直接用Redisson这个客户端库,它把锁续期、自动重试这些细节都封装好了。初识阶段你只需要理解原理,动手造一把简单的分布式锁,能帮自己把分布式场景的思维建立起来。
6.5 给新手的学习路线建议
基于我自己的经验和带人经历,Redis的学习大致可以分三个阶段。第一个阶段就是把这篇内容吃透:理解定位、会安装、熟练操作五种数据类型、能用redis-cli完成常规排查。第二个阶段是进阶核心机制:持久化、过期删除策略、内存淘汰策略、主从与哨兵或集群的搭建和验证。第三个阶段是工程实战:缓存穿透击穿雪崩的治理、分布式锁的落地、Spring Boot整合Redis时的序列化问题、链路追踪与监控告警。
这里特别提一下Spring Boot整合Redis时最容易踩的坑:序列化器不一致。很多人项目里用默认的JdkSerializationRedisSerializer,往Redis写数据时是二进制,用客户端工具查看全是一堆乱码。正确做法是使用StringRedisTemplate,或者给RedisTemplate指定GenericJackson2JsonRedisSerializer作为value序列化器。还有人在用Spring Cache + Redis时,出现“缓存key变化导致命中不了”的情况,多半也是序列化策略没统一。这个话题展开讲能写一整篇,我先在这里埋个引子,后续系列文章会专门处理。
说点我的经验体会
最后聊一点个人带新人时的观察。很多初学者在Redis上栽跟头,根本不是不会用命令,而是不知道“该把什么数据放Redis、用什么类型放、能接受多少数据不一致”。我面试时常问一个很基础的问题:你项目里Redis存了什么?很多人回答“用户信息、验证码”,行,那追问一句“用户信息的修改频率高吗?缓存和数据库不一致能接受几秒?”就卡壳了。
Redis本身不复杂,复杂的是业务判断。你越是能想清楚一个数据该不该进内存、该用什么结构组织、过期时间给多久、挂了之后业务怎么办,你就越接近一个真正有经验的后端开发者。这篇“初识Redis”先把地基给出,后面我们接着往里盖楼。