13 KiB
tags, create time
| tags | create time | |||
|---|---|---|---|---|
|
2026-05-15 18:12 |
Redis 基本命令速查
概述
按使用频率排序的常用命令,标注时间复杂度、典型场景和避坑点。完整命令参考官方文档。
数据类型选型速查
遇到业务需求时,按以下流程选择合适的 Redis 数据结构:
flowchart TD
A["需要存什么?"] --> B{"单值 / 键值对?"}
B -- 是 --> C["String<br/>缓存, 计数器, 分布式锁"]
B -- 否 --> D{"多个字段 / 对象?"}
D -- 是 --> E["Hash<br/>用户信息, 配置, 表单数据"]
D -- 否 --> F{"需要排序 / 排名?"}
F -- 是 --> G["ZSet<br/>排行榜, 延迟队列, 范围查询"]
F -- 否 --> H{"需要去重 / 集合运算?"}
H -- 是 --> I["Set<br/>标签, 共同关注, UV 统计"]
H -- 否 --> J{"需要队列 / 栈?"}
J -- 是 --> K["List<br/>消息队列, 最新列表, 栈"]
J -- 否 --> L["Pub/Sub<br/>广播, 实时通知"]
String — 字符串操作
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
SET key value [EX s] [PX ms] [NX|XX] |
OK / nil | O(1) | 万能设置,EX/PX 设过期,NX 仅不存在时写,XX 仅存在时写 |
GET key |
value / nil | O(1) | 获取值 |
MGET key [key...] |
values array | O(N) | 批量获取 |
SETRANGE key offset value |
length | O(1)* | 覆盖指定偏移量字节(*近似) |
APPEND key value |
new-length | O(1)* | 追加到末尾 |
INCR key |
incremented val | O(1) | 原子 +1 |
INCRBY key increment |
incremented val | O(1) | 原子 +N |
DECRBY key decrement |
decremented val | O(1) | 原子 -N |
STRLEN key |
length | O(1) | 获取字符串长度 |
GETSET key value |
old value | O(1) | 已废弃,用 SET key value NX 替代 |
GETRANGE key start end |
substring | O(N) | 截取子串(end 为 -1 表示末尾) |
SETEX key seconds value |
OK | O(1) | 已废弃,用 SET key value EX seconds 替代 |
PSETEX key milliseconds value |
OK | O(1) | 已废弃,用 SET key value PX milliseconds 替代 |
MSET key value [key value ...] |
OK | O(N) | 原子设置多个键 |
// SET 条件写入——分布式锁的核心
rdb.Set(ctx, "lock:order:"+orderId, "1", 3*time.Second, redis.KeepTTL|redis.OnlyIfAbsent)
// INCR 做限流计数器
rdb.IncrBy(ctx, "rate:user:"+userID, 1)
[!WARNING] MGET ≠ 事务保证 MGET 是并发执行 N 次 GET,不保证原子性。需要原子读多个字段请用
MULTI或 Lua 脚本。
Hash — 哈希操作
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
HSET key field value [field value ...] |
added / changed count | O(N) | 设置一个或多个字段 |
HGET key field |
value / nil | O(1) | 获取单个字段 |
HMGET key field [field ...] |
values array | O(N) | 批量获取多个字段 |
HGETALL key |
all fields | O(N) | 获取全部键值对 ⚠️ 大 hash 慎用 |
HDEL key field [field ...] |
deleted count | O(N) | 删除字段 |
HEXISTS key field |
1 or 0 | O(1) | 判断字段是否存在 |
HINCRBY key field increment |
new value | O(1) | 原子递增字段值 |
HSCAN key cursor MATCH pattern COUNT n |
cursor, entries | O(N) | 安全遍历,不用 HKEYS/HVALS 全量扫 |
// 用户信息缓存——部分更新
pipe := rdb.Pipeline()
pipe.HSet(ctx, "user:42", "email", "new@email.com")
pipe.HSet(ctx, "user:42", "phone", "1380000")
pipe.Exec(ctx) // 管道内原子执行
[!TIP] HGETALL vs HSCAN
HGETALL一步到位获取全部字段,但大 hash(>10K 字段)会阻塞主线程。生产环境推荐HSCAN分片读取。
List — 列表操作
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
LPUSH key elem [elem ...] |
list length | O(N) | 从左侧推入 |
RPUSH key elem [elem ...] |
list length | O(N) | 从右侧推入 |
LPOP key |
element / nil | O(1) | 弹出左侧元素 |
RPOP key |
element / nil | O(1) | 弹出右侧元素 |
LRANGE key start stop |
subarray | O(S+N) | 范围查询 S=start, N=stop-start |
LLEN key |
length | O(1) | 列表长度 |
LINDEX key index |
element | O(N) | 按索引获取 |
LTRIM key start stop |
OK | O(N) | 只保留指定范围(固定队列长度必备) |
BLPOP key [key ...] timeout |
element / nil | O(timeout) | 阻塞式 LPOP |
BRPOP key [key ...] timeout |
element / nil | O(timeout) | 阻塞式 RPOP |
RPOPLPUSH src dst |
element | O(1) | 已废弃,用 LMOVE 替代 |
LMOVE key where1 where2 |
element | O(1) | 弹出一端、push 到另一端(支持 LEFT/RIGHT) |
// BRPOP 消费者模型
val, err := rdb.BRPop(ctx, 5*time.Second, "task_queue").Result()
if err == redis.Nil {
return nil // 超时,无消息
}
[!QUESTION] LPUSH + RPOP 还是 RPUSH + LPOP? 两者性能相同(都是两端操作)。选择取决于语义——"先到的先处理"用 LPUSH+RPOP(FIFO),反之则相反。
Set — 集合操作
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
SADD key member [member ...] |
added count | O(N) | 添加成员 |
SREM key member [member ...] |
removed count | O(N) | 移除成员 |
SMEMBERS key |
all members | O(N) | 获取全部 ⚠️ |
SISMEMBER key member |
1 or 0 | O(1) | 判断成员 |
SCARD key |
cardinality | O(1) | 元素个数 |
SRANDMEMBER key [count] |
member(s) | O(N) | 随机取(不删除) |
SPOP key [count] |
member(s) | O(N) | 随机取并删除 |
SINTER key [key ...] |
intersection | O(N*M) | 交集 ⚠️ 跨节点成本高 |
SUNION key [key ...] |
union | O(N*M) | 并集 |
SDIFF key [key ...] |
difference | O(N*M) | 差集 |
SINTERSTORE dst key [key ...] |
result count | O(N*M) | 交集结果存入新 key |
// 共同关注——用户 A 和用户 B 都关注的人
common := rdb.SInter(ctx, "follow:userA", "follow:userB").Val()
// 抽奖系统——随机取 N 个不重复中奖者
winners := rdb.SPopN(ctx, "entry:lottery", 5).Val()
[!TIP] Set 的典型应用场景
- 去重:UV 统计(
SADD+SCARD)- 交集/并集:共同好友、标签聚合
- 随机抽样:
SPOP/SRANDMEMBER做抽奖或推荐
ZSet —— 有序集合
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
ZADD key score member [score member ...] |
added/changed | O(N log N) | 添加/更新 |
ZSCORE key member |
score / nil | O(1) | 获取分数 |
ZINCRBY key increment member |
new score | O(log N) | 增加分数 |
ZRANGE key start stop [WITHSCORES] |
members | O(log N+M) | 正序范围 |
ZREVRANGE key start stop [WITHSCORES] |
members | O(log N+M) | 倒序范围 |
ZRANGEBYSCORE key min max [WITHSCORES] |
members | O(log N+M) | 分数区间 |
ZRANK key member |
rank / nil | O(log N) | 升序排名 |
ZREVRANK key member |
reverse rank / nil | O(log N) | 降序排名 |
ZCARD key |
count | O(1) | 元素数 |
ZCOUNT key min max |
count | O(log N) | 分数范围内元素数 |
ZREM key member [member ...] |
removed | O(M log N) | 移除 |
ZREMRANGEBYRANK key start stop |
removed | O(log N+M) | 按排名删 |
ZREMRANGEBYSCORE key min max |
removed | O(log N+M) | 按分数删 |
ZMSCORE key member [member ...] |
scores | O(N) | 批量获取分数(7.0+) |
ZPOPMAX key [count] |
top members | O(log N+M) | 弹出最高分 |
ZPOPMIN key [count] |
bottom members | O(log N+M) | 弹出最低分 |
ZINTERSTORE dst numkeys key [numkeys...] |
cardinality | O(N×K+K log K) | 多 ZSet 求交集存新 key |
ZUNIONSTORE dst numkeys key [numkeys...] |
cardinality | O(N×K+K log K) | 多 ZSet 求并集存新 key |
ZRANDMEMBER key [count] [WITHSCORES] |
members | O(N) | 随机取(7.0+) |
ZRANGEBYLEX key min max [LIMIT offset count] |
members | O(log N+M) | 同分数按 member 字典序排 |
ZSCAN key cursor MATCH pattern COUNT n |
cursor, entries | O(N) | 安全遍历 |
// 排行榜——获取 TOP 10
top10 := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 9).Val()
for _, z := range top10 {
fmt.Printf("%s: %.1f\n", z.Member, z.Score)
}
// 分数区间查询——80~100 分的学生
students := rdb.ZRangeByScore(ctx, "score:classA", redis.ZRangeBy{
Min: "80", Max: "100",
}).Val()
[!TIP] 分数边界写法
[包含、(不包含。例如"[80"表示 ≥80,"(100"表示 <100。-inf和+inf表示无穷大。
通用命令
| 命令 | 返回值 | 复杂度 | 说明 |
|---|---|---|---|
DEL key [key ...] |
deleted count | O(N) | N = key 的平均大小,大 key 会阻塞 |
EXPIRE key seconds |
1 or 0 | O(1) | 设过期时间(秒) |
PEXPIRE key milliseconds |
1 or 0 | O(1) | 毫秒级过期 |
TTL key |
seconds / -1 / -2 | O(1) | 剩余存活秒数 |
PTTL key |
milliseconds | O(1) | 剩余存活毫秒 |
EXPIREAT key timestamp |
1 or 0 | O(1) | 按 Unix 时间戳过期 |
KEYS pattern |
matching keys | O(N) | ⛔ 全库扫描,生产禁用! |
SCAN cursor MATCH pattern COUNT n |
cursor, keys | 增量迭代 O(1) | ✅ 安全的模糊查找 |
DBSIZE |
count | O(1) | 当前数据库 key 总数 |
SELECT index |
OK | O(1) | 切换数据库(0~15) |
FLUSHDB |
OK | O(N) | 清空当前库 |
FLUSHALL |
OK | O(N) | 清空所有库 |
TYPE key |
string/hash/list... | O(1) | 查看数据类型 |
MOVE key db |
1 or 0 | O(1) | 移动到其他数据库(集群不支持) |
RENAME key newkey |
OK | O(1) | 重命名(不管类型) |
RANDOMKEY |
key / nil | O(1) | 随机返回一个 key(集群不支持) |
// 优雅删除大 key——避免 DEL 阻塞主线程
keys := []string{"large:key1", "large:key2"}
rdb.Del(ctx, keys...).Err() // 小批量 DEL
// 或者用 SSCAN/HSCAN 分批删除字段
iter := rdb.HScan(ctx, "big:hash", 0, "", 100).Iterator()
for iter.Next(ctx) {
rdb.HDel(ctx, "big:hash", iter.Val()...)
}
[!WARNING] TTL 的陷阱
TTL返回-1表示 key 存在但无过期时间,-2表示 key 不存在- Redis 的过期键删除是惰性删除 + 定期删除混合策略,不能保证设置 EXPIRE 后立即清理
- 集群模式下
MOVE、RANDOMKEY、FLUSHALL不可用
SCAN vs KEYS 深度对比
# ⛔ KEYS — 阻塞整个服务
KEYS user:*
# 100 万 key 中匹配 1000 个 → 主线程阻塞数百毫秒甚至秒级
# ✅ SCAN — 增量迭代,每次 O(1)
SCAN 0 MATCH user:* COUNT 100
# 返回游标 + 一小批匹配 key → 用游标继续,直到游标回到 0
// Go 中的 SCAN 用法
iter := rdb.Scan(ctx, 0, "user:prefix:*", 100).Iterator()
for iter.Next(ctx) {
fmt.Println(iter.Val())
}
// 最后检查 iter.Err()
[!WARNING] SCAN 的不确定性
- 某个 key 可能返回多次(中间被修改或删除)
- 某些匹配的 key 可能不返回
- 因此 不要用 SCAN 做统计计数,只能用于清理或迁移等容错场景
管道 Pipeline vs 事务 Transaction
Redis 协议本身就是 request/response,Pipeline 把多个命令打包一次发送,大幅减少网络往返;而事务通过 MULTI / EXEC 保证一组命令的原子执行。
Pipeline —— 批量发送,减少 RTT
pipe := rdb.Pipeline()
pipe.Set(ctx, "k1", "v1", 0)
pipe.Set(ctx, "k2", "v2", 0)
pipe.Set(ctx, "k3", "v3", 0)
results, err := pipe.Exec(ctx) // 一次性发送,一次性接收
# CLI 模式
redis-cli --pipeline <<< $'SET k1 v1\nSET k2 v2\nSET k3 v3'
[!TIP] Pipeline 不是银弹 单次 batch 控制在 50~200 条为佳。过大反而会增加 RTT 等待时间,且占用服务端协程。
MULTI / EXEC —— 原子化执行
pipe, cancel := rdb.TxPipeline()
defer cancel()
pipe.Set(ctx, "acc:A", "900", 0)
pipe.Set(ctx, "acc:B", "1100", 0)
_, err := pipe.Exec(ctx) // 全部命令依次执行(不被其他命令打断)
# Redis CLI 中的事务
MULTI
> SET acc:A 900
> SET acc:B 1100
EXEC # 原子提交
[!NOTE] Pipeline vs Transaction 对比
维度 Pipeline Transaction (MULTI/EXEC) 目的 减少网络往返(性能) 保证原子性(一致性) 命令失败处理 单个失败不影响其他命令 EXEC前语法错误会取消整个事务WATCH 支持 不支持 支持乐观锁 ( WATCH key)适用场景 批量写入、批量查询 转账、库存扣减等需要原子性的操作
[!WARNING] Redis 事务不是"数据库事务" Redis 事务只保证不中断(没有回滚),中间执行的命令如果出错,后续命令仍会继续。如果需要"出错就回滚"的行为,请使用 Lua 脚本。
关联笔记
- hhs/Redis/02-核心数据类型 — 每种类型的底层编码原理
- hhs/Redis/05-AOF持久化 — 持久化策略对性能的影响
- hhs/Redis/09-高级特性 — Pipeline / Lua / 事务详解