Redis 是一个基于内存的键值存储,支持丰富的数据结构,常用于缓存、消息队列、分布式锁、排行榜等场景。本文从安装开始,覆盖所有核心数据类型和常见实践。
安装与连接
macOS
brew install redis
brew services start redis # 后台启动
redis-cli # 进入交互式客户端
Linux(Ubuntu / Debian)
sudo apt update && sudo apt install redis-server
sudo systemctl start redis
redis-cli
Docker(最推荐,环境干净)
# 启动容器
docker run -d --name redis -p 6379:6379 redis:7-alpine
# 进入 CLI
docker exec -it redis redis-cli
基础连接与验证
redis-cli -h 127.0.0.1 -p 6379
# 测试连通性
PING # 返回 PONG
# 如果设置了密码
AUTH your_password
# 选择数据库(默认 0,共 16 个)
SELECT 1
# 查看当前库的 key 数量
DBSIZE
# 查看所有 key(生产环境慎用,会阻塞)
KEYS *
# 模糊查找(推荐用 SCAN 替代 KEYS)
SCAN 0 MATCH user:* COUNT 100
数据类型总览
| 类型 | 典型用途 |
|---|---|
| String | 缓存、计数器、分布式锁 |
| Hash | 对象存储(用户信息、商品详情) |
| List | 消息队列、最新动态、操作日志 |
| Set | 去重标签、共同好友、抽奖 |
| ZSet(有序集合) | 排行榜、带权重的队列 |
| Stream | 持久化消息队列(Kafka 轻量替代) |
| Bitmap | 用户签到、布隆过滤器 |
| HyperLogLog | 超大规模 UV 统计(近似值) |
String(字符串)
String 是最基础的类型,可以存文本、数字、二进制,最大 512 MB。
# 存取
SET name "Alice"
GET name # "Alice"
# 设置并指定过期时间(秒)
SET token "abc123" EX 3600
# 只有 key 不存在时才设置(分布式锁的基础)
SET lock "1" NX EX 10
# 批量操作
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3
# 追加内容
APPEND name " Smith" # "Alice Smith"
# 数字操作
SET counter 0
INCR counter # 1
INCRBY counter 5 # 6
DECR counter # 5
DECRBY counter 2 # 3
# 浮点数递增
SET price 9.99
INCRBYFLOAT price 0.5 # 10.49
# 获取字符串长度
STRLEN name
# 获取旧值并设置新值
GETSET name "Bob" # 返回 "Alice Smith",设置为 "Bob"
Hash(哈希)
Hash 适合存储结构化对象,避免频繁序列化/反序列化。
# 设置单个字段
HSET user:1 name "Alice" age 25 email "alice@example.com"
# 获取单个字段
HGET user:1 name # "Alice"
# 获取所有字段和值
HGETALL user:1
# 获取所有字段名
HKEYS user:1
# 获取所有字段值
HVALS user:1
# 获取字段数量
HLEN user:1
# 判断字段是否存在
HEXISTS user:1 email # 1(存在)
# 删除字段
HDEL user:1 email
# 批量获取指定字段
HMGET user:1 name age
# 数字字段自增
HINCRBY user:1 age 1
# 只有字段不存在时才设置
HSETNX user:1 role "admin"
List(列表)
List 是双端链表,支持从两端 push/pop,适合队列和栈。
# 从左侧推入(头部)
LPUSH logs "event1" "event2" "event3"
# 从右侧推入(尾部)
RPUSH queue "task1" "task2"
# 从左侧弹出
LPOP logs
# 从右侧弹出
RPOP queue
# 阻塞弹出(0 表示永久等待,适合消费者场景)
BLPOP queue 0
BRPOP queue 30 # 最多等 30 秒
# 查看范围(0 -1 表示全部)
LRANGE logs 0 -1
# 获取指定下标的值
LINDEX logs 0
# 获取列表长度
LLEN logs
# 修剪,只保留指定范围
LTRIM logs 0 99 # 只保留前 100 条
# 在某个元素前/后插入
LINSERT logs BEFORE "event2" "new-event"
# 移除指定个数的匹配值
LREM logs 2 "event1" # 删除 2 个值为 event1 的元素
经典用法:消息队列
# 生产者
RPUSH queue "msg1"
# 消费者(阻塞式,有消息立即返回)
BLPOP queue 0
Set(集合)
Set 是无序不重复集合,支持集合运算。
# 添加成员
SADD tags "redis" "cache" "nosql"
# 判断是否存在
SISMEMBER tags "redis" # 1
# 获取所有成员
SMEMBERS tags
# 集合大小
SCARD tags
# 移除成员
SREM tags "nosql"
# 随机取出(不移除)
SRANDMEMBER tags 2
# 随机弹出并移除
SPOP tags
# 集合运算
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
SUNION set1 set2 # 并集:a b c d
SINTER set1 set2 # 交集:b c
SDIFF set1 set2 # 差集:a(在 set1 但不在 set2)
# 将运算结果存入新 key
SUNIONSTORE result set1 set2
SINTERSTORE result set1 set2
ZSet(有序集合)
ZSet 中每个成员都有一个分数(score),按分数排序,适合排行榜。
# 添加成员(分数 成员名)
ZADD leaderboard 1000 "Alice" 850 "Bob" 1200 "Charlie"
# 获取成员的分数
ZSCORE leaderboard "Alice" # 1000
# 按分数从低到高排名(0 表示第 1 名)
ZRANK leaderboard "Alice" # 1(0-indexed)
# 按分数从高到低排名
ZREVRANK leaderboard "Alice" # 1
# 按排名范围查询(从低到高)
ZRANGE leaderboard 0 -1 WITHSCORES
# 按排名范围查询(从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES
# 按分数范围查询
ZRANGEBYSCORE leaderboard 800 1100 WITHSCORES
# 分数自增
ZINCRBY leaderboard 100 "Alice" # 1100
# 成员数量
ZCARD leaderboard
# 指定分数范围内的成员数
ZCOUNT leaderboard 900 1200
# 移除成员
ZREM leaderboard "Bob"
# 移除排名范围内的成员
ZREMRANGEBYRANK leaderboard 0 1
# 移除分数范围内的成员
ZREMRANGEBYSCORE leaderboard 0 800
过期时间
# 设置过期时间(秒)
EXPIRE key 3600
# 设置过期时间(毫秒)
PEXPIRE key 3600000
# 设置到具体时间戳(Unix 秒)
EXPIREAT key 1893456000
# 查看剩余时间(秒,-1 表示永不过期,-2 表示 key 不存在)
TTL key
# 查看剩余时间(毫秒)
PTTL key
# 移除过期时间(变为永久)
PERSIST key
持久化
Redis 默认是内存数据库,重启数据会丢失。持久化有两种方式:
RDB(快照)
定期将内存数据写入磁盘,是默认的持久化方式。
# redis.conf 配置(每 60 秒,至少 1 个 key 变更就保存)
save 60 1
save 300 10
save 900 1
# 手动触发快照
BGSAVE # 后台异步保存
SAVE # 同步保存(会阻塞,不推荐生产使用)
优点:文件紧凑,恢复快。
缺点:两次快照之间的数据有丢失风险。
AOF(追加日志)
记录每条写操作命令,重启时重放来恢复数据。
# redis.conf 开启 AOF
appendonly yes
appendfilename "appendonly.aof"
# 同步策略
appendfsync always # 每次写都同步,最安全,性能最低
appendfsync everysec # 每秒同步一次(推荐,最多丢 1 秒数据)
appendfsync no # 由 OS 决定,性能最高,但风险最大
优点:数据安全,最多丢 1 秒。
缺点:文件比 RDB 大,恢复慢。
混合持久化(推荐,Redis 4.0+)
# redis.conf
aof-use-rdb-preamble yes
AOF 文件开头存 RDB 快照,后面追加增量命令,兼顾速度和安全性。
发布 / 订阅
# 订阅频道(会阻塞等待消息)
SUBSCRIBE news sports
# 发布消息
PUBLISH news "Breaking: Redis 8.0 released"
# 模式订阅
PSUBSCRIBE news.*
# 查看订阅信息
PUBSUB CHANNELS # 活跃的频道列表
PUBSUB NUMSUB news # 某频道的订阅者数量
注意:发布订阅是"即发即忘",没有持久化。需要可靠消息队列,推荐使用 Stream 或 Redis + MQ。
事务
Redis 事务通过 MULTI / EXEC 实现,保证命令的原子性执行(但不支持回滚)。
MULTI # 开启事务
SET k1 "v1" # 加入队列
SET k2 "v2"
INCR counter
EXEC # 执行所有命令
# 放弃事务
DISCARD
Watch(乐观锁):
WATCH balance # 监听 key
MULTI
DECRBY balance 100
EXEC # 如果 balance 在 WATCH 之后被其他客户端修改,EXEC 返回 nil
Pipeline(批量请求)
Pipeline 将多条命令打包一次性发送,减少网络往返,大幅提升吞吐量。
# redis-cli 中
redis-cli --pipe << EOF
SET k1 v1
SET k2 v2
INCR counter
EOF
各语言客户端都有 Pipeline 支持,以 Node.js(ioredis)为例:
const pipeline = redis.pipeline()
pipeline.set('k1', 'v1')
pipeline.set('k2', 'v2')
pipeline.incr('counter')
await pipeline.exec()
常见使用场景
缓存
# 旁路缓存模式(Cache Aside)
# 1. 读:先查 Redis,命中直接返回;未命中查数据库,再写入 Redis
# 2. 写:先更新数据库,再删除 Redis 缓存(保证一致性)
SET user:1:profile "{...}" EX 300
分布式锁
# 加锁:NX 保证只有一个客户端能设置成功,EX 避免死锁
SET lock:order:123 "client-uuid" NX EX 30
# 释放锁:必须校验 value 是自己的,再删除(防止误删他人的锁)
# 用 Lua 脚本保证原子性
EVAL "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
" 1 lock:order:123 client-uuid
计数器与限流
# 接口限流:每个用户每分钟最多 100 次
INCR rate:user:123
EXPIRE rate:user:123 60
# 或使用原子操作
SET rate:user:123 0 EX 60 NX # 第一次请求时初始化
INCR rate:user:123 # 每次请求递增
排行榜
# 更新分数
ZINCRBY leaderboard 10 "user:123"
# 获取 Top 10
ZREVRANGE leaderboard 0 9 WITHSCORES
# 查询某用户排名
ZREVRANK leaderboard "user:123"
Session 存储
SET session:abc123 "{userId: 1, role: admin}" EX 86400
GET session:abc123
EXPIRE session:abc123 86400 # 续期
缓存三大问题
缓存穿透
现象: 请求的 key 在缓存和数据库中都不存在,每次都打到数据库。
解决:
- 缓存空值:即使数据库没有,也在 Redis 缓存一个空值,并设置较短过期时间
- 布隆过滤器:请求进来先判断是否可能存在,不存在直接拒绝
缓存击穿
现象: 某个热点 key 过期的瞬间,大量请求同时打到数据库。
解决:
- 热点 key 不过期(或逻辑过期)
- 互斥锁:第一个请求查数据库并重建缓存,其余等待
缓存雪崩
现象: 大量 key 在同一时间集中过期,导致数据库被瞬间压垮。
解决:
- 过期时间加随机值,避免集中过期
- 热点数据永不过期,异步刷新
- 服务降级 + 熔断保护数据库
常用管理命令
# 查看 Redis 信息(内存、连接、持久化等)
INFO
INFO memory
INFO server
# 查看慢查询日志
SLOWLOG GET 10
# 清空当前数据库(慎用)
FLUSHDB
# 清空所有数据库(慎用)
FLUSHALL
# 查看 key 类型
TYPE key
# 删除 key
DEL key1 key2
# 异步删除(大 key 推荐,避免阻塞)
UNLINK key
# 重命名 key
RENAME old-key new-key
# 查看内存使用(单个 key)
MEMORY USAGE key
Fonnpo