GET key取一个 key 上存的字符串值。key 不存在返回 nil。Redis 用得最多的一条,所有缓存读都走这里。
GET user:1001:name
GET session:abc123
GET feature_flag:dark_mode
Redis 速查表,80+ 命令覆盖字符串/哈希/列表/集合/有序集合/发布订阅/Streams/脚本,带真实例子和坑。
GET key取一个 key 上存的字符串值。key 不存在返回 nil。Redis 用得最多的一条,所有缓存读都走这里。
GET user:1001:name
GET session:abc123
GET feature_flag:dark_mode
SET key value [EX seconds] [NX|XX]把字符串值写到 key 上,可选过期时间。EX 按秒过期,NX 只在不存在时写(原子创建),XX 只在存在时写(原子更新)。
⚠ 常见坑: 不带 EX 的 SET 会把 key 原本的 TTL 抹掉,新值永不过期。想"刷缓存保留 TTL"用 SET key val KEEPTTL(6.0+)或单独再 EXPIRE。
SET user:1001:name "Alice"
SET lock:order:42 worker-7 EX 30 NX
SET cfg:rate_limit 100 XX
DEL key [key ...]删一个或多个 key,返回真正被删的数量。同步操作,删一个百万字段的大 hash 会卡住事件循环。
⚠ 常见坑: 大 key 用 UNLINK 替代,同步从 keyspace 摘掉,内存回收交给后台线程,不卡主线程。
DEL user:1001:cache
DEL k1 k2 k3
UNLINK big_hash -- 大 key 推荐
EXISTS key [key ...]判断一个或多个 key 是否存在。传 N 个 key 时返回存在的数量(同一个 key 传两次算两次)。
EXISTS user:1001
EXISTS k1 k2 k3
EXISTS missing -- 返回 0
EXPIRE key seconds给 key 设置以秒为单位的 TTL。计时到期后 key 被惰性删除,下次访问时或后台清理任务扫到时才真消失。
⚠ 常见坑: 给不存在的 key 设 EXPIRE 静默返回 0,不会报错。设锁时务必检查返回值。
EXPIRE session:abc 3600
EXPIRE cache:home_page 60
PEXPIRE k 500 -- 毫秒
TTL key查 key 离过期还剩多少秒。-2 表示 key 不存在,-1 表示 key 存在但没设过期。
TTL session:abc -- 比如返回 1800
TTL forever_key -- 返回 -1
PTTL k -- 毫秒
PERSIST key清除 key 的 TTL,让它变成永久。没 TTL 或 key 不存在返回 0,成功返回 1。
PERSIST hot_session
PERSIST upgraded_cache_key
KEYS pattern用 glob 模式扫整个数据库的 key(如 user:*)。一次性同步遍历整个 keyspace,生产环境绝不能用。
⚠ 常见坑: 1000 万 key 的库上 KEYS * 能阻塞几秒钟,所有别的客户端全卡死。生产用 SCAN。KEYS 只在本地开发库用。
KEYS user:* -- 仅限本地
KEYS session:*
KEYS *:tmp
SCAN cursor [MATCH pattern] [COUNT n]cursor 增量遍历 keyspace,KEYS 的安全替代。每次扫几个 bucket,返回下次用的新 cursor。cursor 回到 0 就扫完了。
⚠ 常见坑: SCAN 跨轮次可能返回同一个 key 两次,扫描中新增/修改的 key 可能返回也可能不返回。客户端做去重。COUNT 是提示不是限制。
SCAN 0 MATCH user:* COUNT 1000
SCAN 12345 MATCH session:*
while true; do cursor=$(redis-cli SCAN $cursor MATCH 'tmp:*' | head -1); ...; done
TYPE key返回 key 上值的数据类型:string、list、set、zset、hash、stream,key 不存在返回 none。
TYPE user:1001 -- hash
TYPE leaderboard -- zset
TYPE missing -- none
RENAME key newkey原子重命名一个 key。newkey 已存在会被静默覆盖。要"目标存在就失败"用 RENAMENX。
⚠ 常见坑: 集群模式下 RENAME 要求两个 key 在同一个 slot,用 {tag} 强制同槽:RENAME {usr}:1001 {usr}:1001:archived。
RENAME old_key new_key
RENAMENX draft:42 published:42
UNLINK key [key ...]和 DEL 一样删 key,但内存回收交给后台线程,不同步阻塞。任何可能很大的 key 都该用它删。
⚠ 常见坑: 后台释放只在超过一定体积时才触发,小 key 还是同步释放,所以小 key 上 UNLINK 不比 DEL 快。收益全在大 key 上。
UNLINK big_hash
UNLINK session:* 的批量 -- 配合 SCAN 拿到 key 再 UNLINK
COPY source destination [DB n] [REPLACE]把 source 的值复制到 destination(6.2+)。REPLACE 覆盖已存在的目标,DB 复制到另一个库。复制会保留源 key 的 TTL。
COPY user:1001 user:1001:backup
COPY cfg cfg:staging DB 1 REPLACE
DUMP key把 key 的值序列化成 Redis 专用二进制串,RESTORE 能在别处重建。跨实例迁移 key 的工具底层就是这对命令。
⚠ 常见坑: DUMP 串通常不跨大版本通用,RESTORE 会校验内嵌的版本和 CRC,不匹配就拒绝。在相同版本间迁移,或用支持 RESTORE 的迁移工具。
DUMP user:1001
RESTORE user:1001:copy 0 "$serialized" -- 0 表示无 TTL
RANDOMKEY从当前库随机返回一个 key,库为空返回 nil。用于抽样检查 key 形态或估算 TTL 覆盖率。
RANDOMKEY
for i in {1..100}; do redis-cli RANDOMKEY; done | sort | uniq -c -- 采样看前缀分布OBJECT ENCODING key看 Redis 给某个值选的底层编码:listpack、intset、hashtable、skiplist、embstr、int 等。某个 key 比预期吃内存时第一步就查它。
⚠ 常见坑: 小集合初始是紧凑的 listpack/intset,越过 hash-max-listpack-entries 等阈值后会悄悄切成 hashtable/skiplist。切换不可逆,每元素开销大约翻倍。
OBJECT ENCODING leaderboard -- listpack 或 skiplist
OBJECT ENCODING counter -- int
OBJECT IDLETIME key返回 key 有多少秒没被访问过(仅在 LRU 类 maxmemory-policy 下有意义)。手动清理前找冷 key 很好用。
OBJECT IDLETIME old_cache -- 比如 86400 表示一天没碰
OBJECT FREQ hot_key -- LFU 策略下看访问频度
MOVE key db把 key 从当前库移到同实例的另一个编号库。目标库已有同名 key 时不动(返回 0)。
⚠ 常见坑: 集群模式没有编号库,MOVE 在集群里直接报错。多库本身也不推荐,隔离用 key 前缀或独立实例更好。
MOVE session:abc 1
SELECT 1 -- 切到 1 号库验证
INCR key把 key 上存的整数原子加 1。key 不存在先按 0 处理。返回新值。计数器和限流的基石。
⚠ 常见坑: 值不是合法 int64 会报错。"abc" 或超过 int64 上限的字符串 INCR 会失败,计数 key 命名要单独区分开。
INCR page_views:home
INCR rate_limit:ip:1.2.3.4
INCRBY user:42:credits 10
DECR key把 key 上的整数原子减 1。常用于"固定容量资源池"释放名额。
DECR inventory:sku:42
DECRBY queue_depth 5
APPEND key value在 key 末尾追加字符串。key 不存在等价于 SET。返回追加后总长度。
APPEND log:2026-05-26 "request_id=42\n"
APPEND buffer:user:1 chunk
GETSET key value原子地写新值并返回旧值。常用于"重置计数器同时读出旧值",一个 round-trip 搞定。
⚠ 常见坑: 6.2 起被 SET key value GET 取代,语义一样,选项更统一。两条都还能用。
GETSET page_views:home 0
SET counter 0 GET -- 推荐写法
STRLEN key返回 key 上字符串的字节长度。key 不存在返回 0。常用于快速判断缓存是否填好。
STRLEN session:abc -- 比如返回 312
STRLEN empty_key -- 0
SETEX key seconds value一条命令原子写值并设秒级 TTL。等价于 SET key value EX seconds,但少敲两个 token。
SETEX session:abc 3600 user_id=42
SETEX cache:home 60 "<html>..."
SETNX key valuekey 不存在才写入。成功返回 1,已存在返回 0。"先到先得"的经典原语。
⚠ 常见坑: 现代写法推荐 SET key value NX EX ttl,SETNX 没 TTL 选项,单独 EXPIRE 在两条命令间客户端崩了就留下个永不过期的 key。
SETNX lock:order:42 worker-7
SET lock:order:42 worker-7 NX EX 30 -- 推荐
MSET key value [key value ...]一次原子 round-trip 给多个 key 设多个值。要么全部设上,要么(它不会中途失败)一个都不缺,没有半成品 MSET。
MSET user:1:name Alice user:1:age 30
MSETNX a 1 b 2 -- 全部不存在才设,原子
MGET key [key ...]一次 round-trip 取多个字符串值,返回与输入对齐的数组。缺失的 key 在对应位置返回 nil。
⚠ 常见坑: 集群模式下所有 key 必须在同一个 slot,否则 MGET 报 CROSSSLOT。用 {tag} 强制同槽,或客户端按 slot 分发。
MGET user:1:name user:2:name user:3:name
MGET {usr}:1 {usr}:2 -- 集群下用 hash tag 同槽INCRBYFLOAT key increment给 key 的值加一个浮点数,以字符串返回新值。不需要单独的浮点类型就能做"金额加减"这类小数运算。
⚠ 常见坑: 浮点舍入照样存在,0.1 + 0.2 不会精确等于 0.3。算钱请存"分"用 INCRBY,别拿"元"做 INCRBYFLOAT。
INCRBYFLOAT account:42:balance 0.99
INCRBYFLOAT sensor:temp -1.5
SETRANGE key offset value从指定字节偏移开始覆盖写 key 的字符串,偏移超过当前末尾时用 0 字节补齐。把字符串当成定长字节缓冲来用。
SETRANGE packet:42 4 "PAYLOAD"
SETRANGE greeting 6 "Redis" -- 改第 6 字节起的内容
GETRANGE key start end按闭区间字节范围返回 key 字符串的子串,负数从尾算(-1 是最后一字节)。SETRANGE 的读对应命令。
GETRANGE log_line 0 99 -- 前 100 字节
GETRANGE iso_date 0 3 -- 取年份 "2026"
GETDEL key一步原子读出 key 的值并删除(6.2+)。"一次性 token"的干净原语,读到的同时它就没了。
GETDEL otp:user:42 -- 一次性验证码
GETDEL pending_payload:abc
GETEX key [EX seconds | PERSIST]读 key 的值并在同一条命令里调整它的 TTL(6.2+):EX 续期,PERSIST 清除过期。"滑动窗口会话"不用第二次 round-trip。
GETEX session:abc EX 1800 -- 读会话并续 30 分钟
GETEX cache:home PERSIST -- 读并转永久
HSET key field value [field value ...]给 hash 设一个或多个 field。返回新增 field 数量(覆盖不计)。"对象上几个小字段"就用 hash。
⚠ 常见坑: 4.0 起 HSET 支持多 field 一起写,旧 HMSET 已 deprecated。HMSET 还能跑,但新代码别用。
HSET user:1001 name Alice age 30 email alice@x.io
HSET cart:42 sku:1 2 sku:2 1
HGET key field取 hash 上某个 field 的值。field 或 key 不存在返回 nil。
HGET user:1001 name -- "Alice"
HGET cfg dark_mode
HMGET key field [field ...]一次 round-trip 取 hash 多个 field。返回数组,缺失的位置是 nil,顺序与输入对齐。
HMGET user:1001 name age email
HMGET cfg theme lang tz
HDEL key field [field ...]删 hash 上一个或多个 field。返回实际删除数。删到最后一个 field,hash 本身也消失。
HDEL user:1001 stale_flag
HDEL cart:42 sku:1 sku:3
HKEYS key返回 hash 所有 field 名,顺序任意。复杂度 O(N),几十个 field 没事,百万级危险。
HKEYS user:1001
HKEYS cfg
HVALS key返回 hash 所有 value,顺序任意。复杂度同 HKEYS,大 hash 用 HSCAN。
HVALS cfg
HVALS leaderboard_summary
HGETALL key把 hash 所有 field 和 value 平铺返回 [f1, v1, f2, v2, ...]。方便,但大 hash 上是定时炸弹。
⚠ 常见坑: 10 万+ field 的 hash 上 HGETALL 一次能返回几 MB 数据,阻塞事件循环,客户端内存也爆。改用 HSCAN 或把 hash 拆开。
HGETALL user:1001
HSCAN big_hash 0 COUNT 500 -- 安全替代
HINCRBY key field increment原子地给 hash 上某 field 加整数。field 不存在按 0 自动建。"每用户多维计数"的基石。
HINCRBY user:1001:stats login_count 1
HINCRBY user:1001:stats bytes_uploaded 4096
HINCRBYFLOAT user:1001:wallet usd 0.99
HSETNX key field valuefield 不存在才写入。新建返回 1,已存在返回 0。hash 上"按字段先到先得"。
HSETNX user:1001 created_at 1716700000
HSETNX cfg first_seen_version 5
HEXISTS key field判断 hash 上某个 field 是否存在。O(1)。存在返回 1,field 或整个 hash 不在返回 0。
HEXISTS user:1001 email
HEXISTS cfg dark_mode -- 0 表示未设置过
HLEN key返回 hash 的 field 数量。O(1),计数在 hash 头里。key 不存在返回 0。
HLEN user:1001 -- 比如 5 个字段
HLEN cart:42 -- 购物车 SKU 数
HSCAN key cursor [MATCH pattern] [COUNT n] [NOVALUES]cursor 增量遍历 hash,大 hash 上 HGETALL 的安全替代。NOVALUES(7.4+)只返回 field 名,payload 减半。
⚠ 常见坑: 和 SCAN 一样,HSCAN 跨轮次可能重复返回同一个 field,COUNT 只是提示。客户端按集合去重处理。
HSCAN big_hash 0 MATCH attr:* COUNT 500
HSCAN user:1001 0 NOVALUES -- 只要字段名(7.4+)
HRANDFIELD key [count [WITHVALUES]]从 hash 随机返回字段(6.2+)。正 count 返回不重复字段,负 count 允许重复。WITHVALUES 同时带上值。
HRANDFIELD user:1001 -- 随机一个字段
HRANDFIELD quiz_bank 3 WITHVALUES -- 随机抽 3 道题
LPUSH key value [value ...]把一个或多个值压到 list 头(左侧)。list 不存在自动建。返回新长度。配 RPOP 做 FIFO 队列。
LPUSH queue:jobs job_42
LPUSH queue:jobs job_43 job_44 -- 多值
LPUSH log:errors "$timestamp $msg"
RPUSH key value [value ...]把值压到 list 尾(右侧)。配 LPOP 是 FIFO,配 RPOP 是 LIFO。亚毫秒级。
RPUSH queue:jobs job_42
RPUSH timeline:1001 "post:42"
LPOP key [count]从 list 头弹出并返回一个值。带 count(6.2+)可一次弹 N 个。空 list 或缺失返回 nil。
LPOP queue:jobs
LPOP queue:jobs 10 -- 一次取一批
RPOP key [count]从 list 尾弹出并返回一个值。形参与 LPOP 一致,带 count 一次最多弹 N 个。
RPOP queue:jobs
RPOP queue:jobs 100
LRANGE key start stop按下标返回 list 切片。两端闭区间,负数从尾开始(-1 是最后一个)。LRANGE k 0 -1 取全表。
⚠ 常见坑: LRANGE k 0 -1 在百万级 list 上是 O(N),全量传回客户端。务必分页(LRANGE k 0 99,再 100 199,依次)。
LRANGE timeline 0 9 -- 前 10 条
LRANGE timeline -10 -1 -- 最后 10 条
LLEN key返回 list 长度。O(1),头里有计数。key 不存在返回 0。
LLEN queue:jobs -- 比如 42
LLEN missing -- 0
LINDEX key index取 list 指定下标的元素。负数从尾算。O(N),长 list 别在热路径用。
LINDEX timeline 0 -- 第一条
LINDEX timeline -1 -- 最后一条
LREM key count value从 list 删除最多 |count| 个值匹配的元素。count > 0 从头开始,< 0 从尾开始,0 删全部。返回实际删除数。
LREM dedupe_queue 1 job_42 -- 从头删第一个
LREM dedupe_queue 0 job_42 -- 全删
BLPOP key [key ...] timeout阻塞左弹:在给定 list 上最多等 timeout 秒(0 表示永远等),有数据立刻返回 [key, value],超时返回 nil。
⚠ 常见坑: 集群模式下所有 key 必须落同一个 slot,用 {tag}。每个阻塞客户端占一个连接,1 万 worker 就是 1 万长连接,Redis 端要规划。
BLPOP queue:jobs 5
BLPOP queue:high queue:low 0 -- 优先级队列
LSET key index value用新值覆盖 list 指定下标的元素。下标越界报错。负数从尾算。
LSET timeline 0 "post:99" -- 改最新一条
LSET queue -1 "retry:job_42" -- 改最后一个
LTRIM key start stop把 list 裁剪到只保留闭区间内的元素,其余全丢弃。维护"最近 N 条"定长 feed 的标准做法。
⚠ 常见坑: LPUSH 后 LTRIM 是封顶列表的经典写法,但要放进 MULTI 或 Lua,两条命令之间 list 会短暂超出上限,并发读者会看到多出来的条目。
LPUSH feed:1001 "post:99" LTRIM feed:1001 0 99 -- 只留最新 100 条
LTRIM log 0 -1 -- 不变(保留全部)
LINSERT key BEFORE|AFTER pivot value在第一个匹配 pivot 的元素前或后插入一个值。返回新长度,找不到 pivot 返回 -1,key 不存在返回 0。
⚠ 常见坑: LINSERT 从头扫找 pivot,O(N)。长 list 上慢,而且只在第一个匹配处插入,不是所有匹配处。
LINSERT playlist BEFORE "song:5" "song:ad"
LINSERT steps AFTER "build" "test"
RPOPLPUSH source destination原子地从 source 尾弹出并压到 destination 头,返回被移动的元素。"可靠队列"的经典积木。
⚠ 常见坑: 6.2 起被 LMOVE 取代,LMOVE 两边都能选方向。模式:RPOPLPUSH queue processing,活干成功后再从 processing 删,崩了能重放。
RPOPLPUSH queue processing
LMOVE queue processing LEFT RIGHT -- 推荐(6.2+)
BRPOPLPUSH source destination timeoutRPOPLPUSH 的阻塞版:在 source 上最多等 timeout 秒,有元素就原子移到 destination。可靠 worker 队列靠它。
⚠ 常见坑: 6.2 起 deprecated,用 BLMOVE。可靠队列思路:worker BLMOVE jobs→inflight,处理完从 inflight LREM,再由定时巡检把卡在 inflight 太久的重新入队。
BRPOPLPUSH jobs inflight 5
BLMOVE jobs inflight LEFT RIGHT 5 -- 推荐(6.2+)
BRPOP key [key ...] timeout阻塞右弹:在给定 list 尾上最多等 timeout 秒(0 永远等)。返回 [key, value],超时返回 nil。
BRPOP queue:jobs 5
BRPOP queue:high queue:low 0 -- 配 RPUSH 做 FIFO 优先级队列
SADD key member [member ...]给 set 加一个或多个成员。返回新增数(已存在的静默忽略)。set 提供 O(1) 成员判断。
SADD online_users 1001 1002 1003
SADD tags:post:42 redis tutorial cache
SREM key member [member ...]从 set 删一个或多个成员。返回实际删除数。删到最后一个成员,set 本身也消失。
SREM online_users 1001
SREM tags:post:42 deprecated
SMEMBERS key返回 set 全部成员,顺序任意。O(N),"一篇博文的 tag" 几十个没事,几十万的大 set 危险。
⚠ 常见坑: 大 set 用 SSCAN。100 万成员的 set 上 SMEMBERS 一次返回百万字符串,吃掉客户端内存还卡 Redis。
SMEMBERS tags:post:42
SSCAN big_set 0 COUNT 500 -- 大 set 安全替代
SISMEMBER key member判断成员是否在 set 中。O(1)。"按用户的 feature flag、在线状态、封禁 IP" 的基石。
SISMEMBER online_users 1001
SISMEMBER banned_ips 1.2.3.4
SMISMEMBER online_users 1001 1002 1003 -- 批量(6.2+)
SUNION key [key ...]返回多个 set 的并集,任一 set 里出现过的去重成员。常用于"今天或昨天活跃的用户"这类查询。
SUNION active:today active:yesterday
SUNIONSTORE active:48h active:today active:yesterday
SINTER key [key ...]返回多个 set 的交集,在每个 set 都出现的成员。常用于"同时点了广告 A 和广告 B 的用户"这种人群计算。
⚠ 常见坑: 复杂度 O(N*M),N 是最小 set 的大小,M 是 set 数量。最小 set 放第一个,Redis 早期 miss 就剪枝。
SINTER ad_A_clickers ad_B_clickers
SINTERSTORE both_clickers ad_A_clickers ad_B_clickers
SDIFF key [key ...]返回在第一个 set 但不在后续任何 set 的成员。Redis 实现"没看过弹窗的用户"的标准做法。
SDIFF all_users popup_seen
SDIFFSTORE retarget all_users popup_seen unsubscribed
SCARD key返回 set 的成员数。O(1),基数记在 set 头里。key 不存在返回 0。
SCARD online_users -- 当前在线数
SCARD tags:post:42
SPOP key [count]随机移除并返回 set 中的一个或多个成员。和 SRANDMEMBER 不同,SPOP 会改 set。常用于"无放回抽 N 个中奖者"。
SPOP raffle:pool -- 抽一个并移出
SPOP raffle:pool 3 -- 一次抽 3 个不重复
SRANDMEMBER key [count]从 set 随机返回成员但不移除。正 count 返回不重复成员,负 count 允许重复、数量可超过 set 大小。
SRANDMEMBER products:featured 4 -- 随机推荐 4 个
SRANDMEMBER dice:faces -10 -- 模拟 10 次有放回掷骰
SMOVE source destination member原子地把一个成员从一个 set 移到另一个。成功返回 1,成员不在 source 返回 0。一步同时改两个 set。
SMOVE pending approved order:42
SMOVE online away user:1001
SSCAN key cursor [MATCH pattern] [COUNT n]cursor 增量遍历 set,大 set 上 SMEMBERS 的安全替代。每次扫几个 bucket,cursor 回到 0 就完。
SSCAN ip:banned 0 MATCH "10.*" COUNT 500
SSCAN set:huge 0 COUNT 1000
SINTERCARD numkeys key [key ...] [LIMIT n]返回多个 set 交集的大小,但不实际构造交集(7.0+)。LIMIT 数到 n 就提前停,"是否至少有 n 个共同成员"比 SINTER 便宜得多。
SINTERCARD 2 ad_A_clickers ad_B_clickers
SINTERCARD 2 followers:1 followers:2 LIMIT 1 -- 只问有无共同关注
ZADD key [NX|XX] [GT|LT] [CH] score member [score member ...]给 zset 加或更新成员。NX 跳过更新,XX 跳过插入,GT/LT 只在新分数更大/更小时更新。返回新增数(带 CH 时返回变更数)。
ZADD leaderboard 100 alice 95 bob 110 carol
ZADD high_scores GT 200 alice -- 仅当 200 比当前高才更新
ZADD jobs:scheduled 1716700000 job_42 -- 用时间戳当分数
ZRANGE key start stop [WITHSCORES] [REV] [BYSCORE|BYLEX] [LIMIT offset count]zset 范围查询。默认按 rank 升序,REV 反序,BYSCORE 改成分数区间,BYLEX 改成字典序区间。6.2+ 的万能瑞士军刀。
ZRANGE leaderboard 0 9 WITHSCORES -- 前 10
ZRANGE leaderboard 0 9 REV WITHSCORES -- top 10 倒序
ZRANGE leaderboard 100 200 BYSCORE LIMIT 0 50
ZREVRANGE key start stop [WITHSCORES]按分数从高到低返回成员。6.2+ 等价于 ZRANGE ... REV。"top N 排行榜"标准写法。
⚠ 常见坑: 6.2 起 deprecated,新代码用 ZRANGE ... REV。旧命令还能跑,行为完全一致。
ZREVRANGE leaderboard 0 9 WITHSCORES
ZRANGE leaderboard 0 9 REV WITHSCORES -- 推荐
ZRANGEBYSCORE key min max [LIMIT offset count]返回分数在 [min, max] 区间的成员。( 表示开区间。"该跑的定时任务"用时间戳当分数就是这条。
ZRANGEBYSCORE jobs:scheduled -inf 1716700000 -- 时间戳之前
ZRANGEBYSCORE leaderboard (100 200 -- (100, 200]
ZRANGEBYSCORE leaderboard 100 +inf LIMIT 0 50
ZINCRBY key increment member原子地给 zset 成员的分数加 increment。成员不存在按 increment 自动建。
ZINCRBY leaderboard 10 alice
ZINCRBY trending:topics 1 "redis"
ZRANK key member [WITHSCORE]按分数升序返回成员的 0-based 排名。成员不存在返回 nil。ZREVRANK 是高到低排名。
ZRANK leaderboard alice -- 比如 5
ZREVRANK leaderboard alice -- 高到低
ZRANK leaderboard alice WITHSCORE -- 7.2+
ZSCORE key member以字符串返回 zset 中某成员的分数,成员不存在返回 nil。O(1)。ZMSCORE(6.2+)一次取多个分数。
ZSCORE leaderboard alice -- 比如 "110"
ZMSCORE leaderboard alice bob carol -- 批量(6.2+)
ZCARD key返回 zset 的成员数。O(1)。key 不存在返回 0。配 ZCOUNT 数某个分数区间内的成员。
ZCARD leaderboard -- 总参赛人数
ZCOUNT leaderboard 90 100 -- 分数在 [90,100] 的人数
ZREM key member [member ...]从 zset 删一个或多个成员。返回实际删除数。删到最后一个成员,key 消失。排行榜清退的基础。
ZREM leaderboard cheater_42
ZREM jobs:scheduled job_done_1 job_done_2
ZPOPMIN key [count]原子移除并返回分数最低的成员(5.0+),每个带上分数。"最小分数优先队列"的天然弹出。
ZPOPMIN jobs:scheduled -- 取最早该跑的任务
ZPOPMIN delay_queue 10 -- 一次取 10 个
ZPOPMAX key [count]原子移除并返回分数最高的成员(5.0+),每个带分数。BZPOPMIN / BZPOPMAX 是给队列 worker 用的阻塞版。
ZPOPMAX high_scores -- 取当前最高分并移出
BZPOPMIN delay_queue 5 -- 阻塞取最小分数(5.0+)
ZREMRANGEBYSCORE key min max删除所有分数落在 [min, max] 的成员。用时间戳当分数时,这就是滑动窗口集合上"清掉早于某时刻的全部"的一行写法。
ZREMRANGEBYSCORE rate_limit:ip:1.2.3.4 -inf (1716700000 -- 滑窗限流清旧
ZREMRANGEBYRANK leaderboard 0 -101 -- 只留 top 100
ZRANGESTORE dest src min max [BYSCORE|BYLEX] [REV] [LIMIT off count]执行一次 ZRANGE 查询,把结果成员(带分数)存进目标 zset(6.2+)。在服务端固化一份"top N"快照,不用把数据传回客户端。
ZRANGESTORE top10 leaderboard 0 9 REV -- 固化前 10 名
ZUNIONSTORE combined 2 lb:eu lb:us -- 合并两个榜
SETBIT key offset value在 string 上指定偏移设置一个 bit(0 或 1)。偏移超过当前长度时自动用 0 字节补齐。"日活用户位图"的基础。
⚠ 常见坑: SETBIT k 100000000 1 瞬间分配 12.5 MB。注意偏移,user_id*8 和 user_id 写错能炸内存。
SETBIT dau:2026-05-26 1001 1
SETBIT feature:beta 42 1
GETBIT key offset读 string 指定偏移上的 bit(0 或 1)。偏移超过 string 长度返回 0。
GETBIT dau:2026-05-26 1001 -- 1 表示活跃
GETBIT feature:beta 42
BITCOUNT key [start end [BYTE|BIT]]统计 string 中为 1 的 bit 数,可选范围(按字节或按位)。"今天有多少用户活跃"最快的写法。
BITCOUNT dau:2026-05-26 -- 当日总活跃数
BITCOUNT dau:2026-05-26 0 999 BYTE
BITOP AND|OR|XOR|NOT destkey key [key ...]对一个或多个 string 做按位运算,结果写入 destkey。配合每日 DAU 位图可算周留存、流失、N 日活跃人群。
⚠ 常见坑: BITOP 是 O(N),按最长 source 算。1000 万用户的日 DAU 位图是 1.25 MB,OK,但跨一年 OR 一次写入 destkey 超过 450 MB。
BITOP AND retention:7d dau:2026-05-20 dau:2026-05-26
BITOP OR active:week dau:2026-05-20 dau:2026-05-21 ... dau:2026-05-26
BITPOS key bit [start [end [BYTE|BIT]]]找出 string 中第一个为 0 或 1 的 bit 的位置,可选范围。用位图做"找第一个空槽位"分配器时很顺手。
BITPOS slots 0 -- 第一个空闲槽
BITPOS dau:2026-05-26 1 -- 第一个活跃用户的位
BITFIELD key [GET|SET|INCRBY type offset value] [OVERFLOW WRAP|SAT|FAIL]把一个 string 当成任意位宽的有/无符号整数数组,一条原子命令里读写多个字段。OVERFLOW 控制溢出时回绕 / 饱和 / 失败。
⚠ 常见坑: 偏移以类型为单位,除非加 # 前缀。BITFIELD k SET u8 #0 和 BITFIELD k SET u8 0 含义不同,# 会乘上类型位宽。两种混用会写乱布局。
BITFIELD stats INCRBY u8 #0 1 GET u8 #0 -- 第 0 个 u8 加 1 并读回
BITFIELD counter OVERFLOW SAT INCRBY u8 #0 200 -- 饱和到 255 不回绕
PFADD key element [element ...]把一个或多个元素加入 HyperLogLog。估算基数变了返回 1,否则 0。无论加多少元素,固定占 12 KB。
⚠ 常见坑: HyperLogLog 是估算,标准误差约 0.81%。需要精确计数的场景(计费、审计)绝不能用。十亿级 UV / 去重用它正合适。
PFADD uv:2026-05-26 user_1001 user_1002
PFADD uniq_search_terms "redis cheat"
PFCOUNT key [key ...]返回一个 HyperLogLog 的近似基数,多 key 则返回合并后的基数。单 key O(1),合并 O(N)。
PFCOUNT uv:2026-05-26
PFCOUNT uv:2026-05-20 uv:2026-05-21 uv:2026-05-26 -- 多日合并
PFMERGE destkey sourcekey [sourcekey ...]把多个 HyperLogLog 合并到 destkey。幂等,同样的 source 重复合并结果一样。"日 UV 合算月 UV"常用。
PFMERGE uv:2026-05 uv:2026-05-01 uv:2026-05-02 ... uv:2026-05-31
PFADD vs Set memoryHyperLogLog 数唯一元素,无论加多少都固定约 12 KB,而存同样成员的 Set 会线性增长。用精确性换恒定内存。
⚠ 常见坑: 如果你将来要拿到真实成员(不只是数量),就不能用 HLL,它是单向的。需要枚举才留真 Set,只算基数就用 HLL。
PFADD uv:2026-05-26 u1 u2 u3
MEMORY USAGE uv:2026-05-26 -- 约 12KB 上限
PUBLISH channel message向 channel 上每个 SUBSCRIBE 的客户端广播消息。返回接收的订阅者数。Fire-and-forget,不持久化,不可重放。
⚠ 常见坑: PUBLISH 时不在线的订阅者永远收不到,不缓冲。要至少一次送达用 Streams(XADD + XREADGROUP)。
PUBLISH events:user_signup user_1001
PUBLISH chat:room:42 "hello"
SUBSCRIBE channel [channel ...]订阅一个或多个 channel。连接进入订阅模式,UNSUBSCRIBE 前大多数普通命令禁用。
SUBSCRIBE events:user_signup
SUBSCRIBE chat:room:42 chat:room:43
PSUBSCRIBE pattern [pattern ...]按 glob 模式订阅所有匹配的 channel。每条消息会带上实际命中的 channel 名和命中的 pattern。
⚠ 常见坑: PSUBSCRIBE chat:* 命中所有 chat:N,10 个房间 OK,1000 万个会卡。每次 PUBLISH 都过 pattern,复杂度 O(patterns * subscribers)。
PSUBSCRIBE chat:*
PSUBSCRIBE events:*
PUBSUB CHANNELS [pattern]列出当前至少有一个订阅者的活跃 channel,可按 glob 过滤。"现在谁在听哪个频道"的自省命令。
PUBSUB CHANNELS
PUBSUB CHANNELS chat:*
PUBSUB NUMSUB chat:42 -- 某频道订阅者数
SPUBLISH shardchannel message在 Redis 集群里向分片 channel 发布(7.0+)。和普通 PUBLISH 不同,分片 pub/sub 按 slot 路由,不向每个节点广播,因此能随集群扩展。
⚠ 常见坑: 集群里普通 PUBLISH 会把消息扇出到每个节点,不可扩展。集群高吞吐 pub/sub 用 SPUBLISH + SSUBSCRIBE,流量留在归属分片上。
SPUBLISH orders:shard "new_order"
SSUBSCRIBE orders:shard -- 分片订阅(7.0+)
XADD key [MAXLEN [~|=] N] * field value [field value ...]向 stream 追加新条目。* 表示由 Redis 分配 ID(毫秒时间戳 + 序号)。MAXLEN 配 ~ 是近似截断,配 = 是精确截断。Streams 是 Pub/Sub 的持久可重放替代品。
⚠ 常见坑: 不带 MAXLEN 的 stream 会无限增长吃光内存。生产 stream 一定要封顶(MAXLEN ~ 1000000)或定期 XTRIM。~ 比 = 便宜得多。
XADD events:orders * user 1001 sku 42 qty 2
XADD events:orders MAXLEN ~ 100000 * user 1001 sku 42
XREAD [COUNT n] [BLOCK ms] STREAMS key [key ...] id [id ...]读多个 stream 中给定 ID 之后的新条目。$ 表示"仅我开始等之后新加的"。BLOCK 0 永远等。
XREAD COUNT 100 STREAMS events:orders 0
XREAD BLOCK 5000 STREAMS events:orders $
XREADGROUP GROUP grp consumer [COUNT n] [BLOCK ms] STREAMS key id消费者组读,Redis 按 consumer 跟踪投递,至少一次语义。ID = > 表示"该 consumer 还没看过的新消息"。处理完配 XACK。
⚠ 常见坑: 先用 XGROUP CREATE 建组,组不存在时 XREADGROUP 报错。consumer 崩了用 XPENDING + XCLAIM 把它待处理的消息转给别人。
XGROUP CREATE events:orders processors $ MKSTREAM
XREADGROUP GROUP processors worker-1 COUNT 100 BLOCK 5000 STREAMS events:orders >
XACK key group id [id ...]告诉消费者组某些条目已处理。不 XACK 的条目留在 pending list 里,崩溃后可以被重新分派。
XACK events:orders processors 1716700000-0
XLEN key返回 stream 当前条目数。O(1)。配 XINFO STREAM key 看完整元数据(首尾条目、消费组、长度)。
XLEN events:orders
XINFO STREAM events:orders
XRANGE key start end [COUNT n]返回 ID 落在闭区间 [start, end] 的 stream 条目。- 和 + 表示最小和最大可能 ID,所以 XRANGE k - + COUNT 10 读最旧的 10 条。
XRANGE events:orders - + COUNT 10 -- 最旧 10 条
XREVRANGE events:orders + - COUNT 10 -- 最新 10 条
XPENDING key group [IDLE ms] [start end count [consumer]]查看投递给消费组但还没 XACK 的条目。简要形式给出待处理总数和各 consumer 的计数,扩展形式列出具体卡住的条目。
⚠ 常见坑: XPENDING 数持续增长说明 consumer 在崩或太慢,条目会一直堆到被认领为止。配一个巡检进程,XCLAIM 空闲超阈值的条目,把毒消息转到死信 stream。
XPENDING events:orders processors -- 概览
XPENDING events:orders processors IDLE 60000 - + 100 -- 卡超过 60s 的
XCLAIM key group consumer min-idle-time id [id ...]把待处理且空闲至少 min-idle-time 的条目转给另一个 consumer。worker 处理到一半挂掉时的恢复原语。
⚠ 常见坑: XAUTOCLAIM(6.2+)通常更省事:一次调用扫描、认领并返回 cursor,不用先 XPENDING。务必给重试次数封顶,反复失败的条目应转入死信 stream。
XCLAIM events:orders processors worker-2 60000 1716700000-0
XAUTOCLAIM events:orders processors worker-2 60000 0 -- 推荐(6.2+)
XGROUP CREATE key group id [MKSTREAM]在 stream 上建消费组,从给定 ID 开始($ 仅新条目,0 从头)。MKSTREAM 在 stream 还不存在时自动创建。
⚠ 常见坑: 不带 MKSTREAM 时,对不存在的 stream 建组会报错。从 $ 开始意味着组会忽略所有已有条目,要处理积压就从 0 开始。
XGROUP CREATE events:orders processors $ MKSTREAM
XGROUP CREATECONSUMER events:orders processors worker-3 -- 显式建消费者
XTRIM key MAXLEN|MINID [~|=] threshold按长度(MAXLEN)或最小 ID(MINID,6.2+)裁剪 stream。~ 是近似裁剪,便宜得多,因为只整块丢弃宏节点。
XTRIM events:orders MAXLEN ~ 1000000
XTRIM events:orders MINID 1716700000000 -- 丢弃此时间戳前的(6.2+)
EVAL script numkeys key [key ...] arg [arg ...]原子执行一段 Lua 脚本。所有 key 必须通过 numkeys + KEYS[] 声明。脚本沙箱里运行,单线程,每一行都会阻塞服务器。
⚠ 常见坑: 长脚本阻塞所有别的客户端。硬上限是 lua-time-limit(默认 5 秒),超时只能 SCRIPT KILL 或 SHUTDOWN NOSAVE 恢复。脚本控制在几条命令以内。
EVAL "return redis.call('GET', KEYS[1])" 1 mykeyEVAL "local v = redis.call('INCR', KEYS[1]); if v > tonumber(ARGV[1]) then redis.call('DEL', KEYS[1]); return 0 end; return v" 1 rate_limit 100EVALSHA sha1 numkeys key [key ...] arg [arg ...]按 SHA-1 摘要执行已缓存的脚本。每次调用从整段脚本压缩到 40 个十六进制字符。遇 NOSCRIPT 错误回退到 EVAL。
EVALSHA a9d8b1c... 1 rate_limit 100
-- 通常配合 SCRIPT LOAD: SHA=$(redis-cli SCRIPT LOAD "$src"); redis-cli EVALSHA $SHA ...
SCRIPT LOAD script把脚本缓存到服务器并返回 SHA-1。不会执行。常用于启动时预热,之后一直用 EVALSHA。
⚠ 常见坑: FLUSHALL 或重启会清掉脚本缓存,每个客户端要能在 NOSCRIPT 时重新 LOAD。集群模式下要在每个分片单独 LOAD。
SCRIPT LOAD "return 1" -- 返回 e0e1f9fabfc9d4800c877a703b823ac0578ff831
FCALL function numkeys key [key ...] arg [arg ...]调用已加载 Redis Functions 库里的具名函数(7.0+)。Functions 是 EVAL 脚本的持久化继任者,按名注册,随 RDB/AOF 持久化并复制。
⚠ 常见坑: 和 EVAL 脚本不同(FLUSHALL/重启就没),FUNCTION LOAD 的库会在重启后保留并自动复制到副本。把高频 EVALSHA 路径迁到 FUNCTION,运维省心。
FUNCTION LOAD "#!lua name=mylib\nredis.register_function('myfunc', function(keys, args) return redis.call('GET', keys[1]) end)"FCALL myfunc 1 mykey
SCRIPT EXISTS sha1 [sha1 ...]按 SHA-1 检查一个或多个脚本是否已缓存在服务器上,返回 1/0 数组。客户端据此决定走 EVALSHA 还是重新 LOAD。
SCRIPT EXISTS a9d8b1c... -- [1] 已缓存
SCRIPT FLUSH -- 清空整个脚本缓存
MULTI开启事务块。之后的命令进队列(返回 QUEUED)而非立即执行,直到 EXEC 或 DISCARD。MULTI/EXEC 块原子执行。
MULTI INCR a INCR b EXEC
EXEC原子执行 MULTI 后入队的所有命令,返回回复数组。被 WATCH 的 key 期间被改了,返回 nil 整个事务取消。
⚠ 常见坑: Redis 事务不是 SQL 那种"错了回滚"。块里某条命令出错(比如类型不对),其它命令照样执行。EXEC 只是把每条结果返回。
MULTI SET a 1 SET b 2 EXEC -- 返回 ["OK", "OK"]
DISCARD丢弃排队的命令并退出 MULTI 模式,不执行。连接回到普通命令模式。
MULTI SET a 1 DISCARD -- a 不变
WATCH key [key ...]乐观锁原语。先 WATCH 几个 key,再 MULTI/EXEC,WATCH 到 EXEC 之间任何 WATCH 的 key 被改,事务取消。Redis 的 CAS 模式。
WATCH balance val = GET balance if val >= 100: MULTI DECRBY balance 100 EXEC
UNWATCH清掉当前连接上 WATCH 的所有 key,但不执行事务。EXEC 和 DISCARD 本身会隐式 UNWATCH,只有放弃一次 CAS 尝试时才显式调它。
⚠ 常见坑: 常见 CAS 泄漏:WATCH 了 key,读完决定不写,却忘了 UNWATCH,该连接下一次 MULTI/EXEC 就被这个过时的 watch 无谓地卡住。提前返回的分支务必 UNWATCH。
WATCH balance -- 读后决定不改 UNWATCH
WATCH a b c -- 多 key 乐观锁
SAVE同步把数据集落盘为 RDB。阻塞服务器全程,10 GB 数据要几分钟。生产环境几乎从不该用。
⚠ 常见坑: 非阻塞 RDB 用 BGSAVE。SAVE 只为灾难恢复脚本那种"明确需要服务器停顿一致点"的场景而存在。
SAVE -- 一般别用
BGSAVE -- 推荐
BGSAVEfork 出后台进程写 RDB 快照,不阻塞主线程。但 fork 本身在大实例上因 copy-on-write 页表成本可能也慢。
⚠ 常见坑: 64 GB 实例 + 高写入下,fork 可能要几秒,子进程跑的时候常驻内存会翻倍。BGSAVE 放低峰期。
BGSAVE
BGSAVE SCHEDULE -- 7.0+ 排队避免冲突
LASTSAVE返回最近一次 RDB 成功保存的 UNIX 时间戳。健康检查会拿它和 now() 比,发现后台保存卡住。
LASTSAVE -- 比如返回 1716700000
SLAVEOF host port把当前实例配为指定 master 的副本,同步数据集并跟随主的复制流。已 deprecated,用 REPLICAOF。
⚠ 常见坑: 5.0 起改名 REPLICAOF。两条都还能跑,新代码、新配置用 REPLICAOF。
SLAVEOF master.internal 6379
SLAVEOF NO ONE -- 解除从属变独立实例
REPLICAOF host portSLAVEOF 的现代别名(5.0+)。REPLICAOF NO ONE 切断主从关系,提升为独立主。
REPLICAOF master.internal 6379
REPLICAOF NO ONE -- 提升为主
BGREWRITEAOF在后台重写 AOF,压缩成能复现当前数据集的最小命令集。让 AOF 回放保持快、文件不至于无限膨胀。
⚠ 常见坑: Redis 会按 auto-aof-rewrite-percentage 自动重写,但写入高峰可能跑赢它。盯 INFO persistence 里的 aof_pending_rewrite 和 aof_current_size,文件膨胀前手动触发。
BGREWRITEAOF
INFO persistence -- 看 aof_current_size / aof_base_size
WAIT numreplicas timeout阻塞直到本连接发出的写被至少 numreplicas 个副本确认,或到 timeout 毫秒为止。在异步复制之上换取更强的持久性。
⚠ 常见坑: WAIT 不是事务,副本没达标也不回滚,它只告诉你有多少个确认了。Redis 默认异步复制,WAIT 缩小丢数据窗口但消不掉它。
WAIT 1 100 -- 至少 1 个副本确认,最多等 100ms
WAIT 2 1000 -- 多数派确认
FAILOVER [TO host port] [ABORT] [TIMEOUT ms]从主到它某个副本触发协调式、低数据丢失的故障切换(6.2+)。和 Sentinel 驱动的切换不同,主会暂停写入并干净地交接。
FAILOVER -- 自动选副本
FAILOVER TO replica.internal 6379
FAILOVER ABORT -- 中止进行中的切换
CLIENT LIST列出所有已连接客户端的地址、连接时长、空闲时间、最近一条命令、标志。排查连接泄漏和"谁在打爆我们"必备。
CLIENT LIST
CLIENT LIST TYPE pubsub -- 仅 pub/sub 客户端
CLIENT KILL ADDR 1.2.3.4:5678 -- 踢掉某客户端
INFO [section]以纯文本返回服务器统计:内存、复制、客户端、持久化、CPU、keyspace、命令统计。最有用的诊断命令。
INFO -- 全量
INFO memory
INFO replication
INFO commandstats -- 各命令调用次数和耗时
CONFIG GET parameter读一个或多个服务器配置项。支持 glob(CONFIG GET maxmemory*)。托管 Redis(如 ElastiCache)上只读。
CONFIG GET maxmemory
CONFIG GET maxmemory-policy
CONFIG GET save
CONFIG SET parameter value运行时改一个配置项,不重启。改动只在内存,配 CONFIG REWRITE 才会写回 redis.conf。
⚠ 常见坑: 内存已满的实例上 CONFIG SET maxmemory-policy noeviction 会立刻开始拒绝写入。生产环境改之前确认新策略是你想要的。
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG REWRITE -- 落盘
FLUSHALL [ASYNC|SYNC]删除所有 database 里的所有 key。ASYNC(4.0+)后台回收内存。"最快毁掉一个生产周末"的命令。
⚠ 常见坑: 生产环境在 redis.conf 里改名或禁掉:rename-command FLUSHALL ""。只清当前库用 FLUSHDB。没近期的 RDB/AOF 两条都不可逆。
FLUSHALL ASYNC -- 生产用 ASYNC
FLUSHDB -- 仅当前库
DBSIZE返回当前选中库的 key 数量。O(1),Redis 维护着这个计数,不像 KEYS 要遍历。"这个库多大"的安全问法。
DBSIZE -- 比如 1240392
SELECT 1 DBSIZE -- 看 1 号库
MEMORY USAGE key [SAMPLES n]估算一个 key 连同它的值占用的总字节,包含内部开销。揪出"吃掉大部分内存的少数几个 key"的正确工具。
⚠ 常见坑: 对聚合类型它是抽样嵌套元素而非全量测量,分布不均的集合上调大 SAMPLES(或用 0 求精确)。配 MEMORY DOCTOR 看一句话健康摘要。
MEMORY USAGE user:1001 -- 字节数
MEMORY USAGE big_zset SAMPLES 0 -- 精确测量
MEMORY DOCTOR
SLOWLOG GET [count]返回最近执行耗时超过 slowlog-log-slower-than 微秒的命令。延迟突刺时第一个看的地方,它点出具体哪条慢命令和它的参数。
⚠ 常见坑: Slowlog 只测命令本身的执行时间,不含请求在队列里排在慢命令后面的等待。一条快命令被记成"慢",通常是被前面的 KEYS 或大 Lua 脚本堵住了。
SLOWLOG GET 10 -- 最近 10 条慢命令
SLOWLOG RESET -- 清空
CONFIG SET slowlog-log-slower-than 10000 -- 阈值 10ms
CLIENT NO-EVICT on|off让当前连接免于客户端输出缓冲驱逐(7.0+),这样关键的运维或监控客户端不会在内存压力下被踢。少用。
CLIENT NO-EVICT on
CLIENT NO-TOUCH on -- 读不更新 LRU/LFU(7.2+)
CLIENT SETNAME monitor-1
COMMAND DOCS [command ...]返回命令的结构化文档:摘要、参数、复杂度、标志。COMMAND COUNT 给总数,COMMAND INFO 返回参数个数和 key 位置元数据,客户端用它做路由。
COMMAND COUNT -- 服务器支持的命令总数
COMMAND INFO get set
COMMAND DOCS hset
CLUSTER INFO报告 Redis 集群健康:状态(ok / fail)、16384 个 hash slot 分配了多少、已知和可达节点数。集群异常时第一个看。
⚠ 常见坑: cluster_state:fail 表示有 slot 没分配,集群拒绝触及这些 slot 的查询。CLUSTER SLOTS / CLUSTER SHARDS 给出 slot 到节点的映射,用来定位缺口。
CLUSTER INFO
CLUSTER SLOTS
CLUSTER KEYSLOT user:1001 -- 这个 key 落哪个槽
KEYS in production生产环境 KEYS *(或任何 pattern)会同步遍历整个 keyspace,把 Redis 卡几秒。一律用 SCAN。
⚠ 常见坑: 即使带 pattern 的 KEYS user:* 也会遍历所有 key 逐个匹配。成本和总 keyspace 成正比,不是和命中数。SCAN 增量执行,bucket 间会让出。
KEYS * -- 不要这样
SCAN 0 MATCH user:* COUNT 500 -- 正确做法
SCAN may miss or duplicateSCAN 保证整轮遍历里"从头到尾都存在"的 key 至少返回一次,但扫描中新增/删除的 key 可能漏报或重复返回。客户端做去重。
# 用 Set 在客户端去重 seen = set() cursor = 0 while True: cursor, batch = r.scan(cursor) for k in batch: seen.add(k) if cursor == 0: break
HGETALL on big hash10 万+ field 的 hash 上 HGETALL 一次返回几 MB,阻塞 Redis 事件循环和客户端。改 HSCAN,或把 hash 拆成 N 个小 hash。
HSCAN big_hash 0 COUNT 500
-- 按 hash_tag 拆分: user:1001:profile, user:1001:prefs, user:1001:cache
Expired keys not freed immediatelyTTL 到期 key 不是立刻释放,Redis 用惰性过期(下次访问时删)加概率性后台清理。内存中可能包含"已过期但还没释放"的 key。
⚠ 常见坑: INFO memory used_memory 比所有 key 体积之和大是正常的。差距太大就调高 hz(CONFIG SET hz 100)让后台清理更激进,或接受一点延迟。
DEBUG SLEEP 0 -- 触发一次访问帮过期
CONFIG SET hz 100 -- 默认 10,调到 100 更激进
Memory fragmentation长跑的 Redis 实例 jemalloc 碎片会累积,used_memory_rss 能到 used_memory 的 1.5-2 倍。看 INFO memory 里的 mem_fragmentation_ratio,> 1.5 就该处理。
⚠ 常见坑: 运行时打开主动碎片整理:CONFIG SET activedefrag yes,在不停服情况下处理。老办法,带持久化重启,也行,但会有一个 failover 窗口。
INFO memory -- 看 mem_fragmentation_ratio
CONFIG SET activedefrag yes
AOF vs RDB trade-offsRDB 是某时刻快照,文件小、加载快,崩溃可能丢几分钟。AOF 记录每个写,文件大、回放慢,持久度可调(每写/每秒/OS 决定)。
⚠ 常见坑: 生产默认:两个都开。RDB 用于快速恢复和异地备份,AOF(appendfsync everysec)保证崩溃最多丢 1 秒。AOF 要定期 BGREWRITEAOF 压缩。
CONFIG SET appendonly yes
CONFIG SET appendfsync everysec
BGREWRITEAOF -- 压缩 AOF
Cache stampede on hot key一个热门缓存 key 过期的瞬间,所有并发请求同时 miss,一起冲击数据库重算同一个值,形成惊群。一次慢的后端查询变成成千上万次。
⚠ 常见坑: 缓解:用短时的按 key 锁(SET lock NX EX),只让一个请求重算,其余读旧值或等待;或在过期前一点提前刷新(概率性提前过期)。给 TTL 加抖动,别让 key 集体同时过期。
SET lock:recompute:home worker-7 NX EX 5 -- 只放一个进来重算
EXPIRE cache:home 60 -- 加随机 +rand(0,10) 防同时过期
Hot key / single slot bottleneck在集群里,对某个超热 key 的每次操作都落到同一个分片,导致那个节点打满、其余空闲。全局计数器或明星用户就是典型热 key。
⚠ 常见坑: 把热 key 拆成 N 个带后缀的子 key(counter:0..counter:9),读时求和;或在客户端本地加一层短 TTL 缓存。hash tag 决定同槽,别不小心把所有东西 tag 进同一个 slot。
INCR counter:{shard:$((RANDOM%10))} -- 写打散到 10 个子 keyCLUSTER KEYSLOT "{user}:1001" -- 确认 hash tag 落槽Big keys block the event loopRedis 命令执行是单线程的,所以一个对巨型 key 的 O(N) 操作(百万字段 hash 上 HGETALL、大 set 上 DEL)会在整个过程中卡住所有别的客户端。
⚠ 常见坑: 离线用 redis-cli --bigkeys 或 --memkeys 找大 key(它们是抽样,不阻塞)。用 UNLINK 删、用 *SCAN 系列读,设计 schema 时别让单个 key 无限增长。
redis-cli --bigkeys -- 扫描找大 key
redis-cli --memkeys -- 按内存找
UNLINK the_big_key
maxmemory-policy eviction surprises内存触到 maxmemory 时的行为完全取决于 maxmemory-policy。noeviction 用 OOM 错误拒绝写入;allkeys-lru 驱逐任意 key;volatile-lru 只驱逐设了 TTL 的 key。
⚠ 常见坑: volatile-* 策略下若没有 key 带 TTL,行为等同 noeviction,写入开始失败却没 key 被驱逐。要靠 LRU 驱逐,要么给缓存 key 设 TTL,要么用 allkeys-* 策略。
CONFIG GET maxmemory-policy
CONFIG SET maxmemory-policy allkeys-lru -- 纯缓存场景
INFO stats -- 看 evicted_keys 是否在涨
EXPIRE on replica does not delete副本不会独立过期 key,它们等主在 key 过期时发来显式 DEL。在副本上读,可能短暂读到一个逻辑上已过期的 key。
⚠ 常见坑: 从 Redis 3.2 起,副本读到逻辑已过期的 key 会返回 nil(即便 DEL 还没到),正确性没问题,但在主传来删除前,该 key 仍占副本内存。别假设副本内存和主完全一致。
TTL session:abc -- 主与副本上结果可能短暂不同
INFO keyspace -- 对比主从 expires 计数
Numeric strings and float precisionRedis 把数字按字符串存,每条命令各自解析。计数器超过 2^63-1 会让 INCR 溢出;一连串 INCRBYFLOAT 会像任何浮点运算一样累积 IEEE-754 舍入误差。
⚠ 常见坑: 算钱别对"元"用 INCRBYFLOAT,存"分"用 INCRBY。超大计数器要盯 int64 上限,或把计数器分片让单个 key 不溢出。
INCRBY wallet:42:cents 99 -- 存分,整数
INCR big_counter -- 接近 9.22e18 时会溢出报错
可搜索的 Redis 速查表,覆盖后端、SRE、值班同学日常真在 redis-cli 里敲的 80+ 条命令,不是凑数的 "SET foo bar" 入门篇。十五大分类:键(GET / SET / DEL / EXISTS / EXPIRE / TTL / PERSIST / KEYS 与 SCAN / TYPE / RENAME), 字符串(INCR / DECR / APPEND / GETSET 和 SET ... GET / STRLEN / SETEX / SETNX 与 SET NX EX 对比),哈希 (HSET 多字段、HGET、HMGET、HDEL、HKEYS、HVALS、HGETALL 的危险、HINCRBY / HINCRBYFLOAT),列表当队列(LPUSH / RPUSH / LPOP / RPOP / LRANGE / LLEN / LINDEX / LREM / BLPOP 做阻塞消费),集合(SADD / SREM / SMEMBERS / SISMEMBER / SUNION / SINTER / SDIFF,附 SINTER 交集顺序 的小技巧),有序集合(ZADD 带 NX/XX/GT/LT,6.2+ 的 ZRANGE 万能瑞士军刀,ZRANGEBYSCORE 配时间戳实现定时任 务,ZINCRBY,ZRANK 带 WITHSCORE),位图做紧凑日活标记 (SETBIT / GETBIT / BITCOUNT / BITOP AND/OR/XOR/NOT), HyperLogLog 概率基数(PFADD / PFCOUNT / PFMERGE),发布 订阅 fire-and-forget(PUBLISH / SUBSCRIBE / PSUBSCRIBE) 以及什么时候该改用 Streams,Streams 5.0+ 持久可重放日志 (XADD 带 MAXLEN,XREAD 配 BLOCK $,XREADGROUP 实现至少 一次消费组,XACK,XLEN),Lua 脚本(EVAL、EVALSHA、 SCRIPT LOAD 配 NOSCRIPT 回退),事务(MULTI、EXEC、 DISCARD、WATCH 乐观锁,以及 Redis 事务不会回滚出错命 令的事实),持久化与复制(SAVE 与 BGSAVE、LASTSAVE 做健 康检查、SLAVEOF 已 deprecated 用 REPLICAOF、AOF 与 RDB 的取舍),服务器与客户端运维(CLIENT LIST 排查"谁在打爆 我们"、INFO 各 section、CONFIG GET/SET 运行时调参、 FLUSHALL ASYNC),以及真烧钱的坑(生产环境 KEYS、SCAN 的重复返回语义、大 hash 上 HGETALL、过期键不实时释放、 内存碎片与主动整理、AOF 与 RDB 取舍)。每条都附中英说 明、1-3 条可直接粘到 redis-cli 跑的真实例子、一行"常见 坑"。搜索框跨命令 / 说明 / 例子 / 坑四个字段一起搜,分 类胶囊缩范围。完全在浏览器里跑,不连任何 Redis,不上传, 不发任何网络请求。配合 PostgreSQL 速查表、SQL 速查搭配 Docker / kubectl / nginx 速查覆盖整条技术栈。
把内容粘贴或拖入工具面板。
点击按钮,在浏览器内本地处理,文件不上传。
一键复制结果或下载到本地。
适合在上传、交付、归档、客服排查前使用,也适合任何文件离开本机前的本地复核。
这些入口会把当前任务接到更完整的工具链里。
你想加个 cron 守卫,让两台应用机不会把同一个夜间账单任 务跑两遍。搜「NX」和「EVAL」,复制 SET billing_lock $token NX EX 300,再抓那段先比对 token 再 DEL 的 Lua 释 放脚本。那行坑提醒你:裸 DEL 会在 300 秒 TTL 过期后误删 另一台机刚拿到的锁。
值班告警说某个 Redis 实例到了 90% maxmemory。你搜 「Streams」和「MAXLEN」,找到 XADD stream MAXLEN ~ 1000000 和 XTRIM stream MAXLEN ~ 1000000,明白近似(~) 写法比精确截断便宜得多。那行坑说清楚:不封顶的 Stream 会无限增长,刚好就是吃掉 8GB 的元凶。
同事在请求处理里写了 KEYS user:*,1000 万 key 的实例把所 有连接的客户端都卡住了。你筛「键」,读懂 KEYS 是 O(N) 且 阻塞单线程,复制 SCAN 0 MATCH user:* COUNT 1000 的游标循 环,外加那句提醒:SCAN 跨轮次可能返回同一个 key 两次,要 在客户端去重。
你的缓存实例同时放着 session 和锁键,凌晨 3 点 allkeys-lru 把一个在途任务的锁淘汰掉了。你搜「淘汰」,只给缓存键设 TTL,切到 maxmemory-policy volatile-lru,再用 CONFIG GET maxmemory-policy 确认。那个坑胶囊把你踩的 allkeys-lru 雷点 写得明明白白。
用裸 SET key value 刷新值却把 TTL 抹掉,改用 SET key value KEEPTTL 或重新写上 EX,过期才不会丢。
给锁用 SETNX 加单独 EXPIRE,两条命令间客户端崩了锁就永不过期,改用原子的 SET key token NX EX ttl。
在涨到百万 field 的 hash 上调 HGETALL,一次返回几 MB 还阻塞,改用带 COUNT 的 HSCAN 或只 HMGET 你要的字段。
这份速查就是一个静态页。你输入的搜索词只在浏览器里跟内存中的 命令数组做匹配,不连任何 Redis 服务器,不进 URL,也不上传。 边输入边看 DevTools 的 Network 面板,零请求。堡垒机后面和气隙 网络里都能放心用。
做你这行的人, 还会一起用这些。