Redis 入门:先建立正确认知
Redis 可以理解为“把常用数据放在内存里、用网络命令访问的数据结构服务器”。它常被用作缓存,但并不只是缓存:字符串、哈希、列表、集合、有序集合和 Stream 让它能实现会话、排行榜、计数和事件流。
完成本章后,你应该能够:
- 解释 Redis、key、value、TTL 和数据库编号分别是什么;
- 根据业务选择 String、Hash、List、Set、ZSet 或 Stream;
- 使用
redis-cli完成基本读写并观察返回结果; - 判断一个需求是否适合放入 Redis;
- 理解“Redis 很快”不代表所有命令都安全。
1. 准备一个练习环境
如果本机已经有 Redis,先验证连接:
redis-cli -h 127.0.0.1 -p 6379 ping
预期输出:
PONG
如果尚未安装,可以先用临时容器完成本章实验:
docker run --detach \
--name redis-basics \
--publish 127.0.0.1:6379:6379 \
redis:7.4-alpine
docker ps --filter name=redis-basics
redis-cli ping
本章结束后可执行 docker rm --force redis-basics 清理。这个临时容器没有挂载数据卷,不应用于保存重要数据。
2. 五个必须先记住的概念
- key:数据名称,例如
user:42:profile; - value:key 对应的数据,类型可以是字符串或集合;
- TTL:key 的剩余生存时间,过期后 Redis 自动删除它。
- database:单机 Redis 默认有多个逻辑数据库,编号通常为 0~15;生产建议主要使用 DB 0,并通过 key 前缀隔离业务;
- command:客户端发送给 Redis 的操作,例如
GET、HSET或ZADD。
# 写入一个字符串,EX 60 表示 60 秒后过期
redis-cli SET welcome 'hello redis' EX 60
redis-cli GET welcome
redis-cli TTL welcome
如果 TTL 返回 -1,表示该 key 没有过期时间;缓存类数据大量出现 -1 往往意味着内存会持续增长。返回 -2 表示 key 不存在。
key 命名应能看出业务和对象,推荐使用冒号分层:
业务:对象类型:对象ID:字段
shop:user:42:profile
shop:product:1001:detail
shop:rate-limit:192.168.1.10
不要把完整 SQL、超长 URL 或敏感信息直接放进 key。key 自身也占内存,并会出现在日志、监控和诊断输出中。
3. 动手认识六种常用数据类型
3.1 String:缓存、计数和开关
String 可以保存文本、数字或二进制内容,单个 value 最大可达 512 MB,但生产中不应接近这个上限。
# 缓存商品名称,并设置 5 分钟过期
redis-cli SET 'shop:product:1001:name' '机械键盘' EX 300
redis-cli GET 'shop:product:1001:name'
# 原子计数
redis-cli SET 'shop:page:home:views' 0
redis-cli INCR 'shop:page:home:views'
redis-cli INCRBY 'shop:page:home:views' 10
redis-cli GET 'shop:page:home:views'
预期最终计数为 11。INCR 是原子命令,多个客户端并发执行不会互相覆盖。
3.2 Hash:保存对象字段
redis-cli HSET 'shop:user:42' name 'Alice' level '3' city 'Shanghai'
redis-cli HGET 'shop:user:42' name
redis-cli HGETALL 'shop:user:42'
redis-cli HINCRBY 'shop:user:42' level 1
Hash 适合字段经常单独读取或修改的对象。不要把无上限增长的所有用户都放进一个巨大 Hash,否则迁移、删除和故障恢复都会变得困难。
3.3 List:有序队列
redis-cli LPUSH 'shop:tasks' 'send-email:1001'
redis-cli LPUSH 'shop:tasks' 'send-email:1002'
redis-cli LRANGE 'shop:tasks' 0 -1
redis-cli RPOP 'shop:tasks'
上面的组合形成先进先出队列。List 适合简单任务流,但需要可靠确认、重试和消费组时应使用 Stream 或专业消息队列。
3.4 Set:去重集合
redis-cli SADD 'shop:user:42:tags' vip new-user mobile
redis-cli SADD 'shop:user:42:tags' vip
redis-cli SMEMBERS 'shop:user:42:tags'
redis-cli SISMEMBER 'shop:user:42:tags' vip
第二次添加 vip 返回 0,因为 Set 中元素唯一。
3.5 ZSet:排行榜
redis-cli ZADD 'game:score:daily' 1200 alice 950 bob 1600 carol
redis-cli ZREVRANGE 'game:score:daily' 0 2 WITHSCORES
redis-cli ZINCRBY 'game:score:daily' 700 bob
redis-cli ZREVRANK 'game:score:daily' bob
ZSet 中每个成员有一个 score,Redis 按 score 排序。排名从 0 开始,因此 ZREVRANK 返回 0 表示第一名。
3.6 Stream:可确认的事件流
redis-cli XADD 'orders:events' '*' order_id 1001 status created
redis-cli XADD 'orders:events' '*' order_id 1002 status paid
redis-cli XRANGE 'orders:events' - + COUNT 10
Stream 会为消息生成递增 ID。生产消费还需要消费组、XACK、pending 检查、重试和长度控制;此处只建立基本认识。
4. Redis 适合与不适合什么
| 场景 | 为什么适合 | 需要额外注意 |
|---|---|---|
| 查询缓存 | 内存访问快,可设置 TTL | 必须处理缓存失效和数据库回源 |
| 登录会话 | 多台应用可共享状态 | 设 TTL,登出时删除 |
| 排行榜 | 有序集合可按分数排序 | 限制榜单长度,避免无限增长 |
| 计数/限流 | INCR 是原子操作 | 计数窗口和过期时间要一起设计 |
| Stream 消费 | 有消费组和待确认记录 | 需处理积压、重试和消费者故障 |
Redis 不适合作为唯一的永久主数据存储,也不适合大文件、海量长期对象或复杂关联查询。RDB/AOF 是恢复能力,不是替代主数据库的理由。
一个典型缓存请求是怎样工作的
1. 应用先 GET 缓存 key
2. 命中:直接返回缓存内容
3. 未命中:查询 MySQL
4. 将查询结果 SET 到 Redis,并设置 TTL
5. 返回结果
如果大量请求同时发现一个热门 key 过期,它们可能一起查询数据库,这叫缓存击穿。生产系统需要互斥重建、逻辑过期或请求合并,不能只写简单的 GET/SET。
5. 为什么它快,也为什么它会慢
Redis 使用内存、事件驱动网络模型,核心命令以单线程执行为主,因此避免了大量锁竞争。相反,单条慢命令会阻塞其他客户端:KEYS *、超大集合运算、大 value、长 Lua 脚本都是常见事故源。
新手的第一条规则是:一次命令只处理有限数据;遍历 key 用 SCAN,不用 KEYS。
可以用下面的实验直观看到危险命令的规模效应,但不要在生产执行:
# 只在临时实验实例创建 1000 个 key
for i in $(seq 1 1000); do redis-cli SET "lab:key:$i" "$i" >/dev/null; done
# DBSIZE 是 O(1),适合查看 key 总数
redis-cli DBSIZE
# SCAN 渐进返回,不会一次遍历完整数据库
redis-cli SCAN 0 MATCH 'lab:key:*' COUNT 20
6. 版本与架构选择
新系统选择仍在维护的 Redis 7.x 小版本并固定软件包/镜像标签。单机只适合开发或可重建缓存;需要自动主从切换用 Sentinel;数据或写入超过单机边界时用 Cluster。Valkey 是 Redis 生态的开源替代方案,迁移前须验证客户端、模块和持久化兼容性。
7. 本章检查
你应该能回答:
- TTL 返回
-1和-2分别表示什么? - 用户对象为什么可以用 Hash,而排行榜为什么适合 ZSet?
- 为什么 RDB/AOF 不能自动把 Redis 变成可靠的主数据库?
- 为什么生产环境禁止
KEYS *?
清理本章数据:
redis-cli --scan --pattern 'shop:*'
redis-cli UNLINK \
'shop:product:1001:name' 'shop:page:home:views' 'shop:user:42' \
'shop:tasks' 'shop:user:42:tags'
redis-cli UNLINK 'game:score:daily' 'orders:events'