CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
CLI 与键命名 redis-cli、KEYS 慎用、命名规范。
redis-cli 实战:学会「看」Redis
这一课你将能做到
熟练使用 redis-cli 完成键的增删改查:SET/GET/DEL/EXISTS/TYPE/RENAME/TTL,并准确解释每个返回值(OK / (nil) / (integer))的含义
用 SCAN 游标方式遍历键空间,用自己的话说清为什么生产环境禁用 KEYS *(单线程阻塞)
按「项目:模块:实体:id」规范设计 key,能用 MEMORY USAGE 查看单个 key 的内存占用,并说出本站三类 key 前缀的分工
为什么要学
上一课你已经能 SET/GET。但真实项目里你面对的不是三五个 key,而是成千上万个。本站生产 Redis 里至少有三类 key 并存:
satoken:login:token:xxxx——每个在线用户一条
rl:203.0.113.7——每个被限流统计的 IP 一条
coderoadmap:tracks:list——业务缓存
不会用命令行查看、筛选、统计这些 key,你就是在蒙眼操作:排查「缓存为什么没生效」,你需要 EXISTS 和 TYPE 确认 key 状态;排查「Redis 内存怎么涨了」,你需要 MEMORY USAGE 定位大 key;批量清理一类数据,你需要 SCAN——而用错 KEYS * 可能把线上 Redis 卡死几秒甚至几十秒,所有接口跟着超时。
redis-cli 是 Redis 的手术刀。本 track 后面每一课的验证步骤都依赖它,这一课把它练熟,后面才不卡壳。
先建立图像
Redis 的键空间可以想成一栋大楼的储物柜,每个柜门贴着唯一标签(key):
db0(默认库)
├─ satoken:login:token:8f3a... → 柜内有值,类型 string,TTL 86400s
├─ rl:203.0.113.7 → string 计数器,TTL 60s
├─ coderoadmap:tracks:list → string(JSON),TTL 300s
└─ user:1001:bookmarks → set
对单个 key 的操作都是「按柜门标签定位」:GET 开柜看货、TYPE 问材质、TTL 看电子标签的倒计时、DEL 整柜拆掉、EXISTS 只看柜子在不在。
而 KEYS 和 SCAN 是两种「巡楼」方式:
KEYS * → 一次性把所有柜门标签抄到一张纸上再返回(巡楼期间整栋楼封锁)
SCAN 0 → 从 0 号门开始,每次只抄一小段,返回「下次从哪继续」的游标(不封锁)
人话:key 像文件路径,冒号像文件夹分隔符。乱起名等于所有文件扔在桌面上,删错就哭。
跟着做
步骤 1:确认 Redis 在跑并进入交互模式
docker start redis-dev 2>/dev/null; docker exec -it redis-dev redis-cli
(用的是第 1 课创建的容器;如果没创建过,先执行 docker run -d --name redis-dev -p 6379:6379 redis:7-alpine。Windows 本机没装 redis-cli 也没关系,docker exec 用容器内的即可。)
步骤 2:键的增删改查基础六连
依次执行,注意观察每个返回值:
SET user:1001:name "xiaolin"
GET user:1001:name
EXISTS user:1001:name
TYPE user:1001:name
RENAME user:1001:name user:1001:nickname
GET user:1001:nickname
DEL user:1001:nickname
GET user:1001:nickname
预期输出依次是 OK、"xiaolin"、(integer) 1、string、OK、"xiaolin"、(integer) 1、(nil)。
要点:EXISTS 返回 1/0;DEL 返回实际删掉的个数 (删不存在的 key 返回 0、不报错);(nil) 表示 key 不存在,不是错误。
步骤 3:体验 KEYS 和 SCAN 的区别 docker exec redis-dev sh -c 'for i in $(seq 1 20); do redis-cli SET "book:$i" "v$i" > /dev/null; done'
KEYS book:*
SCAN 0 MATCH book:* COUNT 5
KEYS 一次返回全部;SCAN 返回「一小批 key + 一个新游标」。把输出里的游标数字填回 SCAN <游标> MATCH book:* COUNT 5 继续扫,直到游标回到 0 为止。亲手走完这个循环,SCAN 才算真的会了。
步骤 4:--raw 解决中文显示 SET greet "你好,Redis"
GET greet
exit
docker exec -it redis-dev redis-cli --raw
GET greet
默认模式下中文显示成 \xe4\xbd\xa0\xe5\xa5\xbd 这样的 UTF-8 字节转义(看起来像乱码,但数据没坏);--raw 按原始字节输出,终端就能正常显示中文。日常排查中文内容一律加 --raw。
步骤 5:MEMORY USAGE 与键空间统计 docker exec redis-dev sh -c 'redis-cli SET big:string "$(yes a | head -n 10000 | tr -d "\n")"'
docker exec -it redis-dev redis-cli
MEMORY USAGE big:string
DBSIZE
MEMORY USAGE 返回该 key 占用的字节数(含元数据开销,10000 字符的字符串大约返回 12000 左右);DBSIZE 返回当前库的 key 总数。生产排查「内存去哪了」就靠这两个命令按前缀逐类摸底。
步骤 6:对照本站的 key 命名规范 satoken:login:token:... ← Sa-Token 框架内部命名(框架决定,别手工改)
rl:{clientKey} ← RedisRequestRateLimiter 里的 "rl:" + key
coderoadmap:tracks:list ← 业务缓存(综合实战课你会亲手写)
命名规范:项目:模块:实体:id,全小写,冒号分层,不用空格、不超长。好处有三个:SCAN MATCH 能按前缀批量定位;一眼看出数据归属;清理脚本永远不会误删别的模块。
讲清楚要点
KEYS 是 O(N) 全量遍历,执行期间阻塞整个 Redis :命令执行是单线程,百万级 key 的 KEYS * 可能卡几秒,期间所有请求排队。生产禁用(运维甚至会在配置里 rename-command KEYS "" 直接废掉它)。
SCAN 是分批游标遍历,不阻塞 :每次只扫一小批(COUNT 只是提示值,返回数量可能多于或少于它),返回新游标,游标回到 0 表示遍历结束。遍历期间 key 有增删时结果「不精确但可用」,对缓存清理场景完全够。
返回值就是协议 :(nil) = 不存在,(integer) 1 = 存在/成功个数,OK = 成功。看返回值调试,比猜快得多。
--raw 是给人看中文和给脚本用的 ;默认转义模式是给协议调试用的,两者不是数据坏了没坏的区别。
RENAME 是原子操作 :可用于无停机换 key;注意 RENAME 到已存在的 key 会直接覆盖目标。
命名规范是防事故的第一道墙 :satoken: / rl: / coderoadmap: 三类前缀分立,保证清理脚本永不误删登录态。
你可能会懵的地方
KEYS 在开发机跑一下也不行吗?
开发机 key 少,跑一下没事。危险的是把「开发机随手 KEYS」的习惯带进生产——肌肉记忆要用 SCAN 练出来。
SCAN 的 COUNT 5 是精确返回 5 个吗?
不是。它是「这一批大概扫多少个桶」的提示,实际返回数量可能多也可能少。判断遍历是否结束只看游标是否回到 0,不看数量。
为什么有 db0~db15 十六个库?能不能当多租户用?
历史遗留设计。官方明确不建议:库无法按业务命名、集群模式下只有 db0。业务隔离靠 key 前缀,不靠库编号——本站只用 db0。
TYPE 都有哪几种返回值?
string / list / set / zset / hash / stream。本站业务缓存全是 string 存 JSON,其他类型出现在框架(Sa-Token)或你主动选用高级结构时(第 4 课)。
EXISTS 调多了会不会慢?
EXISTS k1 k2 k3 一次传多个 key 是 O(N) 但纯内存查找,微秒级。真正要警惕的是 KEYS、SMEMBERS 大集合这类「返回大数据」的命令,阻塞和流量都来自它们。
错法对照 错法 问题 正解 生产环境敲 KEYS * 单线程阻塞,全站请求排队 SCAN 0 MATCH xxx COUNT 100 游标遍历key 命名随手起(t1、temp_list2) 无法批量定位,误删难排查 coderoadmap:track:{slug} 分层命名中文显示乱码以为是数据坏了 只是客户端转义显示 redis-cli --raw 重连用 GET 确认存在再 DEL 两次往返且有竞态 直接 DEL,看返回个数 用 db1~db15 做业务隔离 官方不建议,集群不支持 key 前缀隔离,固定 db0 随手 DEL satoken:*「清理」 把真实用户踢下线 只删自己前缀,satoken 只读
卡点急救
NOAUTH Authentication required :连的实例设了密码。交互模式里先 AUTH 你的密码,或连接时用 redis-cli --askpass(交互输入,避免明文进 shell 历史)。
中文显示成 \xe4\xbd\xa0 :默认协议转义模式,exit 后用 redis-cli --raw 重进。
WRONGTYPE Operation against a key holding the wrong kind of value :对 string key 执行了 list 命令之类。先 TYPE key 看类型再选命令;要换类型先 DEL。
SCAN 一直停不下来 :终止条件是游标返回 0。每次把输出里的新游标填回 SCAN,别每次都从 0 开始。
Connection refused :容器没起或端口没映射。docker ps 确认 0.0.0.0:6379->6379/tcp 在列;不在就 docker logs redis-dev 看报错。
面试常问
为什么生产禁用 KEYS?SCAN 好在哪?
KEYS 一次性 O(N) 遍历,Redis 命令单线程执行,期间所有请求被阻塞;SCAN 分批 + 游标,每批开销小、不阻塞。代价是遍历期间结果不保证精确(可能重复或漏),缓存清理这类场景可以接受。
你们项目的 Redis key 是怎么设计的?
三类前缀:satoken:(Sa-Token 框架会话)、rl:(限流计数,RedisRequestRateLimiter 拼接)、coderoadmap:(业务缓存,如 coderoadmap:tracks:list)。统一规范是 项目:模块:实体:id、全小写冒号分层,便于 SCAN MATCH 批量定位、防止跨模块误删。
怎么定位 Redis 的大 key?
单个 key 用 MEMORY USAGE;全库排查用 redis-cli --bigkeys(内部就是 SCAN,不阻塞),或者离线分析 RDB 文件的 rdb-tools。
自测三问
DEL 一个不存在的 key 返回什么?EXISTS 呢?TTL 对「存在但永不过期」的 key 返回什么?
SCAN 的终止条件是什么?COUNT 参数是精确值还是提示值?
本站三类 key 前缀分别是什么?各自存什么数据?为什么必须分立?
小结一句话 redis-cli 的核心是「按 key 精确操作 + SCAN 游标遍历」:生产永远用 SCAN 代替 KEYS,用分层前缀把键空间管理成可维护的目录树。
延伸
redis-cli 官方文档
SCAN 命令
下一课:String 与计数器 ——SET 的 NX/EX 选项、INCR 原子计数,以及本站限流器 RedisRequestRateLimiter 的逐行源码解读。