GET keyFetch the string value stored at a key. Returns nil if the key does not exist. The single most-used Redis command — every cache read goes through this.
GET user:1001:name
GET session:abc123
GET feature_flag:dark_mode
Redis cheat sheet — 80+ commands across strings, hashes, lists, sets, sorted sets, pub/sub, streams, scripts, with examples and pitfalls.
GET keyFetch the string value stored at a key. Returns nil if the key does not exist. The single most-used Redis command — every cache read goes through this.
GET user:1001:name
GET session:abc123
GET feature_flag:dark_mode
SET key value [EX seconds] [NX|XX]Store a string value at a key, optionally with a TTL. EX sets seconds-based expire, NX only sets if absent (atomic create), XX only sets if present (atomic update).
⚠ Common pitfall: Plain SET without EX wipes any previous TTL on the key — the new value lives forever. If you mean "refresh cache, keep TTL", use SET key val KEEPTTL (6.0+) or EXPIRE separately.
SET user:1001:name "Alice"
SET lock:order:42 worker-7 EX 30 NX
SET cfg:rate_limit 100 XX
DEL key [key ...]Delete one or more keys. Returns the count of keys actually removed. Synchronous — large keys (a hash with millions of fields) block the event loop while freeing memory.
⚠ Common pitfall: For huge keys, use UNLINK instead — it removes the key from the keyspace synchronously but reclaims memory in a background thread.
DEL user:1001:cache
DEL k1 k2 k3
UNLINK big_hash -- 大 key 推荐
EXISTS key [key ...]Check whether one or more keys exist. With N keys, returns the count of existing ones (the same key listed twice counts twice).
EXISTS user:1001
EXISTS k1 k2 k3
EXISTS missing -- 返回 0
EXPIRE key secondsSet a time-to-live in seconds on a key. After the timer fires the key is deleted lazily — on the next access, or by the background purge job.
⚠ Common pitfall: EXPIRE on a non-existent key returns 0 silently — there is no error. Always check the return value when setting locks.
EXPIRE session:abc 3600
EXPIRE cache:home_page 60
PEXPIRE k 500 -- 毫秒
TTL keyGet remaining seconds until a key expires. Returns -2 if the key does not exist, -1 if it exists but has no expire set.
TTL session:abc -- 比如返回 1800
TTL forever_key -- 返回 -1
PTTL k -- 毫秒
PERSIST keyRemove the TTL from a key, making it persistent. Returns 0 if the key has no expire or does not exist, 1 otherwise.
PERSIST hot_session
PERSIST upgraded_cache_key
KEYS patternMatch every key in the database against a glob pattern (e.g. user:*). Walks the entire keyspace in one synchronous shot — never run this in production.
⚠ Common pitfall: On a 10M-key database, KEYS * blocks the server for seconds and freezes every other client. Use SCAN. The only safe use of KEYS is on a local dev instance.
KEYS user:* -- 仅限本地
KEYS session:*
KEYS *:tmp
SCAN cursor [MATCH pattern] [COUNT n]Cursor-based incremental keyspace scan — Redis-safe replacement for KEYS. Walks a few buckets at a time, returning a new cursor for the next call. Done when cursor returns 0.
⚠ Common pitfall: SCAN may return the same key twice across iterations, and may return keys created/modified during the scan but not guaranteed to. Dedupe in the client. COUNT is a hint, not a limit.
SCAN 0 MATCH user:* COUNT 1000
SCAN 12345 MATCH session:*
while true; do cursor=$(redis-cli SCAN $cursor MATCH 'tmp:*' | head -1); ...; done
TYPE keyReturn the data type of the value stored at a key: string, list, set, zset, hash, stream, or none if the key does not exist.
TYPE user:1001 -- hash
TYPE leaderboard -- zset
TYPE missing -- none
RENAME key newkeyAtomically rename a key. If newkey already exists, it is silently overwritten. Use RENAMENX if you want it to fail when the target exists.
⚠ Common pitfall: In Cluster mode, RENAME requires both keys to live on the same hash slot — use {tag} to force colocation: RENAME {usr}:1001 {usr}:1001:archived.
RENAME old_key new_key
RENAMENX draft:42 published:42
UNLINK key [key ...]Delete keys like DEL, but reclaim memory in a background thread instead of synchronously. The de-facto safe delete for any key that might be large.
⚠ Common pitfall: Background freeing only kicks in past a size threshold — tiny keys are still freed inline, so UNLINK on a small key is no faster than DEL. The win is purely on big keys.
UNLINK big_hash
UNLINK session:* 的批量 -- 配合 SCAN 拿到 key 再 UNLINK
COPY source destination [DB n] [REPLACE]Copy the value at source to destination (6.2+). REPLACE overwrites an existing destination; DB targets another database. Preserves the source TTL on the copy.
COPY user:1001 user:1001:backup
COPY cfg cfg:staging DB 1 REPLACE
DUMP keySerialize the value at key into a Redis-specific binary blob, which RESTORE can rebuild elsewhere. The pair behind cross-instance key migration tools.
⚠ Common pitfall: The blob is NOT portable across major Redis versions in general — RESTORE checks an embedded version/CRC and rejects mismatches. Migrate between matching versions or use RESTORE-friendly tooling.
DUMP user:1001
RESTORE user:1001:copy 0 "$serialized" -- 0 表示无 TTL
RANDOMKEYReturn a random key from the current database, or nil if the database is empty. Sampling helper for spot-checking key shapes or estimating TTL coverage.
RANDOMKEY
for i in {1..100}; do redis-cli RANDOMKEY; done | sort | uniq -c -- 采样看前缀分布OBJECT ENCODING keyReveal the internal encoding Redis chose for a value: listpack, intset, hashtable, skiplist, embstr, int, and so on. The first stop when a key uses more memory than expected.
⚠ Common pitfall: Small collections start as compact listpack/intset and silently switch to hashtable/skiplist once they cross hash-max-listpack-entries etc. The switch is one-way and roughly doubles per-element overhead.
OBJECT ENCODING leaderboard -- listpack 或 skiplist
OBJECT ENCODING counter -- int
OBJECT IDLETIME keyReturn how many seconds a key has gone untouched (only valid under an LRU maxmemory-policy). Useful for hunting stale keys before a manual cleanup.
OBJECT IDLETIME old_cache -- 比如 86400 表示一天没碰
OBJECT FREQ hot_key -- LFU 策略下看访问频度
MOVE key dbMove a key from the current database to another numbered DB on the same instance. No-op (returns 0) if the destination already has that key.
⚠ Common pitfall: Numbered databases do not exist in Redis Cluster, so MOVE errors there. Multi-DB is also discouraged in general — prefer key prefixes or separate instances for isolation.
MOVE session:abc 1
SELECT 1 -- 切到 1 号库验证
INCR keyAtomically increment the integer stored at key by 1. If the key does not exist, treats it as 0 first. Returns the new value. Foundation of counters and rate limiters.
⚠ Common pitfall: Fails with an error if the value is not parseable as an int64. Bumping a value of "abc" or 9999999999999999999 throws — wrap counter keys with a clear naming convention.
INCR page_views:home
INCR rate_limit:ip:1.2.3.4
INCRBY user:42:credits 10
DECR keyAtomically decrement the integer stored at key by 1. Useful for releasing seats in a fixed-size resource pool.
DECR inventory:sku:42
DECRBY queue_depth 5
APPEND key valueAppend a string to the value at key. If the key does not exist, behaves like SET. Returns the new total length.
APPEND log:2026-05-26 "request_id=42\n"
APPEND buffer:user:1 chunk
GETSET key valueAtomically set a new value and return the old one. Common pattern for resetting a counter and reading its previous total in a single round-trip.
⚠ Common pitfall: Deprecated since 6.2 in favor of SET key value GET — same semantics, more consistent option flags. Both still work.
GETSET page_views:home 0
SET counter 0 GET -- 推荐写法
STRLEN keyReturn the byte length of the string at key. Returns 0 if the key does not exist. Useful for quick "is the cache populated?" probes.
STRLEN session:abc -- 比如返回 312
STRLEN empty_key -- 0
SETEX key seconds valueSet key with a value and a TTL in seconds in one atomic command. Equivalent to SET key value EX seconds but two commands shorter.
SETEX session:abc 3600 user_id=42
SETEX cache:home 60 "<html>..."
SETNX key valueSet key only if it does not already exist. Returns 1 on success, 0 if the key was present. Classic primitive for "first writer wins" patterns.
⚠ Common pitfall: Modern code should prefer SET key value NX EX ttl — SETNX has no TTL option, so a separate EXPIRE leaves a window where the key is permanent if the client crashes between commands.
SETNX lock:order:42 worker-7
SET lock:order:42 worker-7 NX EX 30 -- 推荐
MSET key value [key value ...]Set multiple keys to multiple values in one atomic round-trip. Either every key is set or (it cannot fail mid-way) none — there is no partial MSET.
MSET user:1:name Alice user:1:age 30
MSETNX a 1 b 2 -- 全部不存在才设,原子
MGET key [key ...]Fetch multiple string values in one round-trip, returning an array aligned to the input order. Missing keys come back as nil in their slot.
⚠ Common pitfall: In Cluster mode all keys must live on the same hash slot, or MGET errors with CROSSSLOT. Use {tag} to colocate, or fan out per-slot client-side.
MGET user:1:name user:2:name user:3:name
MGET {usr}:1 {usr}:2 -- 集群下用 hash tag 同槽INCRBYFLOAT key incrementIncrement the value at key by a floating-point amount, returning the new value as a string. Handles money-style fractional math without a separate float type.
⚠ Common pitfall: Floating-point rounding still applies — 0.1 + 0.2 will not be exactly 0.3. For currency, store integer cents and INCRBY, not INCRBYFLOAT on dollars.
INCRBYFLOAT account:42:balance 0.99
INCRBYFLOAT sensor:temp -1.5
SETRANGE key offset valueOverwrite part of the string at key starting at a byte offset, zero-padding if the offset is past the current end. Treats a string as a fixed-layout byte buffer.
SETRANGE packet:42 4 "PAYLOAD"
SETRANGE greeting 6 "Redis" -- 改第 6 字节起的内容
GETRANGE key start endReturn a substring of the string at key by inclusive byte range; negative indexes count from the tail (-1 = last byte). The read counterpart to SETRANGE.
GETRANGE log_line 0 99 -- 前 100 字节
GETRANGE iso_date 0 3 -- 取年份 "2026"
GETDEL keyAtomically read the value at key and delete it in one step (6.2+). The clean primitive for one-shot tokens — read it, and it is gone.
GETDEL otp:user:42 -- 一次性验证码
GETDEL pending_payload:abc
GETEX key [EX seconds | PERSIST]Read the value at key and adjust its TTL in the same command (6.2+): EX refreshes the expire, PERSIST strips it. Sliding-window sessions without a second round-trip.
GETEX session:abc EX 1800 -- 读会话并续 30 分钟
GETEX cache:home PERSIST -- 读并转永久
HSET key field value [field value ...]Set one or more fields on a hash. Returns the count of NEW fields added (overwrites do not count). Hashes are the right shape for "object with a few small fields".
⚠ Common pitfall: HSET key f v (older HMSET) was deprecated in 4.0 — HSET now accepts multiple field-value pairs. HMSET still works but should not be used in new code.
HSET user:1001 name Alice age 30 email alice@x.io
HSET cart:42 sku:1 2 sku:2 1
HGET key fieldGet the value of one field on a hash. Returns nil if the field or the key does not exist.
HGET user:1001 name -- "Alice"
HGET cfg dark_mode
HMGET key field [field ...]Get multiple field values from a hash in one round-trip. Returns an array with nil for any missing fields, preserving input order.
HMGET user:1001 name age email
HMGET cfg theme lang tz
HDEL key field [field ...]Delete one or more fields from a hash. Returns the count actually removed. When the last field is deleted, the hash itself is removed.
HDEL user:1001 stale_flag
HDEL cart:42 sku:1 sku:3
HKEYS keyReturn every field name in a hash, in arbitrary order. O(N) over the hash size — fine for objects with dozens of fields, dangerous for millions.
HKEYS user:1001
HKEYS cfg
HVALS keyReturn every value in a hash, in arbitrary order. Same complexity as HKEYS — use HSCAN for large hashes.
HVALS cfg
HVALS leaderboard_summary
HGETALL keyReturn every field AND value in a hash as a flat [f1, v1, f2, v2, ...] array. Convenient but dangerous on large hashes.
⚠ Common pitfall: HGETALL on a hash with 100k+ fields can return MBs of data in one reply, blocking the event loop and using huge client-side memory. Switch to HSCAN or partition the hash.
HGETALL user:1001
HSCAN big_hash 0 COUNT 500 -- 安全替代
HINCRBY key field incrementAtomically increment an integer field on a hash. Auto-creates the field at 0 if absent. Foundation of per-user counter aggregates.
HINCRBY user:1001:stats login_count 1
HINCRBY user:1001:stats bytes_uploaded 4096
HINCRBYFLOAT user:1001:wallet usd 0.99
HSETNX key field valueSet a hash field only if it does not already exist. Returns 1 if the field was created, 0 if it was already there. Per-field "first writer wins".
HSETNX user:1001 created_at 1716700000
HSETNX cfg first_seen_version 5
HEXISTS key fieldCheck whether a single field exists in a hash. O(1). Returns 1 if present, 0 if the field or the whole hash is missing.
HEXISTS user:1001 email
HEXISTS cfg dark_mode -- 0 表示未设置过
HLEN keyReturn the number of fields in a hash. O(1) — the count lives in the hash header. Returns 0 for a missing key.
HLEN user:1001 -- 比如 5 个字段
HLEN cart:42 -- 购物车 SKU 数
HSCAN key cursor [MATCH pattern] [COUNT n] [NOVALUES]Cursor-based incremental scan over a hash, the safe alternative to HGETALL on big hashes. NOVALUES (7.4+) returns field names only, halving the payload.
⚠ Common pitfall: Like SCAN, HSCAN can return the same field more than once across iterations and COUNT is only a hint. Treat fields as a set on the client side.
HSCAN big_hash 0 MATCH attr:* COUNT 500
HSCAN user:1001 0 NOVALUES -- 只要字段名(7.4+)
HRANDFIELD key [count [WITHVALUES]]Return random field(s) from a hash (6.2+). A positive count returns distinct fields; a negative count allows repeats. WITHVALUES pairs each with its value.
HRANDFIELD user:1001 -- 随机一个字段
HRANDFIELD quiz_bank 3 WITHVALUES -- 随机抽 3 道题
LPUSH key value [value ...]Push one or more values onto the HEAD (left) of a list. Auto-creates the list if missing. Returns the new length. Pair with RPOP for FIFO queues.
LPUSH queue:jobs job_42
LPUSH queue:jobs job_43 job_44 -- 多值
LPUSH log:errors "$timestamp $msg"
RPUSH key value [value ...]Push values onto the TAIL (right) of a list. Pair with LPOP for FIFO; pair with RPOP for LIFO. Single command runs at sub-millisecond cost.
RPUSH queue:jobs job_42
RPUSH timeline:1001 "post:42"
LPOP key [count]Pop and return one value from the HEAD of a list. With count (6.2+) returns up to N values at once. Returns nil on an empty/missing list.
LPOP queue:jobs
LPOP queue:jobs 10 -- 一次取一批
RPOP key [count]Pop and return one value from the TAIL of a list. Same shape as LPOP. With count, returns up to N entries.
RPOP queue:jobs
RPOP queue:jobs 100
LRANGE key start stopReturn a slice of a list by index. Indexes are inclusive on both ends; negative indexes count from the tail (-1 = last). LRANGE k 0 -1 is the entire list.
⚠ Common pitfall: LRANGE k 0 -1 on a list with millions of items is O(N) and ships the entire list to the client. Always paginate (LRANGE k 0 99, then 100 199, ...).
LRANGE timeline 0 9 -- 前 10 条
LRANGE timeline -10 -1 -- 最后 10 条
LLEN keyReturn the length of a list. O(1) — Redis maintains the count in the list header. Returns 0 if the key does not exist.
LLEN queue:jobs -- 比如 42
LLEN missing -- 0
LINDEX key indexGet the element at a specific index in a list. Negative indexes count from the tail. O(N) on long lists — avoid in hot paths.
LINDEX timeline 0 -- 第一条
LINDEX timeline -1 -- 最后一条
LREM key count valueRemove up to |count| occurrences of value from a list. count > 0 starts from head, < 0 starts from tail, 0 removes ALL matches. Returns the number actually removed.
LREM dedupe_queue 1 job_42 -- 从头删第一个
LREM dedupe_queue 0 job_42 -- 全删
BLPOP key [key ...] timeoutBlocking left-pop: wait up to timeout seconds (0 = forever) for an element on any of the given lists. Returns [key, value] when ready, nil on timeout.
⚠ Common pitfall: In Cluster mode all keys must hash to the same slot, so use {tag}. Each blocked client holds a connection — if you have 10k workers, plan for 10k open sockets on the Redis side.
BLPOP queue:jobs 5
BLPOP queue:high queue:low 0 -- 优先级队列
LSET key index valueOverwrite the element at a given list index with a new value. Errors if the index is out of range. Negative indexes count from the tail.
LSET timeline 0 "post:99" -- 改最新一条
LSET queue -1 "retry:job_42" -- 改最后一个
LTRIM key start stopTrim a list to only the elements within the inclusive index range, discarding everything else. The standard way to keep a capped "latest N" feed.
⚠ Common pitfall: LPUSH then LTRIM is the canonical capped-list pattern, but run them in a MULTI or a Lua script — between the two commands the list briefly exceeds the cap and a concurrent reader sees extra items.
LPUSH feed:1001 "post:99" LTRIM feed:1001 0 99 -- 只留最新 100 条
LTRIM log 0 -1 -- 不变(保留全部)
LINSERT key BEFORE|AFTER pivot valueInsert a value just before or after the first occurrence of a pivot element. Returns the new length, -1 if the pivot was not found, 0 if the key is missing.
⚠ Common pitfall: LINSERT scans from the head to find the pivot — O(N). On long lists this is slow, and it only ever touches the FIRST match, not all of them.
LINSERT playlist BEFORE "song:5" "song:ad"
LINSERT steps AFTER "build" "test"
RPOPLPUSH source destinationAtomically pop from the tail of source and push onto the head of destination, returning the moved element. The classic reliable-queue building block.
⚠ Common pitfall: Deprecated since 6.2 in favor of LMOVE, which lets you choose either end on both sides. Pattern: RPOPLPUSH queue processing, then remove from processing only after the work succeeds.
RPOPLPUSH queue processing
LMOVE queue processing LEFT RIGHT -- 推荐(6.2+)
BRPOPLPUSH source destination timeoutBlocking version of RPOPLPUSH: wait up to timeout seconds for an element on source, then atomically move it to destination. Powers reliable worker queues.
⚠ Common pitfall: Deprecated since 6.2 — use BLMOVE. The reliable-queue idea: workers BLMOVE jobs→inflight, process, then LREM from inflight; a periodic reaper requeues anything stuck in inflight too long.
BRPOPLPUSH jobs inflight 5
BLMOVE jobs inflight LEFT RIGHT 5 -- 推荐(6.2+)
BRPOP key [key ...] timeoutBlocking right-pop: wait up to timeout seconds (0 = forever) for an element on the tail of any listed list. Returns [key, value] or nil on timeout.
BRPOP queue:jobs 5
BRPOP queue:high queue:low 0 -- 配 RPUSH 做 FIFO 优先级队列
SADD key member [member ...]Add one or more members to a set. Returns the count of NEW members (duplicates are silently ignored). Sets give O(1) membership tests.
SADD online_users 1001 1002 1003
SADD tags:post:42 redis tutorial cache
SREM key member [member ...]Remove one or more members from a set. Returns the count actually removed. Removing the last member also drops the set itself.
SREM online_users 1001
SREM tags:post:42 deprecated
SMEMBERS keyReturn every member of a set in arbitrary order. O(N) — fine for "tags on a post" (10s of members), dangerous for big sets.
⚠ Common pitfall: For large sets, use SSCAN. SMEMBERS on a 1M-member set returns 1M strings in one reply, eating client memory and blocking Redis.
SMEMBERS tags:post:42
SSCAN big_set 0 COUNT 500 -- 大 set 安全替代
SISMEMBER key memberTest whether a member exists in a set. O(1). Foundation of per-user feature flags, online presence, banned-IP lookups.
SISMEMBER online_users 1001
SISMEMBER banned_ips 1.2.3.4
SMISMEMBER online_users 1001 1002 1003 -- 批量(6.2+)
SUNION key [key ...]Return the union of multiple sets — every distinct member appearing in any input. Useful for "users active today OR yesterday" style queries.
SUNION active:today active:yesterday
SUNIONSTORE active:48h active:today active:yesterday
SINTER key [key ...]Return the intersection of multiple sets — members present in EVERY input. Common for "users who clicked both ad A AND ad B" style cohort math.
⚠ Common pitfall: Complexity is O(N*M) where N is the smallest set and M is the number of sets. Always put the smallest set first — Redis short-circuits when an early miss eliminates a candidate.
SINTER ad_A_clickers ad_B_clickers
SINTERSTORE both_clickers ad_A_clickers ad_B_clickers
SDIFF key [key ...]Return members in the first set that are NOT in any of the subsequent sets. The Redis way to compute "everyone who has not yet seen the popup".
SDIFF all_users popup_seen
SDIFFSTORE retarget all_users popup_seen unsubscribed
SCARD keyReturn the number of members in a set. O(1) — the cardinality is tracked in the set header. Returns 0 for a missing key.
SCARD online_users -- 当前在线数
SCARD tags:post:42
SPOP key [count]Remove and return one or more RANDOM members from a set. Unlike SRANDMEMBER, SPOP mutates the set. Common for "draw N winners without replacement".
SPOP raffle:pool -- 抽一个并移出
SPOP raffle:pool 3 -- 一次抽 3 个不重复
SRANDMEMBER key [count]Return random member(s) from a set WITHOUT removing them. A positive count returns distinct members; a negative count allows repeats and may exceed the set size.
SRANDMEMBER products:featured 4 -- 随机推荐 4 个
SRANDMEMBER dice:faces -10 -- 模拟 10 次有放回掷骰
SMOVE source destination memberAtomically move a member from one set to another. Returns 1 on success, 0 if the member was not in source. Both sets are touched in a single step.
SMOVE pending approved order:42
SMOVE online away user:1001
SSCAN key cursor [MATCH pattern] [COUNT n]Cursor-based incremental scan over a set — the safe replacement for SMEMBERS on large sets. Walks a few buckets per call, done when the cursor returns 0.
SSCAN ip:banned 0 MATCH "10.*" COUNT 500
SSCAN set:huge 0 COUNT 1000
SINTERCARD numkeys key [key ...] [LIMIT n]Return the size of the intersection of sets WITHOUT materializing it (7.0+). LIMIT short-circuits once the count reaches n — much cheaper than SINTER when you only need "are there at least n in common".
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 ...]Add or update members in a sorted set. NX skips updates, XX skips inserts, GT/LT only update when the new score is greater/less. Returns count of NEW members (or changes if 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]Range query on a sorted set. Default by rank (lowest score first); REV reverses; BYSCORE switches to score range; BYLEX to lexicographic range. The 6.2+ swiss-army knife.
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]Return members ordered by score from HIGH to LOW. Equivalent to ZRANGE ... REV on 6.2+. Classic "top N leaderboard" call.
⚠ Common pitfall: Deprecated since 6.2 — prefer ZRANGE ... REV for new code. Still supported and identical in behavior.
ZREVRANGE leaderboard 0 9 WITHSCORES
ZRANGE leaderboard 0 9 REV WITHSCORES -- 推荐
ZRANGEBYSCORE key min max [LIMIT offset count]Return members whose score falls in [min, max]. Use ( for exclusive bounds. Combined with timestamp scores, this is how you implement "scheduled jobs ready to run".
ZRANGEBYSCORE jobs:scheduled -inf 1716700000 -- 时间戳之前
ZRANGEBYSCORE leaderboard (100 200 -- (100, 200]
ZRANGEBYSCORE leaderboard 100 +inf LIMIT 0 50
ZINCRBY key increment memberAtomically increment the score of a member in a sorted set. Auto-creates the member with score = increment if absent.
ZINCRBY leaderboard 10 alice
ZINCRBY trending:topics 1 "redis"
ZRANK key member [WITHSCORE]Return the 0-based rank of a member, ordered by score ascending. Returns nil if the member does not exist. ZREVRANK for high-to-low rank.
ZRANK leaderboard alice -- 比如 5
ZREVRANK leaderboard alice -- 高到低
ZRANK leaderboard alice WITHSCORE -- 7.2+
ZSCORE key memberReturn the score of a member in a sorted set as a string, or nil if the member is absent. O(1). ZMSCORE (6.2+) fetches several scores at once.
ZSCORE leaderboard alice -- 比如 "110"
ZMSCORE leaderboard alice bob carol -- 批量(6.2+)
ZCARD keyReturn the number of members in a sorted set. O(1). Returns 0 for a missing key. Pair with ZCOUNT to count members within a score range.
ZCARD leaderboard -- 总参赛人数
ZCOUNT leaderboard 90 100 -- 分数在 [90,100] 的人数
ZREM key member [member ...]Remove one or more members from a sorted set. Returns the count actually removed. Removing the last member drops the key. Foundation of leaderboard eviction.
ZREM leaderboard cheater_42
ZREM jobs:scheduled job_done_1 job_done_2
ZPOPMIN key [count]Atomically remove and return the member(s) with the LOWEST score (5.0+), each paired with its score. The natural pop for a min-score priority queue.
ZPOPMIN jobs:scheduled -- 取最早该跑的任务
ZPOPMIN delay_queue 10 -- 一次取 10 个
ZPOPMAX key [count]Atomically remove and return the member(s) with the HIGHEST score (5.0+), each with its score. BZPOPMIN / BZPOPMAX are the blocking variants for queue workers.
ZPOPMAX high_scores -- 取当前最高分并移出
BZPOPMIN delay_queue 5 -- 阻塞取最小分数(5.0+)
ZREMRANGEBYSCORE key min maxRemove every member whose score falls in [min, max]. With timestamp scores this is the one-liner for "purge everything older than cutoff" on a sliding-window set.
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]Run a ZRANGE query and store the resulting members (with scores) into a destination sorted set (6.2+). Materialize a "top N" snapshot without round-tripping the data to the client.
ZRANGESTORE top10 leaderboard 0 9 REV -- 固化前 10 名
ZUNIONSTORE combined 2 lb:eu lb:us -- 合并两个榜
SETBIT key offset valueSet a single bit (0 or 1) at the given byte offset on a string. Auto-extends the string with zero-bytes if the offset is past the end. Use for compact daily-active-user flags.
⚠ Common pitfall: SETBIT k 100000000 1 allocates a 12.5 MB string instantly. Watch the offset — a typo with user_id*8 vs user_id can blow memory.
SETBIT dau:2026-05-26 1001 1
SETBIT feature:beta 42 1
GETBIT key offsetGet the bit (0 or 1) at the given offset on a string. Returns 0 if the offset is past the end of the string.
GETBIT dau:2026-05-26 1001 -- 1 表示活跃
GETBIT feature:beta 42
BITCOUNT key [start end [BYTE|BIT]]Count the number of set bits (1s) in a string, optionally within a byte or bit range. The fastest way to ask "how many users were active today?".
BITCOUNT dau:2026-05-26 -- 当日总活跃数
BITCOUNT dau:2026-05-26 0 999 BYTE
BITOP AND|OR|XOR|NOT destkey key [key ...]Bitwise op across one or more source strings, storing the result at destkey. Combine daily DAU bitmaps to compute weekly retention, churn, or N-day active cohorts.
⚠ Common pitfall: BITOP is O(N) over the longest source string. Daily DAU bitmaps for 10M users are 1.25 MB each — fine, but BITOP across a year of them is over 450 MB written to destkey, every time.
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]]]Find the position of the first 0 or 1 bit in a string, optionally within a range. Handy for "find the first free slot" allocators backed by a bitmap.
BITPOS slots 0 -- 第一个空闲槽
BITPOS dau:2026-05-26 1 -- 第一个活跃用户的位
BITFIELD key [GET|SET|INCRBY type offset value] [OVERFLOW WRAP|SAT|FAIL]Treat a string as an array of arbitrary-width signed/unsigned integers, reading and updating several fields in one atomic command. OVERFLOW controls wrap / saturate / fail on overflow.
⚠ Common pitfall: Offsets are in units of the type unless prefixed with #. BITFIELD k SET u8 #0 vs BITFIELD k SET u8 0 mean different things — # multiplies by the type width. Mixing the two corrupts your layout.
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 ...]Add one or more elements to a HyperLogLog. Returns 1 if the estimated cardinality changed, 0 otherwise. Uses a fixed 12 KB regardless of element count.
⚠ Common pitfall: HyperLogLog is APPROXIMATE — standard error around 0.81%. Never use it where exact counts matter (billing, audit logs). Perfect for UV / dedupe at billion scale.
PFADD uv:2026-05-26 user_1001 user_1002
PFADD uniq_search_terms "redis cheat"
PFCOUNT key [key ...]Return the approximate cardinality of one HyperLogLog, or the merged cardinality of several. Cost is O(1) for one key, O(N) for the merge case.
PFCOUNT uv:2026-05-26
PFCOUNT uv:2026-05-20 uv:2026-05-21 uv:2026-05-26 -- 多日合并
PFMERGE destkey sourcekey [sourcekey ...]Merge multiple HyperLogLogs into destkey. Idempotent — re-merging the same sources produces the same destination. Common for "compute monthly UV from daily UVs".
PFMERGE uv:2026-05 uv:2026-05-01 uv:2026-05-02 ... uv:2026-05-31
PFADD vs Set memoryA HyperLogLog counts unique elements in a fixed ~12 KB no matter how many you add, whereas a Set storing the same members grows linearly. The trade is exactness for constant memory.
⚠ Common pitfall: If you ever need the actual members (not just the count), you cannot use a HLL — it is one-way. Keep a real Set only when you must enumerate; use HLL purely for cardinality.
PFADD uv:2026-05-26 u1 u2 u3
MEMORY USAGE uv:2026-05-26 -- 约 12KB 上限
PUBLISH channel messageBroadcast a message to every client SUBSCRIBEd to channel. Returns the number of subscribers that received it. Fire-and-forget — no persistence, no replay.
⚠ Common pitfall: A subscriber that is offline when you PUBLISH NEVER gets the message — there is no buffering. For at-least-once delivery, use Streams (XADD + XREADGROUP).
PUBLISH events:user_signup user_1001
PUBLISH chat:room:42 "hello"
SUBSCRIBE channel [channel ...]Listen for messages on one or more channels. The connection enters subscribe mode — most regular commands become forbidden until UNSUBSCRIBE.
SUBSCRIBE events:user_signup
SUBSCRIBE chat:room:42 chat:room:43
PSUBSCRIBE pattern [pattern ...]Subscribe to every channel matching one or more glob patterns. Each delivered message carries the actual matching channel name plus the matched pattern.
⚠ Common pitfall: PSUBSCRIBE chat:* matches every chat:N channel — fine for 10 rooms, painful for 10M. Pattern matching runs on every PUBLISH and is O(patterns * subscribers).
PSUBSCRIBE chat:*
PSUBSCRIBE events:*
PUBSUB CHANNELS [pattern]List the active channels that currently have at least one subscriber, optionally filtered by a glob pattern. Introspection for "who is listening to what right now".
PUBSUB CHANNELS
PUBSUB CHANNELS chat:*
PUBSUB NUMSUB chat:42 -- 某频道订阅者数
SPUBLISH shardchannel messagePublish to a shard channel in Redis Cluster (7.0+). Unlike regular PUBLISH, sharded pub/sub routes by slot and does NOT broadcast across every node, so it scales with the cluster.
⚠ Common pitfall: Regular PUBLISH in Cluster fans the message to every node, which does not scale. For high-throughput cluster pub/sub use SPUBLISH + SSUBSCRIBE so traffic stays on the owning shard.
SPUBLISH orders:shard "new_order"
SSUBSCRIBE orders:shard -- 分片订阅(7.0+)
XADD key [MAXLEN [~|=] N] * field value [field value ...]Append a new entry to a stream. * means "let Redis assign the ID" (ms timestamp + seq). MAXLEN with ~ caps stream length approximately, with = exactly. Streams are the durable, replayable replacement for Pub/Sub.
⚠ Common pitfall: Without MAXLEN, streams grow forever and eat memory. Always cap producer streams (MAXLEN ~ 1000000) or trim periodically with XTRIM. The ~ form is much cheaper than =.
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 ...]Read new entries from one or more streams after the given IDs. $ means "only entries added AFTER I started waiting". BLOCK 0 waits forever.
XREAD COUNT 100 STREAMS events:orders 0
XREAD BLOCK 5000 STREAMS events:orders $
XREADGROUP GROUP grp consumer [COUNT n] [BLOCK ms] STREAMS key idConsumer-group read — Redis tracks delivery per consumer for at-least-once semantics. ID = > means "new messages this consumer has not seen". Pair with XACK after processing.
⚠ Common pitfall: You MUST create the group first with XGROUP CREATE — XREADGROUP on a non-existent group errors. After a consumer crashes, use XPENDING + XCLAIM to reassign its pending entries.
XGROUP CREATE events:orders processors $ MKSTREAM
XREADGROUP GROUP processors worker-1 COUNT 100 BLOCK 5000 STREAMS events:orders >
XACK key group id [id ...]Acknowledge that one or more entries have been processed by the consumer group. Until you XACK, an entry stays in the group's pending list and can be reassigned.
XACK events:orders processors 1716700000-0
XLEN keyReturn the number of entries currently in a stream. O(1). Pair with XINFO STREAM key for full metadata (first/last entry, groups, length).
XLEN events:orders
XINFO STREAM events:orders
XRANGE key start end [COUNT n]Return stream entries with IDs in the inclusive range [start, end]. - and + mean the smallest and largest possible IDs, so XRANGE k - + COUNT 10 reads the oldest 10 entries.
XRANGE events:orders - + COUNT 10 -- 最旧 10 条
XREVRANGE events:orders + - COUNT 10 -- 最新 10 条
XPENDING key group [IDLE ms] [start end count [consumer]]Inspect entries delivered to a consumer group but not yet XACKed. The summary form shows total pending plus per-consumer counts; the extended form lists individual stuck entries.
⚠ Common pitfall: A growing XPENDING count means consumers are crashing or slow — entries pile up forever until claimed. Pair a reaper that XCLAIMs entries idle past a threshold and routes poison messages to a dead-letter stream.
XPENDING events:orders processors -- 概览
XPENDING events:orders processors IDLE 60000 - + 100 -- 卡超过 60s 的
XCLAIM key group consumer min-idle-time id [id ...]Transfer ownership of pending entries that have been idle at least min-idle-time to another consumer. The recovery primitive when a worker dies mid-processing.
⚠ Common pitfall: XAUTOCLAIM (6.2+) is usually easier: it scans, claims, and returns a cursor in one call, so you do not have to XPENDING first. Always cap retries — an entry that keeps failing should go to a dead-letter 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]Create a consumer group on a stream, starting from a given ID ($ = only new entries, 0 = from the beginning). MKSTREAM auto-creates the stream if it does not exist yet.
⚠ Common pitfall: Without MKSTREAM, XGROUP CREATE on a missing stream errors. Starting at $ means the group ignores every entry that already exists — use 0 if you need to process the backlog too.
XGROUP CREATE events:orders processors $ MKSTREAM
XGROUP CREATECONSUMER events:orders processors worker-3 -- 显式建消费者
XTRIM key MAXLEN|MINID [~|=] thresholdTrim a stream by length (MAXLEN) or by minimum ID (MINID, 6.2+). The ~ form trims approximately and is far cheaper because it only drops whole macro-nodes.
XTRIM events:orders MAXLEN ~ 1000000
XTRIM events:orders MINID 1716700000000 -- 丢弃此时间戳前的(6.2+)
EVAL script numkeys key [key ...] arg [arg ...]Run an inline Lua script atomically against Redis. All keys must be declared via numkeys + KEYS[]. Scripts are sandboxed and run single-threaded — every keystroke blocks the server.
⚠ Common pitfall: Long-running scripts block ALL other clients. Hard limit is lua-time-limit (5s default), after which only SCRIPT KILL or SHUTDOWN NOSAVE can recover. Keep scripts to a handful of commands.
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 ...]Run a previously cached script by its SHA-1 digest. Cuts the bandwidth from the full script body to 40 hex chars per call. Falls back to EVAL on NOSCRIPT errors.
EVALSHA a9d8b1c... 1 rate_limit 100
-- 通常配合 SCRIPT LOAD: SHA=$(redis-cli SCRIPT LOAD "$src"); redis-cli EVALSHA $SHA ...
SCRIPT LOAD scriptCache a script on the server and return its SHA-1. The script is NOT executed. Use this to warm caches on startup, then call EVALSHA from then on.
⚠ Common pitfall: FLUSHALL or restart wipes the script cache — every client must be ready to re-LOAD on NOSCRIPT. In Redis Cluster, you must LOAD on EVERY shard separately.
SCRIPT LOAD "return 1" -- 返回 e0e1f9fabfc9d4800c877a703b823ac0578ff831
FCALL function numkeys key [key ...] arg [arg ...]Call a named function from a loaded Redis Functions library (7.0+). Functions are the durable successor to EVAL scripts — registered by name, persisted in the RDB/AOF, and replicated.
⚠ Common pitfall: Unlike EVAL scripts (which vanish on FLUSHALL / restart), a FUNCTION LOADed library survives restarts and replicates to replicas automatically. Migrate hot EVALSHA paths to FUNCTION for operational sanity.
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 ...]Check whether one or more scripts are already cached on the server by SHA-1, returning a 1/0 array. Clients use this to decide between EVALSHA and a re-LOAD.
SCRIPT EXISTS a9d8b1c... -- [1] 已缓存
SCRIPT FLUSH -- 清空整个脚本缓存
MULTIStart a transaction block. Subsequent commands are queued (return QUEUED) rather than executed, until EXEC or DISCARD. MULTI/EXEC blocks run atomically.
MULTI INCR a INCR b EXEC
EXECExecute all commands queued since MULTI atomically, returning an array of replies. If a WATCHed key was modified, returns nil and the whole transaction is aborted.
⚠ Common pitfall: Redis transactions are NOT rollback-on-error like SQL. If one command in the block fails (e.g. wrong type), the others STILL EXECUTE. EXEC just returns a per-command result.
MULTI SET a 1 SET b 2 EXEC -- 返回 ["OK", "OK"]
DISCARDDrop the queued commands and exit MULTI mode without executing. The connection returns to normal command mode.
MULTI SET a 1 DISCARD -- a 不变
WATCH key [key ...]Optimistic-locking primitive. WATCH some keys, then MULTI/EXEC — if any watched key was modified by anyone between WATCH and EXEC, the transaction aborts. The Redis CAS pattern.
WATCH balance val = GET balance if val >= 100: MULTI DECRBY balance 100 EXEC
UNWATCHForget all keys WATCHed on the current connection, without running a transaction. EXEC and DISCARD already UNWATCH implicitly — call this explicitly only when you abandon a CAS attempt.
⚠ Common pitfall: A common CAS leak: you WATCH a key, read it, decide not to write, but forget to UNWATCH — the next MULTI/EXEC on that connection is now needlessly gated by the stale watch. Always UNWATCH on the early-return path.
WATCH balance -- 读后决定不改 UNWATCH
WATCH a b c -- 多 key 乐观锁
SAVESynchronously dump the dataset to disk as RDB. BLOCKS THE SERVER for the entire duration — on a 10 GB dataset that is minutes. Almost never the right command in production.
⚠ Common pitfall: Use BGSAVE for non-blocking RDB snapshots. SAVE exists mainly for disaster recovery scripts that explicitly want server-quiescent point-in-time consistency.
SAVE -- 一般别用
BGSAVE -- 推荐
BGSAVEFork a background process to write an RDB snapshot without blocking the main thread. The fork itself can be slow on huge instances due to copy-on-write page-table cost.
⚠ Common pitfall: On a 64 GB instance with heavy writes, fork can take seconds and double resident memory while the child runs. Schedule BGSAVE during low traffic.
BGSAVE
BGSAVE SCHEDULE -- 7.0+ 排队避免冲突
LASTSAVEReturn the UNIX timestamp of the most recent successful RDB save. Health checks compare this against now() to detect a stalled background save.
LASTSAVE -- 比如返回 1716700000
SLAVEOF host portConfigure this instance as a replica of the given master, syncing the dataset and following the master's replication stream. Deprecated name — see REPLICAOF.
⚠ Common pitfall: Renamed to REPLICAOF in 5.0 for inclusive language. Both still work, but new tooling and configs should use REPLICAOF.
SLAVEOF master.internal 6379
SLAVEOF NO ONE -- 解除从属变独立实例
REPLICAOF host portModern alias for SLAVEOF (5.0+). REPLICAOF NO ONE detaches from the master and promotes the instance to standalone primary.
REPLICAOF master.internal 6379
REPLICAOF NO ONE -- 提升为主
BGREWRITEAOFRewrite the append-only file in the background, compacting it to the smallest set of commands that reproduce the current dataset. Keeps AOF replay fast and the file from growing without bound.
⚠ Common pitfall: Redis triggers an auto-rewrite based on auto-aof-rewrite-percentage, but a write-heavy spike can outpace it. Watch aof_pending_rewrite and aof_current_size in INFO persistence; trigger manually before the file gets unwieldy.
BGREWRITEAOF
INFO persistence -- 看 aof_current_size / aof_base_size
WAIT numreplicas timeoutBlock until the writes issued by this connection are acknowledged by at least numreplicas replicas, or until timeout ms. Buys stronger durability on top of async replication.
⚠ Common pitfall: WAIT is NOT a transaction and does NOT roll back if replicas fall short — it just tells you how many acked. Redis replication is asynchronous by default; WAIT narrows the data-loss window but cannot eliminate it.
WAIT 1 100 -- 至少 1 个副本确认,最多等 100ms
WAIT 2 1000 -- 多数派确认
FAILOVER [TO host port] [ABORT] [TIMEOUT ms]Trigger a coordinated, low-data-loss failover from a primary to one of its replicas (6.2+). Unlike a Sentinel-driven failover, the primary pauses writes and hands off cleanly.
FAILOVER -- 自动选副本
FAILOVER TO replica.internal 6379
FAILOVER ABORT -- 中止进行中的切换
CLIENT LISTList every connected client with its address, age, idle time, last command, and flags. Indispensable for diagnosing connection leaks and "who is hammering us".
CLIENT LIST
CLIENT LIST TYPE pubsub -- 仅 pub/sub 客户端
CLIENT KILL ADDR 1.2.3.4:5678 -- 踢掉某客户端
INFO [section]Return server stats in plain text: memory, replication, clients, persistence, CPU, keyspace, commandstats. The single most useful diagnostic command.
INFO -- 全量
INFO memory
INFO replication
INFO commandstats -- 各命令调用次数和耗时
CONFIG GET parameterRead one or more server config parameters. Supports glob patterns (CONFIG GET maxmemory*). Read-only on managed Redis services like ElastiCache.
CONFIG GET maxmemory
CONFIG GET maxmemory-policy
CONFIG GET save
CONFIG SET parameter valueSet a config parameter at runtime, without restarting the server. The change is in-memory only — write CONFIG REWRITE to persist into redis.conf.
⚠ Common pitfall: CONFIG SET maxmemory-policy noeviction on a full instance will start refusing writes immediately. Validate the new policy is what you mean before changing it on prod.
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG REWRITE -- 落盘
FLUSHALL [ASYNC|SYNC]Delete every key in EVERY database. ASYNC (4.0+) frees memory in a background thread. The fastest way to ruin a production weekend.
⚠ Common pitfall: Rename or disable in redis.conf for prod: rename-command FLUSHALL "". Equivalent for one DB only is FLUSHDB. Both are irreversible unless you have a recent RDB / AOF.
FLUSHALL ASYNC -- 生产用 ASYNC
FLUSHDB -- 仅当前库
DBSIZEReturn the number of keys in the currently selected database. O(1) — Redis tracks this count, unlike KEYS which would scan. The safe way to ask "how big is this DB".
DBSIZE -- 比如 1240392
SELECT 1 DBSIZE -- 看 1 号库
MEMORY USAGE key [SAMPLES n]Estimate the total bytes a key and its value consume, including internal overhead. The right tool for hunting the handful of keys eating most of your memory.
⚠ Common pitfall: For aggregate types it samples nested elements rather than measuring all of them; bump SAMPLES (or use 0 for exact) on skewed collections. Pair with MEMORY DOCTOR for a plain-language health summary.
MEMORY USAGE user:1001 -- 字节数
MEMORY USAGE big_zset SAMPLES 0 -- 精确测量
MEMORY DOCTOR
SLOWLOG GET [count]Return the most recent commands whose execution time exceeded slowlog-log-slower-than microseconds. The first place to look when latency spikes — it names the exact slow command and its args.
⚠ Common pitfall: Slowlog measures only command EXECUTION time, not the time a request waited in the queue behind a slow command. A fast command logged as "slow" usually means it was stuck behind a KEYS or a big Lua script.
SLOWLOG GET 10 -- 最近 10 条慢命令
SLOWLOG RESET -- 清空
CONFIG SET slowlog-log-slower-than 10000 -- 阈值 10ms
CLIENT NO-EVICT on|offExempt the current connection from client-output-buffer eviction (7.0+) so a critical admin or monitoring client is not killed under memory pressure. Use sparingly.
CLIENT NO-EVICT on
CLIENT NO-TOUCH on -- 读不更新 LRU/LFU(7.2+)
CLIENT SETNAME monitor-1
COMMAND DOCS [command ...]Return structured documentation for commands — summary, arguments, complexity, flags. COMMAND COUNT gives the total, COMMAND INFO returns the arity and key-position metadata clients use for routing.
COMMAND COUNT -- 服务器支持的命令总数
COMMAND INFO get set
COMMAND DOCS hset
CLUSTER INFOReport the health of a Redis Cluster: state (ok / fail), how many of the 16384 hash slots are assigned, and the known/reachable node counts. First check when a cluster misbehaves.
⚠ Common pitfall: cluster_state:fail means some slots are unassigned and the cluster refuses queries that touch them. CLUSTER SLOTS / CLUSTER SHARDS show the slot-to-node map you need to diagnose a gap.
CLUSTER INFO
CLUSTER SLOTS
CLUSTER KEYSLOT user:1001 -- 这个 key 落哪个槽
KEYS in productionRunning KEYS * (or any pattern) on a production instance walks the entire keyspace synchronously and freezes Redis for seconds. Always use SCAN.
⚠ Common pitfall: Even pattern-bounded KEYS (KEYS user:*) walks ALL keys and matches each one. The cost is proportional to total keyspace, not match count. SCAN is incremental and yields between buckets.
KEYS * -- 不要这样
SCAN 0 MATCH user:* COUNT 500 -- 正确做法
SCAN may miss or duplicateSCAN guarantees that every key present from start to end of the full iteration is returned at least once, but elements added or removed DURING the scan may be missed or duplicated. Dedupe on the client.
# 用 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 hashHGETALL on a hash with 100k+ fields returns megabytes in one reply, blocking the Redis event loop and the client. Switch to HSCAN, or shard the hash into N smaller hashes.
HSCAN big_hash 0 COUNT 500
-- 按 hash_tag 拆分: user:1001:profile, user:1001:prefs, user:1001:cache
Expired keys not freed immediatelyWhen a TTL fires the key is NOT freed instantly — Redis uses lazy expiration (delete on next access) plus a probabilistic background sweep. Memory usage may include "expired but not yet freed" keys.
⚠ Common pitfall: INFO memory used_memory > sum(key sizes) is normal. If the gap is large, raise hz (CONFIG SET hz 100) to make the background sweep more aggressive, or accept some lag.
DEBUG SLEEP 0 -- 触发一次访问帮过期
CONFIG SET hz 100 -- 默认 10,调到 100 更激进
Memory fragmentationLong-running Redis instances accumulate jemalloc fragmentation — used_memory_rss can be 1.5-2x used_memory. Check mem_fragmentation_ratio in INFO memory; > 1.5 warrants action.
⚠ Common pitfall: Enable active defrag at runtime: CONFIG SET activedefrag yes. It targets the issue while the server runs. The legacy fix — restart with persistence — also works but causes a failover window.
INFO memory -- 看 mem_fragmentation_ratio
CONFIG SET activedefrag yes
AOF vs RDB trade-offsRDB is a point-in-time snapshot — small, fast to load, can lose minutes on crash. AOF logs every write — large, slower to replay, durability tunable from "every write" to "every second" to "OS decides".
⚠ Common pitfall: Production default: BOTH on. RDB for fast recovery and ship-out backups, AOF (appendfsync everysec) for ≤1s data loss on crash. AOF needs periodic BGREWRITEAOF to compact.
CONFIG SET appendonly yes
CONFIG SET appendfsync everysec
BGREWRITEAOF -- 压缩 AOF
Cache stampede on hot keyWhen a popular cached key expires, every concurrent request misses at once and slams the database to recompute the same value — a thundering herd. One slow backend query becomes thousands.
⚠ Common pitfall: Mitigate with a short-lived per-key lock (SET lock NX EX) so only one request recomputes while others serve stale or wait, or refresh slightly BEFORE expiry (probabilistic early expiration). Add jitter to TTLs so keys do not all expire together.
SET lock:recompute:home worker-7 NX EX 5 -- 只放一个进来重算
EXPIRE cache:home 60 -- 加随机 +rand(0,10) 防同时过期
Hot key / single slot bottleneckIn a cluster, every operation on one extremely popular key lands on a single shard, so that one node saturates while the rest idle. A global counter or a celebrity user is a classic hot key.
⚠ Common pitfall: Shard the hot key into N suffixed sub-keys (counter:0..counter:9) and sum on read, or cache the value in a client-local layer with a short TTL. Hash tags decide colocation — do NOT accidentally tag everything into one slot.
INCR counter:{shard:$((RANDOM%10))} -- 写打散到 10 个子 keyCLUSTER KEYSLOT "{user}:1001" -- 确认 hash tag 落槽Big keys block the event loopRedis is single-threaded for command execution, so one O(N) operation on a giant key (HGETALL on a million-field hash, DEL on a huge set) stalls EVERY other client for the whole duration.
⚠ Common pitfall: Find big keys offline with redis-cli --bigkeys or --memkeys (they sample, not block). Delete them with UNLINK, read them with the *SCAN family, and design schemas so no single key grows unbounded.
redis-cli --bigkeys -- 扫描找大 key
redis-cli --memkeys -- 按内存找
UNLINK the_big_key
maxmemory-policy eviction surprisesWhen memory hits maxmemory, what happens depends entirely on maxmemory-policy. noeviction rejects writes with OOM errors; allkeys-lru evicts any key; volatile-lru only evicts keys that have a TTL set.
⚠ Common pitfall: A volatile-* policy with NO keys carrying a TTL behaves like noeviction — writes start failing with no key ever evicted. If you rely on LRU eviction, either set TTLs on cache keys or use an allkeys-* policy.
CONFIG GET maxmemory-policy
CONFIG SET maxmemory-policy allkeys-lru -- 纯缓存场景
INFO stats -- 看 evicted_keys 是否在涨
EXPIRE on replica does not deleteReplicas do NOT independently expire keys — they wait for the primary to send an explicit DEL when the key expires. A read on a replica can briefly return a logically-expired key.
⚠ Common pitfall: Since Redis 3.2 replicas return nil for a logically-expired key on read even before the DEL arrives, so correctness holds — but the key still occupies memory on the replica until the primary propagates the delete. Do not assume replica memory tracks the primary exactly.
TTL session:abc -- 主与副本上结果可能短暂不同
INFO keyspace -- 对比主从 expires 计数
Numeric strings and float precisionRedis stores numbers as strings and parses them per command. A counter that grows past 2^63-1 overflows INCR; an INCRBYFLOAT chain accumulates IEEE-754 rounding error just like any other float math.
⚠ Common pitfall: For money never use INCRBYFLOAT on dollar amounts — store integer cents and INCRBY. For very large counters, watch the int64 ceiling or shard the counter so no single key overflows.
INCRBY wallet:42:cents 99 -- 存分,整数
INCR big_counter -- 接近 9.22e18 时会溢出报错
Searchable Redis cheat sheet, 80+ entries that backend engineers, SREs and on-call folks actually type into redis-cli — not yet another "SET foo bar" tour. Fifteen sections: keys (GET / SET / DEL / EXPIRE / TTL / KEYS vs SCAN / TYPE / RENAME), strings (INCR / DECR / APPEND / GETSET / STRLEN / SETEX / SETNX vs SET NX EX), hashes (HSET multi-field, HMGET, HDEL, HGETALL danger, HINCRBYFLOAT), lists as queues (LPUSH / RPUSH / LPOP / RPOP / LRANGE / LREM / BLPOP for blocking consumers), sets (SADD / SISMEMBER / SUNION / SINTER / SDIFF with intersection order tricks), sorted sets (ZADD with NX/XX/GT/LT, ZRANGE 6.2+ swiss-army, ZRANGEBYSCORE for scheduled jobs, ZINCRBY, ZRANK), bitmaps for compact DAU flags (SETBIT / BITCOUNT / BITOP), HyperLogLog probabilistic cardinality (PFADD / PFCOUNT / PFMERGE), Pub/Sub fire-and-forget (PUBLISH / SUBSCRIBE / PSUBSCRIBE) and when to switch to Streams, Streams 5.0+ durable replayable log (XADD with MAXLEN, XREAD with BLOCK $, XREADGROUP for at-least-once consumer groups, XACK, XLEN), Lua scripts (EVAL, EVALSHA, SCRIPT LOAD with NOSCRIPT fallback), transactions (MULTI, EXEC, DISCARD, WATCH for optimistic locking — and the fact Redis transactions do NOT rollback on error), persistence and replication (SAVE vs BGSAVE, LASTSAVE for health checks, SLAVEOF / REPLICAOF, AOF vs RDB), server ops (CLIENT LIST, INFO sections, CONFIG GET/SET, FLUSHALL ASYNC), and the money-burning pitfalls (KEYS in production, SCAN duplicate semantics, HGETALL on a big hash, lazy expiration, memory fragmentation, AOF vs RDB trade-offs). Every entry: command + EN/ZH description + 1-3 real redis-cli-pasteable examples + common pitfall. Search across all fields plus category chips. Pure client-side — no Redis connection, no upload. Pair with our PostgreSQL, SQL, Docker, kubectl and nginx cheat sheets.
Paste or drop your content into the tool panel.
Click the button. All processing is local in your browser.
Copy the result or download to disk in one click.
Use it before upload, handoff, archive, support review, or any moment where a file needs one local check before it leaves your machine.
These links move the current task into a more complete workflow.
You are adding a cron-guard so two app boxes never run the same nightly billing job twice. You search "NX" and "EVAL", copy SET billing_lock $token NX EX 300, then grab the Lua release snippet that checks the token before DEL. The pitfall line reminds you that plain DEL would delete another box's freshly acquired lock after your 300s TTL expires.
An on-call page says one Redis instance is at 90% maxmemory. You search "Streams" and "MAXLEN", find XADD stream MAXLEN ~ 1000000 and XTRIM stream MAXLEN ~ 1000000, and learn the approximate (~) form is far cheaper than exact trimming. The pitfall note explains an uncapped Stream grows forever, which is exactly what ate the 8GB.
A teammate shipped KEYS user:* in a request handler and the 10M-key instance stalled every connected client. You filter "Keys", read why KEYS is O(N) and blocks the single thread, then copy the SCAN 0 MATCH user:* COUNT 1000 cursor loop plus the reminder that SCAN can return the same key twice, so you dedupe client-side.
Your cache instance also holds session and lock keys, and allkeys-lru just evicted an in-flight job lock at 3am. You search "eviction", set TTLs on the cache keys only, switch to maxmemory-policy volatile-lru, and confirm with CONFIG GET maxmemory-policy. The pitfall chip spelled out exactly the allkeys-lru footgun you hit.
Running plain SET key value to refresh a value but wiping its TTL — use SET key value KEEPTTL or re-specify EX so the expiry survives.
Pairing SETNX with a separate EXPIRE for a lock — if the client crashes between the two commands the lock never expires; use the atomic SET key token NX EX ttl instead.
Calling HGETALL on a hash you let grow to millions of fields — it returns megabytes in one blocking reply; use HSCAN with COUNT or HMGET only the fields you need.
This cheat sheet is one static page. Your search text is matched against an in-memory array of commands entirely in your browser — it never touches a Redis server, never goes into the URL, and is never uploaded. Open DevTools Network while you type and you will see zero requests, so it is safe behind bastion-only Redis hosts and air-gapped networks.
Folks in your role tend to reach for these alongside this tool.