入门
连接到 Redis 服务器
redis-cli 是标准的命令行客户端。默认连接到 127.0.0.1:6379。使用 -h 指定主机,-p 指定端口,-a 指定密码(或设置 REDISCLI_AUTH 环境变量以避免在 shell 历史记录中泄露密钥)。TLS 需要 --tls 加上证书/密钥。URL 方案 redis:// 在 Redis 6+ 中受支持,rediss:// 用于 TLS。
# connect to local server (default port 6379)
redis-cli
# connect to a remote host with custom port
redis-cli -h redis.example.com -p 6380
# connect with authentication
redis-cli -a "yourpassword"
redis-cli -h host -p 6379 -a "yourpassword"
# connect over TLS
redis-cli --tls --cert client.crt --key client.key -h host -p 6379
# connect using a URL (Redis 6+)
redis-cli redis://user:password@host:6379/0
redis-cli rediss://user:password@host:6379/0 # TLS连接选项与模式
redis-cli 支持交互模式和单次执行模式。将命令作为参数传入会执行后退出。管道模式从 stdin 读取 RESP/文本命令 — 非常适合脚本和批量加载。-n 选择逻辑数据库。--stat 和 --latency 是内置的监控辅助工具。--raw 输出不带类型前缀的值,便于管道传输。
# select a specific database index (0-15)
redis-cli -n 1 # use database 1
# run a single command and exit (non-interactive)
redis-cli GET mykey
redis-cli -h host PING
# pass command with arguments
redis-cli SET counter 100
redis-cli INCR counter
# read commands from stdin (pipe mode)
echo "SET k1 v1" | redis-cli
cat commands.txt | redis-cli
# repeat a command every second (watch mode)
redis-cli --stat
redis-cli --latency
# pretty-print JSON output (Redis 6+)
redis-cli --raw GET user:jsonPING 与连接测试
PING 是最简单的健康检查 — 正常的 Redis 会返回 PONG。--latency 持续测量往返时间(适用于诊断网络问题)。--latency-dist 显示直方图。--latency-history 记录滚动统计信息。在性能调优前,使用这些工具来基线化您的连接质量。
# basic connectivity test
redis-cli ping
# PONG
# ping with a message (echoed back)
redis-cli PING "hello"
# "hello"
# test auth + connection in one shot
redis-cli -a password PING
# check round-trip latency (continuous)
redis-cli --latency
# output: min: 0, max: 1, avg: 0.27 (3421 samples)
# latency histogram
redis-cli --latency-dist
# latency history (rolling, every 15s by default)
redis-cli --latency-history -i 1交互模式技巧
交互模式是 Redis 命令的 REPL。HELP <command> 或 HELP @<group> 显示内置文档(无需联网)。CONNECT 在会话中切换服务器。CLEAR 清屏。CLI 会自动检测带引号字符串的多行输入。使用 ':' 前缀运行 CLI 特定命令(如 :raw),以区别于 Redis 命令。
# start interactive mode
redis-cli
# inside the CLI:
127.0.0.1:6379> HELP SET # command help
127.0.0.1:6379> HELP @string # help for a command group
127.0.0.1:6379> SCAN 0 # any Redis command
# clear the screen
127.0.0.1:6379> CLEAR
# connect to a different server without restarting
127.0.0.1:6379> CONNECT host 6380
# exit
127.0.0.1:6379> EXIT
127.0.0.1:6379> QUIT
# multi-line input: end with a backslash
127.0.0.1:6379> SET mykey "this is \
a multi-line value"
# toggle raw output mode
127.0.0.1:6379> :raw数据库选择与基础
Redis 数据库是共享相同内存的逻辑命名空间(0-15) — 不是隔离的 schema。大多数应用只使用 db 0。FLUSHDB 清空当前数据库;FLUSHALL 清空所有数据库(小心使用!)。不建议使用多个数据库,推荐使用独立实例或键前缀,因为它们共享单一线程。
# Redis has 16 logical databases (0-15) by default
SELECT 0 # switch to database 0
SELECT 15 # switch to database 15
# move a key to another database
MOVE mykey 1
# flush the current database (DESTRUCTIVE)
FLUSHDB
# flush ALL databases (VERY DESTRUCTIVE)
FLUSHALL
# get the number of keys in the current db
DBSIZE
# swap two databases (Redis 4.0+)
SWAPDB 0 1帮助与命令发现
COMMAND 用于内省服务器的命令表 — 适用于发现功能和参数数量。COMMAND DOCS(7.0+)返回结构化文档。CLI 的 HELP 命令显示按类别分组的离线文档(@string、@hash、@server 等)。运行 'redis-cli --help' 查看所有 CLI 标志和选项。
# list all commands (huge output)
COMMAND
# get details of a specific command
COMMAND INFO GET
COMMAND INFO SET HSET LPUSH
# structured documentation (Redis 7+)
COMMAND DOCS SET
COMMAND DOCS @string
# count available commands
COMMAND COUNT
# CLI help
redis-cli --help
redis-cli --version
# inside interactive mode
HELP
HELP @server
HELP @string
HELP @hash
HELP GETSET字符串
基础 SET 与 GET
SET/GET 是基础的字符串操作。NX = 不存在时设置(用于锁);XX = 存在时设置。EX/PX/EXAT/PXAT 原子性地设置过期时间(SET 与 EXPIRE 之间无竞争)。GETSET 在设置新值的同时返回之前的值。字符串是二进制安全的,最大 512MB。
# set and get a key
SET mykey "Hello"
GET mykey # "Hello"
# set only if not exists (NX) or only if exists (XX)
SET mykey "new" NX # set only if key does NOT exist
SET mykey "new" XX # set only if key DOES exist
# set with expiration (seconds / milliseconds)
SET token "abc" EX 3600 # expires in 3600s
SET token "abc" PX 60000 # expires in 60000ms
# set with expiration at a Unix timestamp
SET token "abc" EXAT 1700000000
# get and set atomically (returns old value)
GETSET counter 0 # returns old value, sets new
# delete a key
DEL mykey数字计数器(INCR/DECR)
INCR/DECR 是原子性的 — 无需锁即可安全用于并发访问。这使得 Redis 非常适合计数器、限流(INCR + EXPIRE)和生成序列 ID。INCRBYFLOAT 支持小数,但使用双精度(注意舍入问题)。如果值不是整数,INCR 会返回错误。
# increment a numeric string atomically
SET counter 100
INCR counter # 101
INCRBY counter 10 # 111
# decrement
DECR counter # 110
DECRBY counter 5 # 105
# floating point increment
SET price "10.50"
INCRBYFLOAT price 0.25 # "10.75"
INCRBYFLOAT price -1.00 # "9.75"
# all increment ops are atomic and concurrency-safe
# use for: counters, rate limiting, sequence IDs, stats
# INCR on a non-integer value returns an error
SET mystr "hello"
INCR mystr # ERROR: value is not an integerAPPEND 与字符串操作
APPEND 扩展字符串(如果不存在则创建)。STRLEN 报告长度。GETRANGE/SETRANGE 操作子串(二进制安全)。GETDEL(6.2+)原子性地返回并删除 — 适用于一次性读取模式。GETEX 原子性地返回值并设置 TTL — 便于在读取时刷新会话过期时间。
# append to a string value
SET msg "Hello"
APPEND msg " World" # "Hello World"
# get string length
STRLEN msg # 11
# get substring
GETRANGE msg 0 4 # "Hello"
GETRANGE msg 6 -1 # "World"
# overwrite a substring (grows if needed)
SETRANGE msg 6 "Redis" # "Hello Redis"
# get and delete atomically
GETDEL mykey # returns value AND deletes key
# get and set expiration atomically (6.2+)
GETEX mykey EX 3600 # returns value, sets TTL
# append only if key exists
APPEND existing_key "more"MSET 与 MGET(批量操作)
MSET/MGET 将操作批量到一次往返中 — 对性能至关重要(Redis 是单线程的,因此每条命令的开销很重要)。MSETNX 是原子性的:要么所有键都设置,要么都不设置。对于许多小的键值对,考虑将它们分组成一个哈希(HSET),通过 listpack 编码获得更好的内存效率。
# set multiple keys at once (atomic)
MSET k1 "v1" k2 "v2" k3 "v3"
# get multiple keys at once
MGET k1 k2 k3 # 1) "v1" 2) "v2" 3) "v3"
# set multiple only if ALL keys are new
MSETNX k1 "v1" k4 "v4" # 1 if all set, 0 if any existed
# MSET/MGET reduce round-trips — much faster than
# separate SET/GET calls in a loop
# get the length of a string value
STRLEN k1 # 2
# set multiple fields of different keys is not possible;
# for per-field updates use hashes (HSET)
# pipeline alternative in CLI:
printf '%s\n' "SET k1 v1" "SET k2 v2" "GET k1" | redis-cli --pipe-mode位图(基于字符串)
位图用约 512MB 存储多达 40 亿用户的布尔标志 — 极其节省空间。BITOP AND/OR/XOR/NOT 组合位图(例如,多天活跃的用户)。BITCOUNT 计算活跃用户数。BITFIELD 将多个小计数器打包到一个字符串中。位置 = 用户 ID。使用场景:日活跃用户、功能标志。
# bitmaps are strings operated on at the bit level
# great for boolean flags on a large user base
# set bit at position (0 or 1)
SETBIT user:1:active 7 1 # user 1 active
GETBIT user:1:active 7 # 1
# count set bits
BITCOUNT user:1:active # total bits set to 1
# bitwise operations between strings
SETBIT users:d1 5 1
SETBIT users:d2 5 1
BITOP AND active-both users:d1 users:d2
BITCOUNT active-both # users active on both days
BITOP OR active-either users:d1 users:d2
BITOP XOR diff users:d1 users:d2
# find the first set bit
BITPOS user:1:active 1 # position of first 1-bit
# bitfield: multiple counters in one string
BITFIELD mycnt SET u8 0 100
BITFIELD mycnt INCRBY u8 0 10字符串过期与 TTL
TTL 对于不存在的键返回 -2,对于没有过期时间的键返回 -1。SET 与 EX 是原子性的(SET 与 EXPIRE 之间无竞争)。KEEPTTL(6.0+)允许您在更新值的同时保留其 TTL — 适用于在不重置过期窗口的情况下刷新缓存值。过期是惰性的(访问时检查)加上定期后台扫描。
# set a key with expiration in one atomic command
SET session "data" EX 3600 # 1 hour
SET cache "value" PX 60000 # 60 seconds (ms)
# set expiration on an existing key
EXPIRE mykey 60 # 60 seconds
PEXPIRE mykey 60000 # 60 seconds (ms)
EXPIREAT mykey 1700000000 # Unix timestamp
# view remaining time to live
TTL mykey # seconds (-1 = no expiry, -2 = no key)
PTTL mykey # milliseconds
# remove expiration (make key persistent)
PERSIST mykey
# TTL with KEEPTTL option (6.0+): set value, keep existing TTL
SET mykey "newval" KEEPTTL列表
Push 与 Pop 操作
列表是双端的:LPUSH+RPOP = 队列(FIFO),LPUSH+LPOP = 栈(LIFO)。BLPOP/BRPOP 会阻塞直到有可用项 — 这是 Redis 队列的基础(无需轮询)。始终为 BLPOP 设置超时以处理断开连接。列表的头/尾操作是 O(1),但中间访问是 O(N)。
# lists are ordered sequences of strings (linked lists)
LPUSH mylist "a" "b" # list: [b, a]
RPUSH mylist "c" # list: [b, a, c]
# pop from head or tail
LPOP mylist # "b" (removes from head)
RPOP mylist # "c" (removes from tail)
# pop multiple (6.2+)
LPOP mylist 2 # pop 2 from head
# get length
LLEN mylist # 0
# blocking pop (waits if empty, up to timeout)
BLPOP queue:tasks 30 # blocks up to 30 seconds
BRPOP queue:tasks 0 # blocks forever
# push only if the list exists (don't create new key)
LPUSHX mylist "x" # 0 if key doesn't existLRANGE 与索引
LRANGE 0 -1 返回整个列表。LINDEX 对中间元素是 O(N) — 列表针对端点访问进行了优化。LSET 按索引修改。LINSERT 是 O(N)(在大型列表上谨慎使用)。LPOS 查找元素位置而不移除它们。对于频繁按索引随机访问,列表不是正确的选择 — 考虑使用有序集合。
# get a range of elements (0-based, negative from end)
LRANGE mylist 0 -1 # all elements
LRANGE mylist 0 2 # first 3 elements
LRANGE mylist -3 -1 # last 3 elements
# get element by index (O(N) traversal)
LINDEX mylist 0 # first element
LINDEX mylist -1 # last element
# set an element by index
LSET mylist 1 "newval"
# get length
LLEN mylist
# insert before or after a pivot element
LINSERT mylist BEFORE "pivot" "new"
LINSERT mylist AFTER "pivot" "new"
# find the position of an element (6.0+)
LPOS mylist "value"
LPOS mylist "value" RANK 2 # find 2nd occurrence阻塞 Pop 与队列
BRPOPLPUSH/BLMOVE 原子性地将项从源移动到目标 — 消费者可以安全地处理它,如果它崩溃,项仍在目标列表中(可靠队列模式)。这优于 BRPOP+处理,因为 BRPOP 后崩溃会丢失项。处理完成后,从目标列表中删除。
# simple FIFO queue: LPUSH to add, RPOP to consume
LPUSH tasks "job1" "job2"
RPOP tasks # "job1" (FIFO order)
# reliable queue with BRPOP (blocking, waits for work)
BRPOP tasks 0 # blocks until a task is available
# move items between lists atomically
RPOPLPUSH source destination # move one item
# blocking move (reliable queue pattern)
BRPOPLPUSH source destination 30 # blocks up to 30s
# Redis 6.2+: BLMOVE replaces BRPOPLPUSH
BLMOVE source destination RIGHT LEFT 30
# capped queue (e.g., recent events)
LPUSH recent "event"
LTRIM recent 0 99 # keep latest 100LREM、LSET 与 LTRIM
LTRIM 是限制列表大小的常用模式(例如,只保留最新的 100 个事件)。LREM 按值移除(count:正数=头部,负数=尾部,0=全部)。LMPOP(7.0+)在一次调用中从多 个列表弹出,适用于优先级队列。对于频繁的列表中间操作,列表不是正确的选择。
# trim to a range (removes everything outside)
LTRIM mylist 0 99 # keep only first 100 elements
LTRIM mylist -50 -1 # keep only last 50
# remove elements by value
LREM mylist 2 "value" # remove first 2 occurrences of "value"
LREM mylist -2 "value" # remove last 2 occurrences
LREM mylist 0 "value" # remove ALL occurrences
# pop and push in one atomic operation
RPOPLPUSH source dest
# pop from multiple lists (7.0+)
LMPOP 2 mylist1 mylist2 LEFT COUNT 3
# blocking pop from multiple lists (7.0+)
BLMPOP 30 2 mylist1 mylist2 LEFT COUNT 3
# remove a key only if it's a list (type-safe)
DEL mylist # works on any type列表使用场景
列表最适合具有端点访问模式的序列:队列、栈、动态流、缓冲区。LTRIM 使它们非常适合有上限的日志/动态流。对于限流,时间戳列表 + LTRIM 是一个简单的滑动窗口。对于有序/优先级队列,使用有序集合代替。列表不支持去重 — 使用集合来实现。
# 1. Task queue (FIFO)
LPUSH tasks "process_order:123"
BRPOP tasks 30
# 2. Recent activity feed (capped)
LPUSH user:1:feed "post:99" "post:98"
LTRIM user:1:feed 0 49 # latest 50
# 3. Stack (LIFO)
LPUSH stack "item"
LPOP stack
# 4. Circular buffer (limited size)
LPUSH buffer "new"
LTRIM buffer 0 999 # keep 1000 items
# 5. Rate limiter (sliding window)
LPUSH rate:user:1 1700000000
LTRIM rate:user:1 0 99
LLEN rate:user:1 # request count in window
# 6. Chat history (capped)
LPUSH chat:room:1 "alice: hello"
LTRIM chat:room:1 0 999 # keep last 1000 messages集合
SADD、SMEMBERS 与 SISMEMBER
集合存储唯一值 — 非常适合标签、分类和去重。SISMEMBER 是 O(1) — 比检查列表快得多。SMEMBERS 在大型集合上可能阻塞;使用 SSCAN 进行迭代。SMISMEMBER(7.4+)批量检查成员资格,减少往返次数。当所有成员都是整数时,集合使用 intset(有序数组)— 非常节省内存。
# sets: unordered collection of unique strings
SADD tags "redis" "db" "cache"
SADD tags "redis" # ignored (already exists)
# check membership (O(1))
SISMEMBER tags "redis" # 1 if member, 0 if not
# get all members (avoid on large sets)
SMEMBERS tags # 1) "redis" 2) "db" 3) "cache"
# get the number of members
SCARD tags # 3
# remove a member
SREM tags "cache" # 1
# check multiple memberships at once (7.4+)
SMISMEMBER tags "redis" "db" "missing"
# 1) 1 2) 1 3) 0集合操作(并集/交集/差集)
集合操作是 Redis 的超能力 — 在 O(N) 时间内完成并集、交集和差集,无需客户端处理。使用场景:共同好友(SINTER)、唯一访客(SUNION)、基于标签的过滤(标签集合的 SINTER)。SINTERCARD(7.0+)只返回计数,当您不需要成员时更快。存储结果以避免重复计算。
# set union, intersection, difference
SADD set1 "a" "b" "c"
SADD set2 "b" "c" "d"
# union (all unique members from all sets)
SUNION set1 set2 # a, b, c, d
# intersection (members in ALL sets)
SINTER set1 set2 # b, c
# difference (members in set1 but NOT in set2)
SDIFF set1 set2 # a
# store the result in a new set
SUNIONSTORE result set1 set2
SINTERSTORE result set1 set2
SDIFFSTORE result set1 set2
# count intersection without returning members (7.0+)
SINTERCARD 2 set1 set2 LIMIT 0
# operations on multiple sets at once
SUNION s1 s2 s3 s4SREM、SMOVE 与 SPOP
SMOVE 是原子性的 — 适用于状态转换(例如,将用户从 'pending' 集合移动到 'active' 集合)。SPOP 移除随机成员(适用于抽奖/分片)。SRANDMEMBER 不移除。BSPOP(7.0+)像 BLPOP 一样阻塞,但用于集合 — 适用于顺序不重要的事件驱动模式。SREM 返回实际移除的数量。
# remove one or more members
SADD myset "a" "b" "c" "d"
SREM myset "a" "b" # 2 (number removed)
# move a member from one set to another atomically
SMOVE source dest "member"
# pop a random member (removes it)
SPOP myset # removes and returns one
SPOP myset 3 # removes and returns 3 (6.2+)
# get a random member (does NOT remove)
SRANDMEMBER myset # one random member
SRANDMEMBER myset 3 # 3 random (may repeat)
SRANDMEMBER myset -3 # 3 unique random
# blocking set pop (7.0+) - waits until a member exists
BSPOP myset 30 # blocks up to 30 seconds
# delete the entire set
DEL mysetSSCAN 迭代
SSCAN 是迭代大型集合的安全方式 — SMEMBERS 会阻塞服务器处理大型集合。SSCAN 基于游标:重复调用直到游标返回 0。COUNT 是一个提示(可能返回更多或更少)。SSCAN 提供弱保证:可能返回重复项或遗漏迭代期间添加的成员,但永远不会阻塞。始终在代码中处理游标循环。
# SSCAN: cursor-based iteration for large sets
SSCAN myset 0 MATCH "pre*" COUNT 100
# returns: [nextCursor, [member1, member2, ...]]
# continue until cursor returns to 0
SSCAN myset <nextCursor> MATCH "pre*" COUNT 100
# iterate all members safely
cursor=0
while true:
cursor, members = SSCAN myset cursor COUNT 100
process(members)
if cursor == 0: break
# scan with type filtering (7.0+ for SCAN, sets always same type)
# COUNT is a hint, not exact (may return more or fewer)
# MATCH pattern supports glob: *, ?, [abc]
SSCAN myset 0 MATCH "user:*" COUNT 1000
# for small sets, SMEMBERS is fine
# for large sets (>10k), always use SSCAN集合使用场景
集合是处理唯一性和关系的首选。SINTER 模式用于共同关注者/好友是经典用法。对于基数巨大的“每日访客”,考虑使用 HyperLogLog(近似但固定 12KB)。对于有序唯一数据(排行榜),使用有序集合。集合是无序的 — 如果顺序重要,使用列表或有序集合。
# 1. Tags / categories
SADD post:1:tags "redis" "db"
SADD post:2:tags "redis" "cache"
SINTER post:1:tags post:2:tags # posts sharing "redis"
# 2. Unique visitors per day
SADD visitors:2025-01-01 "user:1" "user:2"
SCARD visitors:2025-01-01 # visitor count
# 3. Followers / following (mutual friends)
SADD user:1:following "user:2" "user:3"
SADD user:2:following "user:1" "user:3"
SINTER user:1:following user:2:following # mutual follows
# 4. Blacklist / whitelist
SADD blacklist "ip:1.2.3.4"
SISMEMBER blacklist "ip:1.2.3.4" # 1 = blocked
# 5. Lottery / random selection
SADD participants "u1" "u2" "u3" "u4"
SPOP participants 1 # draw a winner有序集合
ZADD、ZSCORE 与 ZRANK
有序集合(zsets)是 Redis 最强大的结构 — 唯一成员按分数排序,具有 O(log N) 操作。ZADD 选项(NX/XX/GT/LT)启用条件更新:GT 仅在新分数更大时更新(非常适合“最佳分数”跟踪)。分数是双精度浮点数;成员是唯一的。平局按字典序打破。
# sorted set: unique members ordered by a float score
ZADD leaderboard 100 "alice" 200 "bob" 150 "carol"
# update a score (re-add with new score)
ZADD leaderboard 250 "alice" # alice's score becomes 250
# add with options (NX: only new, XX: only existing, GT/LT)
ZADD leaderboard NX 300 "new" # only if new
ZADD leaderboard GT 300 "alice" # only if new score > current
ZADD leaderboard LT 50 "alice" # only if new score < current
# get a member's score
ZSCORE leaderboard "alice" # "250"
# get a member's rank (0-based, ascending)
ZRANK leaderboard "alice" # rank from lowest
ZREVRANK leaderboard "alice" # rank from highest (0 = top)
# get the number of members
ZCARD leaderboard # 3
# get multiple scores at once (6.2+)
ZMSCORE leaderboard "alice" "bob" # ["250", "200"]ZRANGE 与 ZREVRANGE
ZRANGE 按排名(索引)返回;使用 BYSCORE 时按分数范围返回。Redis 6.2 将 ZRANGEBYSCORE/ZREVRANGE 统一为带 BYSCORE/BYLEX/REV 选项的 ZRANGE。WITHSCORES 在输出中包含分数。对于排行榜,ZRANGE 0 9 REV 给出前 10 名。ZRANGESTORE(6.2+)将结果存储到新键中供后续处理。
# get members by index range (ascending)
ZRANGE leaderboard 0 -1 # all, ascending
ZRANGE leaderboard 0 2 # first 3 (lowest scores)
ZRANGE leaderboard -3 -1 # last 3 (highest scores)
# descending order
ZREVRANGE leaderboard 0 2 # top 3 (highest scores)
# with scores
ZRANGE leaderboard 0 -1 WITHSCORES
# Redis 6.2+: unified ZRANGE with BYSCORE/REV options
ZRANGE leaderboard 100 200 BYSCORE
ZRANGE leaderboard 200 100 BYSCORE REV
# store a range in a new key (6.2+)
ZRANGESTORE top3 leaderboard 0 2 REV
# get by rank with scores
ZRANGE leaderboard 0 9 REV WITHSCORES # top 10
# limit results (pagination)
ZRANGE leaderboard 0 -1 BYSCORE LIMIT 0 10ZRANGEBYSCORE 与 ZCOUNT
ZRANGEBYSCORE 按分数范围返回;'(' 前缀使分数变为排他。ZCOUNT 给出范围内的计数而不获取成员。ZINCRBY 原子性地调整分数并重新排序 — 非常适合实时排行榜。ZREMRANGEBYRANK 非常适合保持有序集合有界(例如,只保留前 1000 名)。
# get members by score range
ZRANGEBYSCORE leaderboard 100 200 # scores 100-200
ZRANGEBYSCORE leaderboard 100 +inf # scores >= 100
ZRANGEBYSCORE leaderboard -inf 200 # scores <= 200
ZRANGEBYSCORE leaderboard (100 200 # scores > 100 (exclusive)
# with pagination
ZRANGEBYSCORE leaderboard 100 200 LIMIT 0 10
# count members in a score range
ZCOUNT leaderboard 100 200
# increment a member's score atomically
ZINCRBY leaderboard 50 "alice" # alice += 50
# remove a member
ZREM leaderboard "bob"
# remove by rank range
ZREMRANGEBYRANK leaderboard 0 9 # remove lowest 10
ZREMRANGEBYSCORE leaderboard 0 100 # remove scores <= 100ZUNIONSTORE 与 ZINTERSTORE
ZUNIONSTORE/ZINTERSTORE 以可配置的聚合(SUM/MIN/MAX)和权重组合有序集合 — 对于加权评分非常强大(例如,相关性 = 文本匹配*2 + 时效性*1)。Redis 6.2 添加了 ZUNION/ZINTER/ZDIFF,它们返回结果而不存储(少一条命令)。这些是 O(N*M) — 在多个大型集合上要小心。集合数量必须位于集合名称之前。
# combine multiple sorted sets
ZADD set1 1 "a" 2 "b"
ZADD set2 2 "a" 3 "c"
# union: sum scores by default (a = 1+2 = 3)
ZUNIONSTORE result 2 set1 set2
ZRANGE result 0 -1 WITHSCORES # a=3, b=2, c=3
# specify aggregation: SUM (default), MIN, MAX
ZUNIONSTORE result 2 set1 set2 AGGREGATE MAX # a=2, b=2, c=3
# apply weights to each set's scores
ZUNIONSTORE result 2 set1 set2 WEIGHTS 2 1 # set1 scores * 2
# intersection (only members in ALL sets)
ZINTERSTORE result 2 set1 set2 # only "a"
# Redis 6.2+: return without storing
ZUNION 2 set1 set2 WITHSCORES
ZINTER 2 set1 set2 WITHSCORES
ZDIFF 2 set1 set2 WITHSCORES
# count intersection (7.0+)
ZINTERCARD 2 set1 set2 LIMIT 0字典序范围(BYLEX)
BYLEX 在分数相等时启用对成员的范围查询 — 将有序集合转变为自动排序的字符串索引。使用场景:自动补全(以分数 0 存储单词,查询前缀范围)、有序字典。'[a' 表示包含,'(a' 表示排他,'-' 表示开头,'+' 表示结尾。这仅在所有分数相同时有意义。
# when all scores are equal, sorted sets act as a sorted string set
ZADD myset 0 "apple" 0 "banana" 0 "cherry" 0 "date"
# range by lexicographical order
ZRANGEBYLEX myset "[a" "[c" # apple, banana, cherry
ZRANGEBYLEX myset "(a" "[c" # banana, cherry (a exclusive)
ZRANGEBYLEX myset "-" "[b" # from start to banana
ZRANGEBYLEX myset "[c" "+" # cherry to end
# count in a lex range
ZLEXCOUNT myset "[a" "[c" # 3
# remove by lex range
ZREMRANGEBYLEX myset "[a" "[b"
# Redis 6.2+: use ZRANGE with BYLEX
ZRANGE myset "[a" "[c" BYLEX
# limit results (pagination)
ZRANGEBYLEX myset "[a" "[c" LIMIT 0 10排行榜模式
有序集合使排行榜变得简单:ZADD 更新分数,ZREVRANGE 获取前 N 名,ZREVRANK 获取玩家位置。ZINCRBY 原子性地更新分数。对于分时段排行榜(每日/每周),每个时段使用一个带 TTL 的键。分页使用带偏移量的 ZREVRANGE。这是 Redis 最自然和最强大的使用场景之一。
# build a leaderboard
ZADD leaderboard 100 "alice" 250 "bob" 150 "carol"
# top 10 players (highest scores)
ZREVRANGE leaderboard 0 9 WITHSCORES
# a player's rank (0 = top)
ZREVRANK leaderboard "alice"
# update a score (atomic)
ZINCRBY leaderboard 50 "alice"
# players within a score range (e.g., silver tier 100-199)
ZRANGEBYSCORE leaderboard 100 199
# percentile rank
total = ZCARD(leaderboard)
rank = ZREVRANK(leaderboard "alice")
# percentile = (total - rank) / total * 100
# pagination (page 3, 10 per page)
ZREVRANGE leaderboard 20 29 WITHSCORES
# time-bucketed leaderboards (weekly)
ZADD leaderboard:2025-W01 100 "alice"
EXPIRE leaderboard:2025-W01 604800 # 1 week TTL哈希
HSET、HGET 与 HGETALL
哈希非常适合存储对象 — 每个对象一个键,每个属性一个字段。这优于序列化 JSON,因为您可以原子性地更新单个字段(HSET)而无需读-改-写。HGETALL 返回所有字段;对于大型哈希,优先使用 HSCAN。哈希在较小时(约 128 个字段以下)使用 listpack 编码,使其节省内存。
# a hash maps fields to values (like a small object)
HSET user:1 name "Alice" age 30 email "[email protected]"
# get a single field
HGET user:1 name # "Alice"
# get all fields and values
HGETALL user:1
# 1) "name" -> "Alice"
# 2) "age" -> "30"
# 3) "email" -> "[email protected]"
# get the number of fields
HLEN user:1 # 3
# get the string length of a field's value
HSTRLEN user:1 name # 5 (length of "Alice")
# set a field only if it doesn't exist
HSETNX user:1 status "new" # 1 if set, 0 if existed
# delete a field
HDEL user:1 email # removes the email fieldHMGET(多字段获取)
HMGET 在一次往返中批量获取多个字段 — 比单独的 HGET 调用快得多。HSET(自 4.0 起)接受多个字段-值对,使 HMSET 被弃用。HKEYS/HVALS 返回所有字段名/值。HRANDFIELD(6.2+)适用于采样(例如,随机功能标志)。HGETALL 在大型哈希上会阻塞 — 生产环境迭代使用 HSCAN。
# get multiple fields at once
HMGET user:1 name age # 1) "Alice" 2) "30"
HMGET user:1 name missing age # 1) "Alice" 2) nil 3) "30"
# set multiple fields (HSET supports multiple field-value pairs)
HSET user:1 name "Bob" age 25 email "[email protected]"
# HMSET is deprecated (use HSET with multiple fields)
# HMSET still works but returns OK instead of count
# get all field names
HKEYS user:1 # 1) "name" 2) "age" 3) "email"
# get all values
HVALS user:1 # 1) "Alice" 2) "30" 3) "[email protected]"
# check if a field exists
HEXISTS user:1 name # 1 if exists, 0 if not
# get a random field (6.2+)
HRANDFIELD user:1 # one random field name
HRANDFIELD user:1 3 WITHVALUES # 3 random fields + valuesHINCRBY(数字字段)
对哈希字段的 HINCRBY 是原子性的 — 非常适合每用户计数器(登录次数、查看次数、购物车数量)。HINCRBYFLOAT 支持小数。当最后一个字段被移除时,哈希会自动删除(HDEL)。这使哈希成为带数字计数器对象的理想结构 — 比每个计数器单独使用字符串键高效得多。
# increment a numeric field atomically
HSET user:1:stats logins 0
HINCRBY user:1:stats logins 1 # logins = 1
HINCRBY user:1:stats logins 1 # logins = 2
HINCRBY user:1:stats logins -1 # logins = 1
# increment by a specific amount
HINCRBY user:1:stats page_views 10
# floating point increment
HINCRBYFLOAT user:1:stats score 0.5
# all HINCRBY operations are atomic
# perfect for per-user/per-object counters
# use case: shopping cart (product_id -> quantity)
HSET cart:user:1 product:100 2
HINCRBY cart:user:1 product:100 1 # now 3
HINCRBY cart:user:1 product:100 -1 # now 2
HDEL cart:user:1 product:100 # remove itemHSCAN(哈希迭代)
HGETALL 对大型哈希会阻塞服务器 — 生产环境迭代使用 HSCAN。HSCAN 基于游标:重复调用直到游标返回 0。COUNT 是一个提示。结果以扁平的 [字段, 值, 字段, 值, ...] 数组形式返回 — 在代码中配对。对于有数百万字段的哈希,考虑分片到多个键。
# HSCAN: cursor-based iteration (for large hashes)
HSCAN user:1 0 MATCH "na*" COUNT 10
# returns [cursor, [field, value, field, value, ...]]
# iterate all fields safely
cursor=0
while true:
cursor, fields = HSCAN user:1 cursor COUNT 100
process(fields)
if cursor == 0: break
# HGETALL is fine for small hashes (< 100 fields)
# HSCAN is required for large hashes to avoid blocking
# match field names with glob patterns
HSCAN user:1 0 MATCH "user:*" COUNT 1000
# no VALUES-only option; results are [field, value, field, value, ...]
# pair them up in your code
# COUNT is a hint — may return more or fewer than requested
# HSCAN never blocks; safe for production use哈希使用场景
哈希在对象和分组计数器方面表现出色。一个强大的内存技巧:当您有许多小的字符串键时,将它们分组到一个哈希中 — listpack 编码使用的开销远少于数千个单独的键(每个都有元数据)。这种“键分桶”对于小值可将内存使用减少 5-10 倍。字段级 TTL(7.4+)是一个重要补充。
# 1. Store an object (user profile, config)
HSET config:app name "MyApp" version "2.0" debug "off"
# 2. Per-user counters
HINCRBY user:1:stats logins 1
HINCRBY user:1:stats page_views 1
HGET user:1:stats logins
# 3. Shopping cart (product_id -> quantity)
HSET cart:user:1 product:100 2
HINCRBY cart:user:1 product:100 1
HDEL cart:user:1 product:100
# 4. Feature flags
HSET feature_flags user:1 new_ui 1 beta 0
HGET feature_flags user:1 new_ui # 1
# 5. Session store with TTL
HSET session:abc user_id 1 data "..."
EXPIRE session:abc 1800 # 30 min
# 6. Hash field expiration (Redis 7.4+)
HEXPIRE user:1 60 FIELDS 1 session_token
HPTTL user:1 FIELDS 1 session_token键管理
EXISTS、TYPE 与 DEL
EXISTS 与多个键一起使用时返回存在的键的数量。TYPE 返回数据结构类型 — 操作前始终检查以避免 WRONGTYPE 错误(键的类型在其生命周期内是不可变的)。DEL 是阻塞的,在大型结构上可能阻塞服务器;大型键优先使用 UNLINK。TYPE 对不存在的键返回 'none'。
# check if a key exists
SET mykey "hello"
EXISTS mykey # 1
EXISTS missing # 0
EXISTS key1 key2 key3 # count of existing keys (multiple)
# get the type of a value
TYPE mykey # "string"
TYPE mylist # "list"
TYPE missing # "none"
# delete one or more keys
DEL mykey # 1 if deleted, 0 if not
DEL key1 key2 key3 # number of keys deleted
# delete is blocking for large keys — use UNLINK instead
# UNLINK deletes in a background thread
# check type before operating to avoid WRONGTYPE errors
TYPE mykey
# returns: string | list | hash | set | zset | stream | noneUNLINK(异步删除)
UNLINK(4.0+)是大型键 DEL 的非阻塞替代方案 — 它立即从键空间中移除键,但在后台线程中释放内存,防止服务器阻塞。对于有数百万元素的键,始终使用 UNLINK。FLUSHDB ASYNC / FLUSHALL ASYNC 对批量删除做同样的事情。lazyfree 线程池处理实际的释放。
# UNLINK: non-blocking delete (large keys)
UNLINK biglist bighash # returns count of deleted keys
# UNLINK removes the key from the keyspace immediately
# but frees the memory in a background thread
# compare:
DEL biglist # blocks until fully freed
UNLINK biglist # returns immediately, frees later
# for small keys, DEL and UNLINK are equivalent
# for large keys (millions of elements), UNLINK is essential
# flush database asynchronously (non-blocking)
FLUSHDB ASYNC
FLUSHALL ASYNC
# check what's being freed in background
INFO memory | grep lazyfreeRENAME 与 RENAMENX
RENAME 原子性地将键移动到新名称,覆盖任何已存在的目标键(及其值)。RENAMENX(6.2+)仅在目标不存在时重命名,防止意外覆盖。重命名时保留 TTL。RENAME 是原子性的 — 没有其他客户端能看到中间状态。不支持跨数据库重命名;使用 MOVE 或 DUMP/RESTORE。
# rename a key (overwrites destination if it exists)
SET oldkey "value"
RENAME oldkey newkey # OK; oldkey is gone, newkey has "value"
# rename only if destination doesn't exist (6.2+)
RENAMENX oldkey newkey # 1 if renamed, 0 if newkey already exists
# rename preserves the type and TTL
SET tempkey "data" EX 3600
RENAME tempkey permkey # permkey now has TTL of ~3600s
TTL permkey # ~3600
# rename across databases is not supported
# use MOVE + RENAME, or DUMP/RESTORE for cross-db moves
# type is immutable — rename keeps the type
TYPE permkey # "string"
# renaming is atomicKEYS 与 SCAN
KEYS 在扫描整个键空间时会阻塞服务器 — 切勿在生产环境的大型数据集上使用。SCAN 是生产安全的替代方案:基于游标、非阻塞,返回一个您跟随直到 0 的游标。SCAN 可能 返回重复项或遗漏迭代期间添加的键,但永远不会阻塞。COUNT 是一个提示(可能返回更多或更少)。始终在代码中处理游标循环。
# KEYS: blocks the server while scanning — NEVER in production
KEYS * # all keys (DANGEROUS on large DBs)
KEYS user:* # keys matching pattern
KEYS session:*
# SCAN: cursor-based iteration (non-blocking, production-safe)
SCAN 0 MATCH user:* COUNT 100
# returns: [nextCursor, [key1, key2, ...]]
# continue until cursor returns to 0
SCAN <nextCursor> MATCH user:* COUNT 100
# scan by type (7.0+)
SCAN 0 TYPE hash
SCAN 0 MATCH user:* TYPE string COUNT 100
# SCAN is weakly consistent:
# - may return duplicates
# - may miss keys added during iteration
# - but NEVER blocks the server
# COUNT is a hint, not exactRANDOMKEY 与 DBSIZE
RANDOMKEY 返回一个随机键而不移除它 — 适用于采样。DBSIZE 返回当前数据库中键的确切数量(O(1))。INFO keyspace 显示每个数据库的计数和过期统计(键数、过期数、平均 TTL)。对于特定类型的随机键,没有内置命令 — 在循环中使用 SCAN + TYPE。
# get a random key (O(1), does not remove)
RANDOMKEY # "user:1" or nil if db is empty
# get the number of keys in the current database
DBSIZE # 1234
# count keys across all databases (in a cluster)
# note: DBSIZE only counts the current db
SELECT 0; DBSIZE
SELECT 1; DBSIZE
# get keyspace info (keys per db with expiry info)
INFO keyspace
# db0:keys=1000,expires=500,avg_ttl=3600000
# db1:keys=50,expires=10,avg_ttl=60000
# scan with count for approximate size estimation
SCAN 0 COUNT 1 # get cursor + small sample
# find a random key of a specific type
# (no built-in; use SCAN + TYPE in a loop)OBJECT 命令
OBJECT ENCODING 揭示内部表示(调试内存)。OBJECT IDLETIME 查找冷键(驱逐候选)。OBJECT FREQ 仅在 maxmemory-policy LFU 下工作。MEMORY USAGE 与 SAMPLES 0 精确计数(大型集合使用默认采样)。COMMAND DOCS(7.0+)返回结构化文档。这些内省工具帮助调试内存膨胀。
# OBJECT subcommands for introspection
OBJECT ENCODING mykey # internal encoding (embstr, raw, int, listpack, etc.)
OBJECT REFCOUNT mykey # reference count
OBJECT IDLETIME mykey # seconds since last access
OBJECT FREQ mykey # access frequency (LFU mode only)
# check memory usage of a key (bytes)
MEMORY USAGE mykey
MEMORY USAGE mykey SAMPLES 0 # exact count (slower for large keys)
# total memory used by the server
INFO memory | grep used_memory_human
# inspect a key's internal structure (debugging, can be slow)
DEBUG OBJECT mykey # raw internal info
# the encoding changes automatically as the structure grows
# e.g., hash: listpack -> hashtable when fields exceed threshold
# help understand a command
COMMAND INFO GET
COMMAND DOCS SET # structured docs (7.0+)过期与持久化
EXPIRE 与 EXPIREAT
EXPIRE 在已存在的键上设置 TTL;SET 与 EX/PX/EXAT/PXAT 原子性地执行(首选)。NX/GT/LT 选项(7.0+)启用条件性 TTL 设置 — 适用于“仅在无过期时添加”或“只延长,从不缩短”。TTL 对没有过期的键返回 -1,对不存在的键返回 -2。过期是惰性的(访问时检查)加上定期后台扫描。
# set expiration on an existing key (seconds)
SET mykey "value"
EXPIRE mykey 60 # expires in 60 seconds
EXPIRE mykey 60 NX # only set TTL if none exists (7.0+)
EXPIRE mykey 60 GT # only set if new TTL > current (7.0+)
EXPIRE mykey 60 LT # only set if new TTL < current (7.0+)
# set expiration in milliseconds
PEXPIRE mykey 60000
# set expiration at a Unix timestamp
EXPIREAT mykey 1700000000 # seconds
PEXPIREAT mykey 1700000000000 # milliseconds
# set value AND expiration atomically (preferred)
SET token "abc" EX 3600
SET token "abc" PX 3600000
SET token "abc" EXAT 1700000000
# get remaining TTL
TTL mykey # seconds (-1 = no expiry, -2 = no key)
PTTL mykey # millisecondsPERSIST(移除 TTL)
PERSIST 移除 TTL,使键变为持久化(值保留)。GETEX(6.2+)原子性地返回值并设置或移除 TTL — 适用于在读取时刷新会话过期(GETEX key EX 3600)或将临时键变为永久(GETEX key PERSIST)。COPY(6.2+)复制键,可选跨数据库,没有 GET+SET 的竞争。
# remove expiration (make key persistent)
SET session "data" EX 3600
TTL session # 3600
PERSIST session # 1 (success)
TTL session # -1 (no expiry)
# PERSIST returns 1 if the TTL was removed, 0 if key had no TTL or doesn't exist
# copy a key with optional TTL handling (6.2+)
COPY source dest # copy without TTL
COPY source dest REPLACE # overwrite destination
COPY source dest DB 1 # copy to another database
# GETEX: get value and optionally set/remove TTL (6.2+)
GETEX mykey EX 3600 # returns value, sets 3600s TTL
GETEX mykey PERSIST # returns value, removes TTL
# note: PERSIST only removes TTL; the key and value remainSAVE 与 BGSAVE(RDB)
SAVE 阻塞服务器直到快照完成 — 切勿在生产环境使用。BGSAVE 分叉进程(写时复制)使主线程不被阻塞,但大型数据集在分叉期间可能导致内存峰值。'save' 规则根据变更率触发自动快照。RDB 紧凑,非常适合备份,但有丢失最后一次快照后写入数据的风险。
# SAVE: synchronous snapshot (BLOCKS the server until done)
SAVE # blocks — use only in maintenance windows
# BGSAVE: background snapshot (non-blocking, forks a child)
BGSAVE # returns immediately, saves in background
LASTSAVE # Unix timestamp of the last successful save
# check if a BGSAVE is in progress
INFO persistence | grep rdb_bgsave_in_progress
# configure automatic snapshots in redis.conf:
# save 3600 1 # save if >= 1 key changed in 3600s
# save 300 100 # save if >= 100 keys changed in 300s
# save 60 10000 # save if >= 10000 keys changed in 60s
# save "" # disable RDB entirely
# RDB file location
CONFIG GET dbfilename # "dump.rdb"
CONFIG GET dir # "/var/lib/redis"
# RDB pros: compact, fast restart, great for backups
# RDB cons: potential data loss between snapshotsBGREWRITEAOF(AOF 压缩)
AOF 将每条写命令追加到日志中 — 比 RDB 持久得多(everysec 最多丢失 1 秒数据)。'always' 在每次写入时 fsync(非常慢,仅用于关键数据)。BGREWRITEAOF 压缩日志(重写为最小命令集)。Redis 7 使用多部分 AOF 格式(基础 RDB + 增量日志)— appenddirname 保存各部分。
# AOF: logs every write command (durable, replayable)
# enable in redis.conf:
# appendonly yes
# appendfilename "appendonly.aof"
# fsync policies:
# appendfsync always # fsync every write (safest, slowest)
# appendfsync everysec # fsync once per second (default, balanced)
# appendfsync no # let OS decide (fastest, risk of data loss)
# trigger AOF rewrite (compacts the log)
BGREWRITEAOF # non-blocking, rewrites in background
# AOF auto-rewrite thresholds:
# auto-aof-rewrite-percentage 100
# auto-aof-rewrite-min-size 64mb
# check AOF status
INFO persistence | grep aof
# AOF pros: minimal data loss (1 sec max with everysec)
# AOF cons: larger files, slower load on restart
# Redis 7: multi-part AOF (base + incremental files)
CONFIG GET appenddirname # "appendonlydir"持久化配置
CONFIG SET 在运行时更改设置(无需重启);CONFIG REWRITE 将其持久化到 redis.conf。推荐的生产设置是 RDB + AOF 混合:RDB 用于快速重启和备份,AOF 用于最小化数据丢失。自 Redis 7 起,AOF 以 RDB 基础快照开始,后跟增量命令 — 结合快速加载与持久性。
# view current persistence settings
CONFIG GET save
CONFIG GET appendonly
CONFIG GET appendfsync
CONFIG GET dir
CONFIG GET dbfilename
CONFIG GET appendfilename
# enable AOF at runtime
CONFIG SET appendonly yes
# change fsync policy at runtime
CONFIG SET appendfsync everysec
# disable RDB snapshots
CONFIG SET save ""
# persist config changes to redis.conf
CONFIG REWRITE
# hybrid persistence (recommended):
# RDB for fast restart + AOF for durability
# redis.conf:
# save 3600 1
# appendonly yes
# aof-use-rdb-preamble yes # RDB header in AOF (faster load)
# check persistence status
INFO persistence备份与恢复
定期备份 RDB 文件(紧凑、快速)— 即使 Redis 运行时它也是一致的快照(写时复制)。对于 AOF,备份整个 appendonly 目录(Redis 7 多部分)。通过停止 Redis、替换文件、重启来恢复。要从错误命令恢复,在重启前编辑 AOF 移除它。redis-cli --rdb 执行在线备份,不直接接触文件系统。
# backup: copy the RDB file while Redis runs
BGSAVE
# wait for LASTSAVE to update, then copy the file
cp /var/lib/redis/dump.rdb /backup/dump-$(date +%F).rdb
# AOF backup: copy the AOF manifest + files (Redis 7)
cp -r /var/lib/redis/appendonlydir /backup/
# restore: stop Redis, replace data files, restart
redis-cli SHUTDOWN
cp /backup/dump.rdb /var/lib/redis/dump.rdb
redis-server /etc/redis/redis.conf
# for point-in-time recovery from AOF:
# edit the AOF file to remove unwanted commands, then restart
# RDB file analysis (offline, safe)
redis-check-rdb /var/lib/redis/dump.rdb
redis-check-aof /var/lib/redis/appendonly.aof
# fix a truncated AOF
redis-check-aof --fix /var/lib/redis/appendonly.aof
# online backup with --rdb (dumps RDB to stdout/file)
redis-cli --rdb /backup/dump.rdb发布/订阅
SUBSCRIBE 与 UNSUBSCRIBE
SUBSCRIBE 阻塞客户端以接收来自一个或多个频道的消息。已订阅的客户端只能调用 SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE/PING/QUIT — 不能调用其他命令。PUBSUB 内省发布/订阅状态(活跃频道、订阅者计数)。消息是即发即弃的:如果没有订阅者监听,消息就会丢失。对于可靠传递,使用 Streams。
# subscribe to channels (in one client, blocks)
SUBSCRIBE news alerts
# the client now listens for messages:
# 1) "subscribe" 2) "news" 3) 1
# 1) "subscribe" 2) "alerts" 3) 2
# unsubscribe from specific channels
UNSUBSCRIBE news
UNSUBSCRIBE news alerts
# unsubscribe from ALL channels
UNSUBSCRIBE
# note: published messages are NOT persisted
# if no subscriber is listening, the message is lost
# for persistent messaging, use Streams instead
# count active subscriptions (in another client)
PUBSUB CHANNELS # list active channels
PUBSUB NUMSUB news # subscriber count for "news"PUBLISH
PUBLISH 返回收到消息的订阅者数量(如果没有则为 0)。消息发布后无法检索 — 如果没有订阅者监听,它就消失了。PUBSUB CHANNELS 列出活跃频道(至少有一个订阅者的频道);PUBSUB NUMSUB 返回订阅者计数。对于可靠、可重放的消息传递,使用 Streams(XADD/XREAD)。
# publish a message to a channel (from another client)
PUBLISH news "Breaking: Redis 7.4 released"
# returns: number of subscribers that received the message
# publish to a channel with no subscribers
PUBLISH empty "hello"
# returns: 0 (message is lost)
# publish to multiple channels (separate PUBLISH calls)
PUBLISH news "msg1"
PUBLISH alerts "msg2"
# pub/sub has no persistence, no replay, no consumer groups
# subscribers must be connected to receive messages
# check how many clients are listening
PUBSUB NUMSUB news
PUBSUB NUMSUB news alerts # multiple channels
# list active channels (with pattern)
PUBSUB CHANNELS # all active channels
PUBSUB CHANNELS news:* # channels matching patternPSUBSCRIBE(模式订阅)
PSUBSCRIBE 订阅匹配 glob 模式的频道 — 对于事件路由非常强大(例如,user:*:login 捕获所有用户登录事件)。同时匹配直接订阅和模式订阅的消息会被传递两次。模式订阅者接收包含模式、频道和消息的 'pmessage' 事件。PUNSUBSCRIBE 取消模式订阅。
# subscribe to channels matching a glob pattern
PSUBSCRIBE news.*
PSUBSCRIBE user:*:login
PSUBSCRIBE events:*
# patterns use glob syntax:
# * matches any sequence
# ? matches one character
# [ab] matches 'a' or 'b'
# unsubscribe from patterns
PUNSUBSCRIBE news.*
PUNSUBSCRIBE # unsubscribe from ALL patterns
# a message matching both a direct subscription and a pattern
# is delivered twice (once for each)
# pattern subscribers receive:
# 1) "pmessage" 2) "news.*" 3) "news.tech" 4) "the message"
# use case: event routing
PSUBSCRIBE user:*:login # all user logins
PSUBSCRIBE order:*:created # all order creations
PSUBSCRIBE cache:*:invalidate # cache invalidation events分片发布/订阅(7.0+)
分片发布/订阅(7.0+)解决了集群模式的问题:常规发布/订阅将每个 PUBLISH 广播到所有集群节点,浪费带宽。分片发布/订阅仅将消息路由到拥有频道键的分片 — 在大型集群中效率更高。在集群部署中使用 SSUBSCRIBE/SPUBLISH。频道名称决定分片(类似键)。
# regular Pub/Sub broadcasts to ALL cluster nodes (expensive)
# sharded Pub/Sub routes messages only to the shard owning the channel
# subscribe to a shard channel
SSUBSCRIBE mychannel
# publish to a shard channel
SPUBLISH mychannel "hello"
# sharded pub/sub uses the channel name for shard routing
# so messages only travel within one shard (more efficient)
# use sharded pub/sub in a Redis Cluster to avoid
# broadcasting messages to every node
# regular SUBSCRIBE/PUBLISH still work in cluster mode
# but they broadcast to all shards (cluster-wide overhead)
# check shard channel subscribers (7.0+)
PUBSUB SHARDCHANNELS
PUBSUB SHARDNUMSUB mychannel键空间通知
键空间通知让客户端对键生命周期事件(设置、删除、过期、驱逐)做出反应。通过 notify-keyspace-events 配置。它们是即发即弃的(无持久化)且不可靠 — 如果 Redis 服务器重启,待处理的事件会丢失。对于可靠的事件处理,使用 Streams。键空间通知非常适合清理或缓存预热等副作用。
# enable keyspace notifications (disabled by default)
CONFIG SET notify-keyspace-events "KEA"
# K = keyspace events, E = keyevent events
# g = generic (DEL, EXPIRE, ...), $ = string, l = list
# h = hash, s = set, z = sorted set, x = expired, e = evicted
# A = all except m (m = keymiss)
# subscribe to events on a specific key
SUBSCRIBE __keyspace@0__:mykey
# publishes "set", "del", "expire", etc.
# subscribe to events of a specific type
SUBSCRIBE __keyevent@0__:del
# publishes the key name that was deleted
# example: react when a key expires
SUBSCRIBE __keyspace@0__:session:abc
# receives "expired" when the key TTL ends
# example: react to all expired keys
SUBSCRIBE __keyevent@0__:expired
# check current setting
CONFIG GET notify-keyspace-events事务
MULTI 与 EXEC
MULTI/EXEC 将命令组合成原子性、顺序执行的块 — 没有其他客户端可以交错。命令被排队(返回 QUEUED)并在 EXEC 时一起执行。排队错误(错误的命令名)会中止整个事务。运行时错误(键的类型错误)只跳过该命令 — 其余命令仍会执行。这与 SQL 事务不同。
# MULTI starts a transaction; commands are queued
MULTI
SET counter 1
INCR counter
INCR counter
GET counter
EXEC
# returns: 1) OK 2) 2 3) 3 4) "3"
# all queued commands execute atomically (no interleaving)
# other clients cannot run commands between MULTI and EXEC
# each queued command returns "QUEUED" until EXEC
MULTI
SET x 1
# "QUEUED"
INCR x
# "QUEUED"
EXEC
# 1) OK 2) 2
# the transaction is atomic: all or nothing (at queuing time)DISCARD(取消事务)
DISCARD 取消事务,清除所有排队的命令并将连接从事务模式中释放。它还清除所有 WATCH 的键(参见 WATCH)。DISCARD 后,客户端可以再次发出正常命令。DISCARD 是“回滚”的等价物 — 但注意 Redis 不支持 EXEC 内的部分回滚;运行时错误跳过失败命令但提交其余命令。
# DISCARD cancels a transaction (clears all queued commands)
MULTI
SET x 1
INCR x
DISCARD
# OK — no commands are executed
# after DISCARD, the connection returns to normal mode
GET x
# (nil) or the previous value — nothing changed
# DISCARD also clears all WATCHed keys
WATCH counter
MULTI
INCR counter
DISCARD
# counter is no longer watched
# you must call MULTI before DISCARD
DISCARD
# ERR DISCARD without MULTIWATCH(乐观锁)
WATCH 实现乐观锁:如果被监视的键在 WATCH 和 EXEC 之间发生变化,事务被中止(EXEC 返回 nil)。这是 Redis 读-改-写原子性的标准模式(因为 Redis 没有行锁)。始终在 nil 时重试。WATCH 在 EXEC 时检查。UNWATCH 取消监视(在 DISCONNECT 时也会发生)。被同一客户端修改的被监视键也会触发中止。
# WATCH monitors keys; if any change before EXEC, transaction aborts
WATCH counter
val = GET counter # read current value
MULTI
INCR counter # queue the update
EXEC # nil if counter changed -> retry
# typical pattern: read-modify-write with retry
WATCH key
current = GET key
new_value = compute(current)
MULTI
SET key new_value
EXEC
# if EXEC returns nil, the key changed — loop and retry
# UNWATCH cancels all watches
UNWATCH
# WATCH is checked at EXEC time, not during queuing
# if a watched key is modified (by any client, including yourself),
# EXEC returns nil and the transaction is not executedUNWATCH 与 WATCH 边界情况
EXEC 后(无论事务提交还是因 WATCH 中止),所有监视都会自动清除。DISCARD 也是如此。要重试乐观锁循环,必须在下一个 MULTI 之前再次调用 WATCH。MULTI 内的 WATCH 是错误的 — WATCH 必须始终在 MULTI 之前。UNWATCH 主要用于在不进入事务的情况下取消监视。
# UNWATCH cancels all watches
WATCH key1 key2
UNWATCH # both keys are no longer watched
# WATCH inside a MULTI is not allowed (error)
MULTI
WATCH key
# ERR WATCH inside MULTI is not allowed
# after EXEC (success or abort), watches are cleared
WATCH key
MULTI
SET key "new"
EXEC
# key is no longer watched after EXEC
# after DISCARD, watches are cleared
WATCH key
MULTI
DISCARD
# key is no longer watched
# re-WATCH after EXEC to retry the optimistic lock loop
WATCH key
val = GET key
MULTI
SET key newval
EXEC
# if nil, loop: WATCH key again, GET, MULTI, SET, EXEC事务错误处理
Redis 在事务中有两种错误类型。排队错误(错误的命令名/参数数量)在 EXEC 时中止整个事务(EXECABORT)— 什么都不执行。运行时错误(例如,对非整数 INCR)只跳过失败的命令 — 其余命令仍会执行。没有回滚。这是与 SQL 数据库的关键区别。在 MULTI 之前验证数据以避免运行时错误。
# two types of errors in transactions:
# 1. Queuing errors (bad command syntax) — aborts the WHOLE transaction
MULTI
SET key "value"
INCRBY key "notanumber" # error during queuing (wrong arity/type at parse)
GET key
EXEC
# EXECABORT Transaction discarded because of previous errors.
# NOTHING executes — not even the SET
# 2. Runtime errors (wrong type at execution) — skips ONLY that command
SET key "stringvalue"
MULTI
INCR key # queues fine (syntax OK)
SET otherkey "ok"
EXEC
# 1) (error) ERR value is not an integer
# 2) OK
# the INCR failed but SET otherkey succeeded — no rollback!
# lesson: Redis transactions do NOT have rollback like SQL管道
管道基础
管道批量发送命令以减少网络往返 — 比网络上单独发送命令快得多。每次往返耗费网络延迟(约 0.1-1ms);1000 条单独命令 = 100-1000ms,但管道方式 = 约 1ms。大多数客户端库提供 pipeline/multi 方法。redis-cli 可通过 stdin 进行管道传输。管道不保证原子性 — 其他客户端可以交错。
# pipelining sends multiple commands in one network round-trip
# (no atomicity guarantee, just reduced latency)
# using redis-cli with stdin (simple pipeline):
printf '%s\n' "SET k1 v1" "SET k2 v2" "GET k1" | redis-cli
# OK
# OK
# "v1"
# using redis-cli --pipe (raw RESP, fastest for bulk loads):
# (see the data-import-export section for details)
# in a client library (e.g., Node.js with ioredis):
const pipe = redis.pipeline()
pipe.set("k1", "v1")
pipe.set("k2", "v2")
pipe.incr("counter")
const results = await pipe.exec()
# vs individual commands:
await redis.set("k1", "v1") # round-trip 1
await redis.set("k2", "v2") # round-trip 2
await redis.incr("counter") # round-trip 3MULTI 与管道
MULTI/EXEC 保证原子性(没有其他客户端可以交错)且被大多数客户端库自动管道化(因此您也获得了减少往返的好处)。普通管道只减少往返 — 其他客户端可以在您的管道命令之间执行。当需要原子性时(例如,使用 WATCH 的读-改-写)使用 MULTI/EXEC;对于顺序/原子性不重要的批量操作使用普通管道。
# MULTI/EXEC: atomic (no interleaving), pipelined automatically
MULTI
SET k1 v1
SET k2 v2
INCR counter
EXEC
# all 3 commands run atomically as one block
# Pipelining: NOT atomic, just batched (other clients can interleave)
# (in client code)
pipe.set("k1", "v1")
pipe.set("k2", "v2")
pipe.incr("counter")
pipe.exec()
# commands may interleave with other clients
# when to use which:
# - need atomicity (all-or-nothing)? -> MULTI/EXEC
# - just want speed (bulk operations)? -> pipelining
# - both? -> MULTI/EXEC (auto-pipelined)
# MULTI/EXEC is automatically pipelined by most clients
# so you get atomicity + batch round-trips in oneRESP 协议
RESP 是 Redis 的通信协议 — 简单、基于文本、快速。*N 表示 N 个元素的数组;$N 表示 N 字节的批量字符串;+ 是简单字符串;: 是整数;- 是错误。redis-cli --pipe 模式接受原始 RESP 以实现最高速度的批量加载(比通过 stdin 的文本命令快得多)。理解 RESP 有助于调试和构建自定义客户端。
# RESP (REdis Serialization Protocol) is the wire format
# redis-cli sends RESP; you can also send it raw with --pipe
# simple command (PING) in RESP:
# *1\r\n$4\r\nPING\r\n
# SET key value in RESP:
# *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n
# RESP types:
# +OK simple string
# -ERR error
# :42 integer
# $5\r\nhello bulk string
# *2\r\n... array
# raw pipeline with RESP (fastest bulk insert):
# generate a RESP file and pipe it:
cat commands.resp | redis-cli --pipe
# --pipe mode expects raw RESP; checks reply counts
# output: "Last reply received from server: ..."
# "errors: 0, replies: N"Shell 脚本中的管道
使用 redis-cli 的 Shell 脚本是批量加载数据或运行批量操作的常见方式。最简单的方法是通过 stdin 管道传输文本命令(echo/cat | redis-cli)。为了最大速度,生成 RESP 并使用 --pipe 模式(对于大型加载快一个数量级)。bash 循环示例展示了两种方法 — 根据您的数据量和性能需求选择。
# pipeline multiple commands via stdin
echo -e "SET k1 v1\nSET k2 v2\nGET k1\nGET k2" | redis-cli
# pipeline from a file
cat commands.txt | redis-cli
# pipeline with --pipe-mode (raw RESP, fastest)
# first generate RESP, then pipe:
redis-cli --pipe < commands.resp
# bulk insert example (bash, generate RESP):
for i in $(seq 1 1000); do
printf '*3\r\n$3\r\nSET\r\n$4\r\nkey:\r\n$1\r\n%s\r\n' "$i"
done | redis-cli --pipe
# or simpler (text mode, slightly slower):
for i in $(seq 1 1000); do
echo "SET key:$i value$i"
done | redis-cli
# --pipe-mode reports errors and throughput管道限制与最佳实践
管道是 Redis 最大的性能杠杆,但不要过度管道化 — 排队的回复 在客户端和服务器上都消耗内存。以 100-1000 条命令为一组进行批处理。使用 redis-benchmark -P 进行基准测试以找到最佳点(吞吐量会趋于平稳,延迟随管道增大而增加)。对于依赖先前结果的命令,使用 Lua 脚本(原子、单次往返)而不是多次管道调用。
# don't pipeline too many commands at once
# (queued replies consume memory on both client and server)
# bad: 1 million commands in one pipeline
# -> huge memory buffer, potential timeout
# good: batch in groups of 100-1000
for batch in chunks(data, 500):
pipe = redis.pipeline()
for item in batch:
pipe.set(item.key, item.value)
pipe.exec()
# benchmark pipelining
redis-benchmark -t set -n 100000 -P 16 # -P = pipeline size
# try -P 1, 10, 100, 1000 to find the sweet spot
# use pipelining for:
# - bulk imports
# - batch reads (MGET is better for simple multi-get)
# - pre-warming cache
# - reducing latency in high-RTT environments
# avoid pipelining for:
# - commands that depend on previous results (use Lua instead)
# - very large batches (split into chunks)Lua 脚本
EVAL 基础
EVAL 原子性地运行 Lua 脚本 — 脚本运行时,没有其他命令执行,使其非常适合多步原子操作。KEYS 和 ARGV 从调用者传递数据;切勿在脚本中通过字符串拼接构建键(违反集群规则 — 所有键必须在 KEYS 中)。redis.call 抛出错误(停止),redis.pcall 返回错误。脚本通过其 SHA1 哈希缓存供 EVALSHA 使用。
# EVAL: run a Lua script inline
# EVAL "script" numkeys key1 key2 ... arg1 arg2 ...
EVAL "return redis.call('GET', KEYS[1])" 1 mykey
# 1 = number of keys
# mykey = KEYS[1]
# run a simple script (no keys)
EVAL "return 42" 0
EVAL "return {1, 2, 3}" 0
EVAL "return 'hello'" 0
# a check-and-set script:
EVAL "
local cur = redis.call('GET', KEYS[1])
if cur == ARGV[1] then
return redis.call('SET', KEYS[1], ARGV[2])
end
return 0
" 1 mykey "expected" "newvalue"
# redis.call() raises error if command fails (stops script)
# redis.pcall() returns error as a table (can handle)
EVAL "local ok, err = pcall(redis.call, 'GET', 'missing'); return err" 0EVALSHA 与 SCRIPT LOAD
EVALSHA 通过其 SHA1 哈希运行之前加载的脚本,避免每次调用都重新发送脚本体 — 对于重复运行相同脚本的应用是重大的带宽节省。缓存未命中时(SCRIPT FLUSH 或重启后),Redis 返回 NOSCRIPT;客户端捕获此错误并回退到 EVAL + SCRIPT LOAD。始终使用透明处理此过程的库(redis-py、ioredis)。
# load a script once, get its SHA1
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# returns: "fa00d1f0..." (the SHA1 hash)
# execute by hash (saves bandwidth on repeated calls)
EVALSHA "fa00d1f0..." 1 mykey
# check if a script is cached
SCRIPT EXISTS "fa00d1f0..." "abcd1234..."
# 1) 1 2) 0 (1 = exists, 0 = not cached)
# flush the script cache
SCRIPT FLUSH
# list all running scripts (during debug)
SCRIPT DEBUG SYNC # blocking debug mode
SCRIPT DEBUG NO # disable debugger
# typical client library pattern:
# 1. SCRIPT LOAD the script on startup
# 2. EVALSHA to run it (falls back to EVAL on NOSCRIPT error)SCRIPT LOAD、FLUSH 与 EXISTS
SCRIPT LOAD 缓存脚本并返回其 SHA1。SCRIPT EXISTS 检查脚本是否已缓存(重启后有用)。SCRIPT FLUSH 清除所有缓存的脚本 — 随后的 EVALSHA 返回 NOSCRIPT。SCRIPT KILL 停止长时间运行的脚本,但仅当它尚未执行任何写入时(以保持一致性);如果已经写入,必须 SHUTDOWN NOSAVE。脚本是临时的 — 使用 Functions(7.0+)实现持久化。
# SCRIPT LOAD: load a script without running it
SCRIPT LOAD "return redis.call('SET', KEYS[1], ARGV[1])"
# returns the SHA1 hash
# SCRIPT EXISTS: check if scripts are cached (after load or restart)
SCRIPT EXISTS "abc123..." "def456..."
# 1) 1 2) 0
# SCRIPT FLUSH: clear the script cache
SCRIPT FLUSH
# all EVALSHA calls will now return NOSCRIPT until scripts are reloaded
# SCRIPT KILL: stop a running script (only if it hasn't written yet)
SCRIPT KILL
# if the script has already executed a write command:
# SHUTDOWN NOSAVE is the only way to stop it (extreme measure)
# scripts are ephemeral — they don't survive a restart
# use Redis Functions (7.0+) for persistent scripts
# check the script cache size
INFO memory | grep script_cacheKEYS 与 ARGV
KEYS[] 携带键名(对于集群路由至关重要 — 集群使用这些来确定哪个分片处理脚本)。ARGV[] 携带值和参数。切勿在脚本内硬编码键名(例如,redis.call('GET', 'mykey'))— 这会破坏集群路由,因为集群无法判断 'mykey' 在哪个分片上。始终通过 KEYS[] 传递键。numkeys 参数告诉 Redis 期望多少个 KEYS。
# KEYS[]: key names passed to the script
# ARGV[]: additional arguments (values, options)
# example: atomic increment with cap
EVAL "
local current = tonumber(redis.call('GET', KEYS[1]) or 0)
local max = tonumber(ARGV[1])
if current >= max then
return -1 -- cap reached
end
return redis.call('INCR', KEYS[1])
" 1 counter 100
# KEYS[1] = counter, ARGV[1] = 100
# example: move item between lists atomically
EVAL "
local item = redis.call('RPOP', KEYS[1])
if item then
redis.call('LPUSH', KEYS[2], item)
end
return item
" 2 source_list dest_list
# NEVER hardcode key names in scripts (breaks cluster routing)
# always pass keys via KEYS[] so the cluster can route correctlyLua 原子操作
Lua 脚本是 Redis 中执行原子多命令操作的规范方式(无 MULTI/EXEC 竞争)。比较并设置、信号量获取、队列迁移和限流都受益。脚本运行时会阻塞服务器 — 保持简短,避免在许多键上循环(可能阻塞其他客户端)。对于非常长的工作,使用 Redis Functions(Redis 7.0+),它们像存储过程一样声明和版本化。
# atomic compare-and-set (only update if value matches)
EVAL "
local cur = redis.call('GET', KEYS[1])
if cur == ARGV[1] then
return redis.call('SET', KEYS[1], ARGV[2])
end
return 0
" 1 mykey "expected" "newvalue"
# atomic 'decrement if positive' (semaphore / rate limit)
EVAL "
local val = tonumber(redis.call('GET', KEYS[1]) or 0)
if val > 0 then
redis.call('DECR', KEYS[1])
return 1
end
return 0
" 1 semaphore
# atomic list pop + push (move between queues)
EVAL "
local item = redis.call('RPOP', KEYS[1])
if item then
redis.call('LPUSH', KEYS[2], item)
end
return item
" 2 source_queue dest_queue
# atomic rate limiter (fixed window)
EVAL "
local count = redis.call('INCR', KEYS[1])
if count == 1 then
redis.call('EXPIRE', KEYS[1], ARGV[1])
end
return count
" 1 rate:user:1 60Redis Functions(7.0+)
Functions(7.0+)改进了 EVAL 脚本:它们有名称(不只是哈希),组织成库,并在重启后持久化(EVAL 脚本是临时的)。这使它们更易于管理和部署。FCALL 按名称调用函数。Functions 是添加原子服务器端逻辑的现代方式。与脚本一样,它们必须确定性以支持复制。FUNCTION DUMP/RESTORE 启用备份和迁移。
# FUNCTIONS are named, versioned Lua scripts (better than EVAL)
# register a function library
FUNCTION LOAD '#!lua name=mylib
redis.register_function("my_set",
function(keys, args)
return redis.call("SET", keys[1], args[1])
end
)'
# call a function by name
FCALL my_set 1 mykey "hello"
# list loaded libraries
FUNCTION LIST
FUNCTION LIST WITHCODE # include source
# get the function's code (for backup)
FUNCTION DUMP
# delete a library
FUNCTION DELETE mylib
# restore from a dump (for migration)
FUNCTION RESTORE <payload>
# functions persist across restarts (unlike EVAL scripts)
# they're saved in RDB/AOF连接管理
CLIENT LIST
CLIENT LIST 显示每个连接的客户端及其地址、年龄、空闲时间、当前命令和标志 — 对于查找泄漏或卡住的客户端非常有价值。'idle' 字段帮助发现被遗弃的连接。'cmd' 显示最后一条命令(用于查找运行慢命令的客户端)。'flags' 表示客户端类型(N=普通,M=主节点,S=从节点/副本,P=发布/订阅,b=阻塞)。7.0+ 中可按 TYPE 过滤。
# list all connected clients
CLIENT LIST
# fields: id, addr, laddr, fd, name, age, idle, flags, db, cmd, ...
# filter by type (7.0+)
CLIENT LIST TYPE normal # normal clients only
CLIENT LIST TYPE replica # replicas
CLIENT LIST TYPE pubsub # pub/sub subscribers
# filter by ID (7.0+)
CLIENT LIST ID 1 2 3
# output includes useful fields:
# addr=10.0.0.1:12345 remote address
# age=3600 seconds since connected
# idle=0 seconds since last command
# cmd=get last command executed
# db=0 current database
# flags=N N=normal, M=master, S=slave, P=pubsub
# count connected clients
CLIENT LIST | wc -lCLIENT SETNAME 与 GETNAME
CLIENT SETNAME 为连接打标签,以便您在 CLIENT LIST 中识别它 — 对于在多服务环境中调试连接泄漏至关重要。在应用中连接后立即设置名称(例如,'api-server:pid12345:pool1')。名称出现在 CLIENT LIST 的 'name' 字段中。这使得查找哪个服务拥有卡住或泄漏的连接变得轻而易举。
# name your connection (for identification in CLIENT LIST)
CLIENT SETNAME "my-app-worker-1"
# get the current connection's name
CLIENT GETNAME
# in application code, set the name immediately after connecting:
# redis-cli example:
redis-cli CLIENT SETNAME "debug-session"
# names appear in CLIENT LIST output:
CLIENT LIST
# ... name=my-app-worker-1 ...
# names are useful for:
# - identifying which app/pool a connection belongs to
# - finding leaks (connections that should have been closed)
# - debugging stuck clients
# - naming conventions: app:hostname:pid or pool:name
# names are limited to a reasonable length and cannot contain spacesCLIENT KILL
CLIENT KILL 强制断开客户端 — 适用于移除卡住或泄漏的连接。按 ADDR(ip:port)、ID、TYPE 或 USER(ACL)断开。MAXAGE(7.0+)断开空闲超过 N 秒的客户端。SKIPME yes(默认)防止断开自己的连接。先用 CLIENT LIST 找到目标,然后用 CLIENT KILL 断开。断开是立即的(无优雅关闭)。
# kill a client by address
CLIENT KILL ADDR 10.0.0.1:12345
# kill a client by ID
CLIENT KILL ID 42
# kill by type (7.0+)
CLIENT KILL TYPE normal # kill all normal clients
CLIENT KILL TYPE pubsub # kill all pub/sub clients
# kill by user (ACL, 6.0+)
CLIENT KILL USER appuser
# kill by address with skip-me
CLIENT KILL ADDR 10.0.0.1:0 SKIPME yes # don't kill myself
# kill all clients matching filters (7.0+)
CLIENT KILL TYPE normal MAXAGE 3600 # kill idle > 1h
# kill returns the number of clients killed
# use with caution — kills are immediate, no graceful shutdown
# common use: disconnect a stuck client consuming resources
CLIENT LIST
CLIENT KILL ID <id>CLIENT ID 与 INFO
CLIENT ID 返回连接的唯一、永不重用的整数 — 适用于日志和关联。CLIENT INFO 提供有关自己连接的详细信息。CLIENT NO-EVICT(6.0+)保护客户端的写入不触发驱逐(适用于关键写入)。CLIENT REPLY 控制服务器是否发送回复(OFF 用于即发即弃的批量加载,SKIP 用于单条命令)。
# get the current client's unique ID (never reused)
CLIENT ID
# get the current client's info (detailed)
CLIENT INFO
# id=42 addr=127.0.0.1:12345 laddr=127.0.0.1:6379 name= db=0 ...
# the ID is a monotonically increasing integer
# it's unique per connection and never reused
# useful for logging and tracking
# CLIENT NO-EVICT (6.0+): this client's writes won't trigger eviction
CLIENT NO-EVICT ON # protect this client from eviction
CLIENT NO-EVICT OFF # normal behavior
# CLIENT REPLY (6.0+): control reply mode
CLIENT REPLY ON # normal replies
CLIENT REPLY OFF # no replies
CLIENT REPLY SKIP # skip reply for next command only
# CLIENT UNPAUSE: resume processing after CLIENT PAUSE
CLIENT UNPAUSECLIENT PAUSE
CLIENT PAUSE 阻塞所有其他客户端的命令执行 N 毫秒 — 适用于安全快照、迁移和集群故障转移。WRITE 模式(7.0+)只暂停写入而允许读取继续(干扰更小)。发起暂停的客户端本身不被阻塞。暂停期间,排队的命令消耗内存,因此保持暂停简短。CLIENT UNPAUSE 立即恢复。
# pause all client processing for N milliseconds
CLIENT PAUSE 10000 # pause for 10 seconds
# all other clients' commands are queued, not executed
# pause modes (7.0+):
CLIENT PAUSE 10000 ALL # pause all commands (default)
CLIENT PAUSE 10000 WRITE # pause only writes (reads continue)
# resume processing immediately
CLIENT UNPAUSE
# use cases:
# - safe snapshot/migration (pause writes, take consistent backup)
# - cluster failover (pause to ensure clean handoff)
# - debugging (pause to inspect state)
# during pause:
# - commands from OTHER clients are queued (memory grows)
# - the pausing client can still run commands
# - paused commands execute when the pause ends
# check if a pause is active
CLIENT INFO | grep flags
# 'P' flag = paused连接限制与超时
maxclients 限制并发连接数(默认 10000)— 如果您有许多应用服务器或连接池,请提高它。timeout 断开空闲客户端(0 = 永不;设置为 300s 以保证安全)。tcp-keepalive 检测失效的 TCP 连接(默认 300s)。每个客户端为其输出缓冲区消耗内存 — 用 INFO clients 监控。client-output-buffer-limit 防止一个慢客户端消耗所有服务器内存(对于发布/订阅和副本尤其重要)。
# max concurrent clients
CONFIG GET maxclients # default 10000
CONFIG SET maxclients 50000 # increase limit
# timeout: disconnect idle clients after N seconds
CONFIG GET timeout # default 0 (never)
CONFIG SET timeout 300 # 5 minutes
# TCP keepalive (detect dead connections)
CONFIG GET tcp-keepalive # default 300
CONFIG SET tcp-keepalive 60
# when maxclients is reached, new connections are rejected
# error: "ERR max number of clients reached"
# check current client count vs limit
INFO clients
# connected_clients:150
# blocked_clients:5
# maxclients:10000
# each connection uses memory (~a few KB for the output buffer)
# monitor for output buffer growth:
INFO clients | grep client_recent_max_output_buffer
# set output buffer limits (prevent one client from using all memory)
CONFIG GET client-output-buffer-limit服务器管理
INFO 部分
INFO 是主要的监控命令 — 一次性报告内存、客户端、复制、持久化和统计信息。过滤部分(INFO memory)以减少输出。关键指标:used_memory_rss(实际占用)、mem_fragmentation_ratio(理想约 1.0-1.5)、connected_clients、instantaneous_ops_per_sec、keyspace_hits/misses(缓存命中率)。TIME 返回服务器时间用于时钟同步。
# comprehensive server information
INFO # all sections
INFO server # version, uptime, config
INFO clients # connected clients
INFO memory # memory usage, fragmentation
INFO persistence # RDB/AOF status
INFO stats # ops/sec, hit rate, keyspace
INFO replication # primary/replica state
INFO cpu # CPU usage
INFO keyspace # keys per database
INFO commandstats # command frequency
INFO errorstats # error frequency
# get a specific metric
INFO memory | grep used_memory_human
INFO stats | grep instantaneous_ops_per_sec
INFO replication | grep role
# check Redis version and mode
INFO server | grep redis_version
INFO server | grep redis_mode # standalone, cluster, sentinel
# get server time
TIME # [unix_timestamp, microseconds]CONFIG GET 与 SET
CONFIG SET 在运行时更改设置而无需重启 Redis — 非常适合调优。CONFIG GET 检索当前值(使用模式如 CONFIG GET *memory*)。常见的运行时调整:maxmemory + 驱逐策略、slowlog 阈值、timeout、编码阈值。并非所有设置都可在运行时更改(例如,port、bind 地址需要重启)。始终先在预发布环境测试配置更改。
# view config (runtime changes with CONFIG SET)
CONFIG GET maxmemory
CONFIG GET maxmemory-policy
CONFIG GET save
CONFIG GET appendonly
CONFIG GET requirepass
# change config at runtime (no restart needed)
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG SET slowlog-log-slower-than 10000 # 10ms in microseconds
CONFIG SET timeout 300
# view all config (large output)
CONFIG GET *
# get a specific pattern
CONFIG GET *max*
CONFIG GET *timeout*
# common runtime tweaks:
CONFIG SET maxclients 20000
CONFIG SET hash-max-listpack-entries 256
CONFIG SET appendfsync everysecCONFIG REWRITE
CONFIG REWRITE 将运行时 CONFIG SET 更改持久化回 redis.conf — 文件就地更新,保留注释和结构。这弥合了运行时更改与重启后持久化之间的差距。REWRITE 前始终备份 redis.conf(很少出问题,但安全第一)。对 redis.conf 进行版本控制,以便随时间跟踪和审计更改。
# persist config changes to redis.conf
CONFIG REWRITE
# after CONFIG SET maxmemory 4gb
# CONFIG REWRITE updates redis.conf to include:
# maxmemory 4gb
# the original redis.conf is updated in-place
# comments and structure are preserved
# view the config file path
CONFIG GET *
# typical redis.conf (production):
# bind 127.0.0.1
# protected-mode yes
# port 6379
# maxmemory 4gb
# maxmemory-policy allkeys-lru
# save 3600 1
# appendonly yes
# appendfsync everysec
# tcp-keepalive 300
# timeout 0
# always back up redis.conf before CONFIG REWRITESLOWLOG
SLOWLOG 记录慢于配置阈值的命令(默认 10ms = 10000us)— 对于查找延迟元凶非常有价值。将阈值设置为您的 SLO(例如,5ms)。常见的慢命令:KEYS、大型集合上的 SMEMBERS、大型哈希上的 HGETALL、大型数据上的 SORT。slowlog-max-len 限制日志(默认 128)。将阈值设置为 0 可记录所有内容(仅用于调试 — 开销很高)。
# configure slow log (threshold in microseconds)
CONFIG SET slowlog-log-slower-than 10000 # 10ms (10000 us)
CONFIG SET slowlog-max-len 128 # keep last 128 entries
# view slow commands
SLOWLOG GET # last 10 (default)
SLOWLOG GET 5 # last 5
SLOWLOG GET 0 # all entries
# each entry:
# 1) id unique ID
# 2) timestamp Unix time
# 3) duration microseconds
# 4) command the command and args
# 5) client address:port
# 6) client name if set
# slowlog length
SLOWLOG LEN
# reset (clear all entries)
SLOWLOG RESET
# set threshold to 0 to log ALL commands (debugging)
# set to -1 to disable
CONFIG SET slowlog-log-slower-than 0MONITOR
MONITOR 实时流式传输每个客户端的每条命令 — 强大的调试工具,但性能影响巨大(吞吐量大约减半)。除非绝对必要且只短暂使用,否则切勿在生产环境使用 MONITOR。它非常适合预发布环境:连接 MONITOR,重现问题,准确查看应用发送了什么命令。对于生产环境,改用 SLOWLOG、LATENCY MONITOR 或 INFO commandstats。
# MONITOR: real-time log of ALL commands (debugging only!)
MONITOR
# every command from every client is printed:
# 1700000000.123456 [0 10.0.0.1:12345] "SET" "key" "value"
# 1700000000.123567 [0 10.0.0.1:12345] "GET" "key"
# to stop monitoring, send any non-MONITOR command or disconnect
# press Ctrl+C in redis-cli
# WARNING: MONITOR has a HUGE performance impact
# it roughly halves throughput — use only for debugging
# use cases:
# - debugging what commands an app is sending
# - auditing command patterns
# - finding the source of unexpected writes
# safer alternatives for production:
# - SLOWLOG (only slow commands)
# - LATENCY MONITOR (specific events)
# - COMMANDSTATS in INFO (command frequency)
# MONITOR is best used in a staging/dev environmentLATENCY 监控
LATENCY MONITOR 跟踪延迟事件(设置阈值,例如,100ms)。LATENCY DOCTOR 分析最近的事件并建议修复 — 一个很好的诊断起点。常见事件:'command'(慢命令)、'fork'(BGSAVE 分叉延迟)、'expire-cycle'(过期扫描)、'aof-write'(AOF fsync 延迟)。LATENCY GRAPH 显示 ASCII 时间线。将阈值设置为您的 SLO 并定期监控。
# enable latency monitoring (threshold in ms)
CONFIG SET latency-monitor-threshold 100 # 100ms
# view latency events
LATENCY HISTORY event-name
LATENCY LATEST # most recent events
LATENCY HISTORY command
LATENCY HISTORY expire-cycle
# ASCII art graph of an event
LATENCY GRAPH command
# human-readable diagnosis and recommendations
LATENCY DOCTOR
# reset latency data
LATENCY RESET
LATENCY RESET event-name
# common events to monitor:
# command slow commands
# fast-command slow O(1) commands
# expire-cycle expiration cycle taking too long
# fork BGSAVE/BGREWRITEAOF fork taking too long
# aof-write AOF write delays
# unlink async delete delays
# check current threshold
CONFIG GET latency-monitor-threshold集群
CLUSTER INFO
CLUSTER INFO 提供快速的健康摘要:cluster_state 应为 'ok'(覆盖所有 16384 个槽),cluster_slots_ok 应等于 16384。cluster_known_nodes 是总节点数;cluster_size 是主节点数量。如果 cluster_state 为 'fail',某些槽不可用(可能是主节点及其副本都宕机)。从任何节点检查 — 信息是集群范围的。
# cluster-wide health
CLUSTER INFO
# key fields:
# cluster_state:ok # ok or fail
# cluster_slots_assigned:16384 # total slots
# cluster_slots_ok:16384 # slots covered
# cluster_known_nodes:6 # total nodes
# cluster_size:3 # number of primaries
# cluster_current_epoch:7
# cluster_my_epoch:1
# cluster_state:ok means all slots are covered
# cluster_state:fail means some slots are unavailable
# check from any node in the cluster
redis-cli -p 7000 CLUSTER INFO
# other cluster commands require being in cluster mode
# (redis-server --cluster-enabled yes)CLUSTER NODES
CLUSTER NODES 是权威的拓扑视图 — 每个节点一行,包含 ID、地址、角色(主/从)、它跟随的主节点(对于副本)和槽范围。查找 'fail' 或 'fail?' 标志以发现不健康的节点。槽范围显示哪个主节点拥有哪些哈希槽(0-16383)。CLUSTER SHARDS(7.0+)按分片分组节点(主节点 + 其副本)。
# view the full cluster topology
CLUSTER NODES
# each line is a node:
# <id> <addr:port@cport> <flags> <master> <ping-sent> <pong-recv> <epoch> <link> <slots>
# flags include:
# M = master (primary)
# S = slave (replica)
# mymaster = the node this replica follows
# fail? = suspected down
# fail = confirmed down
# handshake = new node joining
# nofailover = failover disabled
# example output:
# 1a2b... 127.0.0.1:7000@17000 myself,master - 0 0 1 connected 0-5460
# 3c4d... 127.0.0.1:7001@17001 master - 0 0 2 connected 5461-10922
# 5e6f... 127.0.0.1:7002@17002 master - 0 0 3 connected 10923-16383
# 7g8h... 127.0.0.1:7003@17003 slave 1a2b... 0 0 4 connected
# count nodes by type
CLUSTER NODES | grep -c master
CLUSTER NODES | grep -c slave
# Redis 7.0+: shard view
CLUSTER SHARDSCLUSTER KEYSLOT 与 COUNTKEYSINSLOT
CLUSTER KEYSLOT 显示键映射到哪个槽(CRC16 % 16384)。哈希标签({...})强制相关键到同一槽 — 对于集群中的多键操作(MGET、事务、Lua 脚本)至关重要。使用 redis-cli -c(集群模式)自动跟随 MOVED 重定向。CLUSTER GETKEYSINSLOT 检索槽中的键(在重新分片期间使用)。
# get the hash slot for a key
CLUSTER KEYSLOT mykey # e.g., 1234
# CRC16(key) % 16384
# count keys in a specific slot (on the owning node)
CLUSTER COUNTKEYSINSLOT 1234
# get keys in a slot (must be run on the slot's owning node)
CLUSTER GETKEYSINSLOT 1234 10 # up to 10 keys
# in a cluster, keys must be on the same slot for multi-key ops
# use hash tags to force keys to the same slot:
SET {user:1}:profile "alice"
SET {user:1}:cart "items"
# both map to the same slot (the part inside {})
# CLUSTER KEYSLOT {user:1}:profile
# CLUSTER KEYSLOT {user:1}:cart
# same result -> same slot -> same shard
# if you access a key on the wrong node, you get a MOVED redirect:
# (error) MOVED 1234 127.0.0.1:7000
# redis-cli -c follows redirects automatically哈希标签
哈希标签({...})强制键到同一槽,在集群中启用多键操作。但要明智使用 — 过度使用一个标签(例如,{users}:1,{users}:2)会在一个分片上创建热点。围绕访问模式设计哈希标签:将一起访问的键分组(一个用户的数据,一个订单的项)但保持无关键独立。这是核心的集群数据建模挑战。
# hash tags: the substring inside {} determines the slot
# {user:1}:profile and {user:1}:cart share a slot
# enabling multi-key operations in a cluster:
MSET {user:1}:name "Alice" {user:1}:age 30 # OK (same slot)
MSET user:1:name "Alice" user:2:age 30 # CROSSSLOT error
# transactions across keys (must be same slot)
WATCH {order:1}:items
MULTI
HINCRBY {order:1}:items product:1 1
HINCRBY {order:1}:total 10
EXEC
# Lua scripts accessing multiple keys (all keys must share a slot)
EVAL "..." 2 {user:1}:a {user:1}:b
# design hash tags carefully:
# - too broad -> hotspots (one shard overloaded)
# e.g., {users}:1, {users}:2 -> all on one shard (bad!)
# - too narrow -> can't do multi-key ops
# e.g., user:1:a, user:1:b -> different slots (can't MGET)
# good: {user:1}:* groups one user's data on one shard
# bad: {all-users}:* puts everyone on one shardCLUSTER FAILOVER
当主节点在 cluster-node-timeout(默认 15s)后不可达时,集群故障转移是自动的。手动故障转移用于维护(零停机升级):FAILOVER 是优雅的(等待复制同步),FORCE 跳过同步(更快,有轻微数据丢失风险),TAKEOVER 绕过共识(仅紧急情况,有脑裂风险)。故障转移后,旧主节点返回时成为副本。生产环境中始终使用副本运行。
# when a primary fails, its replica is promoted automatically
# manual failover (for maintenance), run on the REPLICA:
redis-cli -p 7001 CLUSTER FAILOVER # graceful (waits for sync)
redis-cli -p 7001 CLUSTER FAILOVER FORCE # without primary agreement (faster)
redis-cli -p 7001 CLUSTER FAILOVER TAKEOVER # force, may split brain (emergency)
# graceful failover steps:
# 1. replica stops syncing from master
# 2. master stops accepting writes
# 3. replica catches up on replication offset
# 4. replica is promoted to master
# 5. cluster is updated with new master info
# 6. old master becomes a replica when it returns
# CLUSTER RESET resets a node (HARD removes all data)
CLUSTER RESET HARD
CLUSTER RESET SOFT
# check failover status
CLUSTER INFO | grep cluster_state
# rolling upgrade pattern:
# for each replica: CLUSTER FAILOVER (becomes master), upgrade old masterredis-cli --cluster 工具
redis-cli --cluster 工具处理复杂的集群操作:创建、重新分片、重新平衡、节点添加/移除、健康检查和修复。--cluster check 是更改后验证集群健康的首选。--cluster fix 自动修复小问题(孤立槽等)。--cluster call 在所有节点上运行命令。对于操作任务,使用这些而不是手动 CLUSTER 命令。
# create a cluster (6 nodes: 3 primaries + 3 replicas)
redis-cli --cluster create \
host1:7000 host2:7000 host3:7000 \
host1:7001 host2:7001 host3:7001 \
--cluster-replicas 1
# check cluster health
redis-cli --cluster check host1:7000
# repair issues
redis-cli --cluster fix host1:7000
# reshard (move slots between nodes)
redis-cli --cluster reshard host1:7000
# prompts for: how many slots, target node, source nodes
# rebalance slots across all nodes
redis-cli --cluster rebalance host1:7000
# add a new node
redis-cli --cluster add-node newhost:7000 host1:7000
# add a replica
redis-cli --cluster add-node newhost:7001 host1:7000 \
--cluster-slave --cluster-master-id <nodeId>
# remove a node (migrates its slots first)
redis-cli --cluster del-node host1:7000 <nodeId>
# call a command on all nodes
redis-cli --cluster call host1:7000 INFO serverSentinel(高可用)
Sentinel 设置
Sentinel 提供无分片的高可用:它监视主节点,在故障时提升副本,并重新配置客户端。Quorum(最后一个数字)是有多少个 sentinel 必须同意主节点已宕机 — 对于 N 个 sentinel 设置为 (N/2)+1。在独立机器上部署 3 个 sentinel 以实现容错。down-after-milliseconds 应大于网络抖动;太低会导致误判故障转移。
# Sentinel monitors a primary + replicas, handles failover
# sentinel.conf (port 26379):
sentinel monitor mymaster 192.168.1.10 6379 2
# name, host, port, quorum (number of sentinels to agree on failure)
sentinel down-after-milliseconds mymaster 30000 # 30s to declare down
sentinel failover-timeout mymaster 180000 # 3min failover timeout
sentinel parallel-syncs mymaster 1 # replicas to resync in parallel
# start a sentinel:
redis-sentinel /etc/redis/sentinel.conf
# or:
redis-server /etc/redis/sentinel.conf --sentinel
# deploy at least 3 sentinels (odd number) for quorum
# sentinels communicate with each other and the Redis nodes
# connect to a sentinel to find the current primary:
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# sentinel auto-discovers other sentinels and replicasSENTINEL masters 与 replicas
SENTINEL masters 列出所有被监视的主节点;SENTINEL master <name> 给出详细信息(包括 num-slaves 和 num-other-sentinels)。SENTINEL replicas 显示副本;SENTINEL sentinels 显示对等 sentinel。ckquorum 验证您是否有足够的 sentinel 形成法定人数 — 在监控中运行此命令。所有主要的 Redis 客户端库原生支持 Sentinel(它们查询 sentinel 以找到当前主节点)。
# query sentinel state
redis-cli -p 26379 SENTINEL masters # all monitored primaries
redis-cli -p 26379 SENTINEL master mymaster # details of one primary
# fields: name, ip, port, runid, role, ...
# num-slaves, num-other-sentinels
# quorum, down-after-milliseconds
# failover-timeout, parallel-syncs
# list replicas of a master
redis-cli -p 26379 SENTINEL replicas mymaster
# list other sentinels monitoring the same master
redis-cli -p 26379 SENTINEL sentinels mymaster
# count monitoring sentinels (should be >= quorum)
redis-cli -p 26379 SENTINEL sentinels mymaster | grep -c name
# check the number of OK sentinels
SENTINEL ckquorum mymaster
# "OK 3 usable Sentinels. Quorum and failover authorization can be reached"
# or "NOQUORUM" warning (not enough sentinels)SENTINEL get-master-addr-by-name
SENTINEL get-master-addr-by-name 返回当前主节点的 IP 和端口 — 客户端调用此命令发现主节点,然后连接到它。故障转移时,sentinel 返回新主节点的地址。客户端应订阅 +switch-master 事件以立即了解故障转移。这种发现模式内置于所有主要 Redis 客户端库的 Sentinel 模式中。
# find the current primary address
redis-cli -p 26379 SENTINEL get-master-addr-by-name mymaster
# 1) "192.168.1.10"
# 2) "6379"
# clients use this to discover the current primary
# on failover, the sentinel returns the new primary's address
# clients should connect to sentinels, NOT directly to the primary
# libraries: redis-py, jedis, ioredis all support sentinel mode
# example client connection (pseudo-code):
# 1. connect to a sentinel
# 2. SENTINEL get-master-addr-by-name mymaster
# 3. connect to the returned primary
# 4. on connection error, repeat from step 1
# monitor for failover events
redis-cli -p 26379 SUBSCRIBE +switch-master
# publishes: mymaster 192.168.1.10 6379 192.168.1.11 6379
# other events:
redis-cli -p 26379 SUBSCRIBE +failover-state
redis-cli -p 26379 SUBSCRIBE +sdown
redis-cli -p 26379 SUBSCRIBE +odownSENTINEL failover
SENTINEL failover 触发手动故障转移(用于滚动升级/维护)。当主节点宕机超过 down-after-milliseconds 时发生自动故障转移。故障转移是自动的,但会导致短暂的写入中断(几秒)。被选中的 sentinel 领导者选择最新的副本提升。当旧主节点返回时,它成为副本 — 它在网络分区期间接受的写入会丢失(脑裂风险)。
# force a manual failover (for maintenance)
SENTINEL failover mymaster
# triggers failover: promotes a replica to primary
# the failover process:
# 1. sentinels mark primary SDOWN (subjectively down)
# 2. quorum agrees -> ODOWN (objectively down)
# 3. a sentinel is elected leader
# 4. leader picks the best replica (most up-to-date)
# 5. promotes the replica: REPLICAOF NO ONE
# 6. reconfigures other replicas to follow the new primary
# 7. updates sentinel configs with the new primary address
# 8. clients reconnect via sentinel discovery
# during failover, writes fail briefly (seconds)
# reads from replicas continue (if the app uses them)
# the old primary, when it returns, is reconfigured as a replica
# of the new primary (its writes during partition are lost)
# monitor failover events:
redis-cli -p 26379 SUBSCRIBE +switch-master
redis-cli -p 26379 SUBSCRIBE +failover-*Sentinel Quorum 与监控
Quorum 是触发故障转移前必须同意主节点已宕机的 sentinel 数量 — 对于 N 个 sentinel 设置为多数(N/2+1)。ckquorum 验证您是否有足够的数量。down-after-milliseconds 应大于网络抖动(30s 是安全的)。failover-timeout 限制故障转移持续时间。parallel-syncs 控制同时重新同步的副本数(1 = 安全但慢)。Sentinel 在拓扑更改时自动更新其配置。
# quorum: number of sentinels that must agree a master is down
# set in sentinel monitor command:
sentinel monitor mymaster 192.168.1.10 6379 2
# ^ quorum = 2
# for 3 sentinels: quorum = 2 (majority)
# for 5 sentinels: quorum = 3 (majority)
# check quorum status
SENTINEL ckquorum mymaster
# "OK 3 usable Sentinels" or "NOQUORUM"
# tune down-after-milliseconds (time before declaring down)
sentinel down-after-milliseconds mymaster 30000 # 30s default
# tune failover-timeout (max time for failover to complete)
sentinel failover-timeout mymaster 180000 # 3min default
# parallel-syncs: how many replicas resync simultaneously
sentinel parallel-syncs mymaster 1 # 1 at a time (safe, slow)
# view sentinel configuration
SENTINEL masters
SENTINEL master mymaster
# reset a master's state (clears discovered replicas/sentinels)
SENTINEL reset mymaster
# Sentinel automatically updates its config file when topology changes监控
MONITOR 命令
MONITOR 实时流式传输每个客户端的每条命令。输出格式为:时间戳 [数据库 客户端地址] “命令” “参数”。它对于调试应用实际发送的命令非常有价值。但它大约使吞吐量减半 — 仅在预发布/开发环境或短暂的生产调试中使用。用 grep 过滤特定模式。对于生产监控,改用 SLOWLOG、INFO commandstats 或 LATENCY MONITOR。
# MONITOR: real-time log of ALL commands (debugging only!)
MONITOR
# every command from every client is printed:
# 1700000000.123456 [0 10.0.0.1:12345] "SET" "key" "value"
# 1700000000.123567 [0 10.0.0.1:12345] "GET" "key"
# format: <unix_time> [<db> <addr>] "<cmd>" "<arg>" ...
# the [0 ...] shows which database and client
# to stop monitoring: Ctrl+C or send any command
# filter for specific patterns (in another terminal):
redis-cli MONITOR | grep "SET"
redis-cli MONITOR | grep "10.0.0.1"
redis-cli MONITOR | grep -E "(SET|DEL)"
# WARNING: MONITOR roughly halves throughput
# use only for debugging, never in production
# safer alternatives:
# - SLOWLOG (only slow commands)
# - INFO commandstats (command frequency)
# - LATENCY MONITOR (latency events)LATENCY 监控
LATENCY MONITOR 按事件类型跟踪延迟峰值。设置阈值(例如,100ms),Redis 记录超过它的事件。LATENCY DOCTOR 分析最近的事件并建议修复 — 一个很好的诊断起点。LATENCY GRAPH 显示 ASCII 时间线。常见元凶:'fork'(大型数据集上的 BGSAVE)、'expire-cycle'(批量过期)、'aof-fsync-always'(AOF fsync)。将阈值设置为您的 SLO 并定期监控。
# enable latency monitoring (threshold in ms)
CONFIG SET latency-monitor-threshold 100 # 100ms
# view recent latency events
LATENCY LATEST
# event, timestamp, last latency, max latency
# history of an event
LATENCY HISTORY command
LATENCY HISTORY expire-cycle
LATENCY HISTORY fork
# ASCII art graph
LATENCY GRAPH command
# human-readable diagnosis and recommendations
LATENCY DOCTOR
# reset latency data
LATENCY RESET
LATENCY RESET command
# common events:
# command slow commands (over threshold)
# fast-command slow O(1)/O(log N) commands
# expire-cycle key expiration sweep too slow
# fork BGSAVE/BGREWRITEAOF fork delay
# aof-fsync-always AOF fsync delay
# unlink async delete delay
# check threshold
CONFIG GET latency-monitor-thresholdMEMORY DOCTOR 与 STATS
MEMORY USAGE 报告一个键的字节数(SAMPLES 0 用于完全准确)。MEMORY STATS 给出详细的分配器统计。MEMORY DOCTOR 提供自动诊断(查找碎片、大键等)。关注 mem_fragmentation_ratio:>1.5 浪费内存(碎片),<1 表示 Redis 在交换(对延迟是灾难性的)。MEMORY PURGE 尝试将内存返回给操作系统。主动碎片整理(4.0+)自动回收碎片内存。
# MEMORY USAGE: bytes used by a single key
MEMORY USAGE mykey
MEMORY USAGE bigset SAMPLES 0 # exact (default samples for large keys)
# MEMORY STATS: detailed allocator stats
MEMORY STATS
# MEMORY DOCTOR: automated memory diagnosis
MEMORY DOCTOR
# MEMORY MALLOC-STATS: low-level allocator stats (jememalloc)
MEMORY MALLOC-STATS
# check total memory used
INFO memory | grep used_memory_human
# key metrics to watch:
# used_memory logical bytes used
# used_memory_rss RSS from OS (real footprint)
# used_memory_peak peak usage
# mem_fragmentation_ratio >1.5 = fragmentation, <1 = swapping (bad!)
# MEMORY PURGE: attempt to free memory back to OS
MEMORY PURGE
# active defragmentation (4.0+)
CONFIG SET activedefrag yesDEBUG 命令
DEBUG 命令用于诊断和测试 — 谨慎使用。DEBUG SLEEP 测试您的应用如何处理延迟(阻塞服务器)。DEBUG OBJECT 揭示键的内部编码和引用计数。DEBUG RELOAD 保存、刷新并重新加载数据集(用于测试持久化)。这些功能强大但危险 — 在生产环境中用 ACL 限制(-@admin 或 -@dangerous 类别)。在不了解影响的情况下,切勿在生产主节点上运行 DEBUG。
# DEBUG SLEEP: block the server for N seconds (testing only!)
DEBUG SLEEP 2 # blocks for 2 seconds (testing latency)
# DEBUG OBJECT: raw internal info about a key
DEBUG OBJECT mykey
# Value at:0x7f... refcount:1 encoding:embstr serializedlength:12 ...
# DEBUG SET-ACTIVE-EXPIRE: toggle expiration sweeps
DEBUG SET-ACTIVE-EXPIRE 0 # disable expiration sweeps (debugging)
DEBUG SET-ACTIVE-EXPIRE 1 # re-enable
# DEBUG JMAP: memory map (jemalloc)
DEBUG JMAP
# DEBUG CHANGE-REPL-ID: change replication ID (advanced)
DEBUG CHANGE-REPL-ID
# DEBUG RELOAD: save RDB, flush, reload from RDB (testing)
DEBUG RELOAD
# DEBUG LOADAOF: reload from AOF (testing)
DEBUG LOADAOF
# WARNING: DEBUG commands can be dangerous
# use ACL to restrict them in production:
# ACL SETUSER default -@adminredis-cli --latency
redis-cli --latency 持续测量往返时间 — 对于基线化网络性能至关重要。--latency-history 每 15s 记录滚动统计(使用 -i 更改)。--latency-dist 显示直方图(可视化抖动和尾延迟)。对于集群模式,分别检查到每个节点的延迟。建立基线,然后监控峰值。本地主机上延迟 >1ms 表示有问题(CPU、内存压力或慢命令)。
# continuous latency measurement (round-trip time)
redis-cli --latency
# output: min: 0, max: 1, avg: 0.27 (3421 samples)
# latency history (rolling, every N seconds)
redis-cli --latency-history
# default interval: 15 seconds
# output: min: 0, max: 2, avg: 0.31 (1500 samples) -- 15.01 seconds
# custom interval (1 second)
redis-cli --latency-history -i 1
# latency distribution (histogram)
redis-cli --latency-dist
# shows a visual histogram of latency distribution
# latency to a remote host
redis-cli -h remote-host --latency
# use cases:
# - baseline your network latency
# - detect network issues (jitter, spikes)
# - compare latency before/after config changes
# - monitor latency over time with --latency-history
# for cluster mode, check latency to each node:
redis-cli -h node1 -p 7000 --latency &
redis-cli -h node2 -p 7000 --latency &redis-cli --stat
redis-cli --stat 类似于 Redis 的 'top' — 它显示键、内存、客户端、阻塞客户端、请求/秒和连接的刷新摘要。请求旁边的 (+N) 显示自上次采样以来的增量。用它进行快速健康检查并发现异常(内存增长、客户端泄漏、请求率激增)。'child' 列显示是否有 BGSAVE/BGREWRITEAOF 子进程在运行。
# --stat: continuous server stats (like top for Redis)
redis-cli --stat
# output (refreshes every second):
# ------- data ------ --------------------- load -------------------- - child -
# keys mem clients blocked requests connections
# 1000 1.50M 50 0 12345 (+12) 1000
# --stat shows:
# keys total keys in the db
# mem memory usage
# clients connected clients
# blocked blocked clients (BLPOP, etc.)
# requests total requests (+N since last sample)
# connections total connections since start
# child child process info (BGSAVE/BGREWRITEAOF)
# custom interval (every 5 seconds)
redis-cli --stat -i 5
# monitor a remote server
redis-cli -h host --stat
# use cases:
# - quick health check (are clients/memory/keys growing?)
# - spot traffic spikes (requests/second)
# - monitor during load tests
# - detect connection leaks (clients increasing steadily)
# combine with --no-raw for machine-readable output安全
AUTH(传统密码)
AUTH 是传统的单密码认证(Redis 6 之前)。在 Redis 6+ 中,使用 ACL 用户(AUTH 用户名 密码)。-a 标志会在 shell 历史记录中暴露密码 — 优先使用 REDISCLI_AUTH 环境变量。始终使用强密码(64+ 随机字符)。在生产环境中,将 AUTH 与 TLS、网络限制(bind/防火墙)和基于 ACL 的最小权限结合使用以实现深度防御。
# set a password (simple, legacy)
CONFIG SET requirepass "strongpassword"
# authenticate (legacy, before Redis 6)
AUTH "strongpassword"
# OK
# authenticate as a specific user (Redis 6+ ACL)
AUTH appuser "secretpassword"
# OK
# connect with password via CLI
redis-cli -a "strongpassword"
redis-cli -a "strongpassword" --no-auth-warning # suppress warning
# safer: use environment variable
export REDISCLI_AUTH="strongpassword"
redis-cli # reads REDISCLI_AUTH automatically
# remove password
CONFIG SET requirepass ""
# AUTH returns ERR if the password is wrong
# after AUTH, all commands work normallyACL SETUSER
Redis 6+ ACL 取代了单密码模型 — 创建具有范围权限的用户。语法:on(启用)、>password、~pattern(键模式)、+command/-command、+@category/-@category(例如,+@read、-@dangerous)。始终为每个应用创建最小权限用户。default 用户始终存在 — 设置强密码或限制它。将 ACL 保存到文件(ACL SAVE)以持久化。
# Redis 6+: ACL system with users and permissions
# create a user with limited access
ACL SETUSER appuser on >secretpassword ~app:* +get +set +del -@dangerous
# on = enabled
# >password = set password
# ~pattern = key patterns this user can access
# +command = allowed commands
# -command = denied commands
# +@category / -@category = allow/deny command categories
# categories: @read, @write, @admin, @dangerous, @keyspace, @connection, etc.
# create a read-only user
ACL SETUSER readonly on >pass ~* +@read -@dangerous
# create an admin user
ACL SETUSER admin on >adminpass ~* +@all
# create a user for a specific app (restricted keys)
ACL SETUSER app1 on >pass ~app1:* +@read +@write +@connection -@dangerous
# the default user (always exists)
ACL SETUSER default on >defaultpass ~* +@all -@dangerousACL LIST 与 GETUSER
ACL LIST 以紧凑规则格式显示所有用户。ACL GETUSER 给出一个用户的详细权限(包括哈希密码、允许/拒绝的命令、键模式)。ACL WHOAMI 返回当前用户。ACL LOG(7.0+)记录被拒绝的命令和认证失败 — 适用于安全审计。ACL SAVE/LOAD 管理 ACL 文件以跨重启持久化。
# list all users (compact rules)
ACL LIST
# 1) "user default on nopass ~* +@all"
# 2) "user appuser on #abc123... ~app:* +get +set +del -@dangerous"
# view a user's detailed permissions
ACL GETUSER appuser
# flags, passwords, commands, keys, channels, selectors
# view current user
ACL WHOAMI
# "appuser"
# view all users (just names, 7.0+)
ACL USERS
# delete a user
ACL DELUSER appuser
# save ACLs to a file (persists across restarts)
ACL SAVE
# reload ACLs from file
ACL LOAD
# aclfile in redis.conf:
# aclfile /etc/redis/users.acl
# view current user's rules
ACL LOG # security audit log (7.0+)
ACL LOG RESET # clear the audit logACL WHOAMI 与 DELUSER
ACL WHOAMI 标识当前用户。ACL DELUSER 移除用户(并断开其活动连接)。将用户设置为 'off' 可临时禁用而不删除其规则 — 适用于暂停访问。ACL LOG 记录安全事件(被拒绝的命令、认证失败)— 监控它以发现可疑活动。定期使用 ACL SETUSER >newpass 轮换密码。
# get the current authenticated user
ACL WHOAMI
# "appuser"
# switch user (re-authenticate)
AUTH adminuser "adminpass"
ACL WHOAMI
# "adminuser"
# delete a user (disconnects their clients)
ACL DELUSER tempuser
# temporarily disable a user (keeps the rules, blocks login)
ACL SETUSER appuser off
# the user cannot authenticate, but their rules are preserved
# re-enable:
ACL SETUSER appuser on
# rotate a user's password
ACL SETUSER appuser >newpassword
# remove a user's password
ACL SETUSER appuser <secretpassword # remove this password
# or remove all passwords:
ACL SETUSER appuser resetpass
# view the audit log (denied commands, auth failures)
ACL LOG
ACL LOG COUNT 5 # last 5 entries
ACL LOG RESET # clear the logrename-command(禁用命令)
rename-command(在 redis.conf 中)禁用或重命名危险命令 — 设置为空字符串禁用,或字符串重命名。它需要重启(不可在运行时更改)。在 Redis 6+ 中,优先使用 ACL(-flushall、-@dangerous),它可在运行时更改且按用户设置。值得限制的命令:FLUSHALL/FLUSHDB(数据丢失)、KEYS(阻塞服务器)、CONFIG(安全)、DEBUG(可能崩溃)、SHUTDOWN(停止服务器)。
# rename or disable dangerous commands (in redis.conf)
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG "CONFIG_9f8a7b"
rename-command KEYS ""
rename-command DEBUG ""
rename-command SHUTDOWN "SHUTDOWN_7x9y"
# "" completely disables the command (returns error)
# a string renames it (must use the new name)
# Redis 6+: prefer ACLs instead of rename-command
# ACL SETUSER default -flushall -config -keys -@dangerous
# view available commands (after rename/restrictions)
COMMAND
# note: rename-command requires a restart (not changeable at runtime)
# ACLs are runtime-changeable and more flexible
# dangerous commands to restrict:
# FLUSHALL, FLUSHDB - delete all data
# KEYS - blocks server
# CONFIG - can change security settings
# DEBUG - can crash the server
# SHUTDOWN - stops the server
# MONITOR - performance impactTLS/SSL 加密
TLS 加密传输中的数据 — 对于生产环境至关重要,尤其是在不可信网络上。Redis 6+ 原生支持 TLS。mTLS(tls-auth-clients yes)要求客户端提供证书,提供无需密码的双向认证。对于集群/sentinel,也在节点间通信上启用 TLS。端口 0 禁用明文;迁移期间临时运行两个端口。生产环境中始终使用正确的 CA 签名证书。
# enable TLS in redis.conf (Redis 6+)
port 0 # disable plain-text port
tls-port 6379
tls-cert-file /etc/redis/redis.crt
tls-key-file /etc/redis/redis.key
tls-ca-cert-file /etc/redis/ca.crt
# connect with TLS
redis-cli --tls --cert client.crt --key client.key \
--cacert ca.crt -h host -p 6379
# mutual TLS (mTLS) for client authentication
tls-auth-clients yes # require client certs
tls-auth-clients no # don't require (default)
tls-auth-clients optional # verify if provided
# TLS for cluster and replication
tls-cluster yes # TLS between cluster nodes
tls-replication yes # TLS between primary/replica
# mix TLS and plain text (transition period)
# port 6380
# tls-port 6379
# generate self-signed certs for testing:
# openssl req -x509 -newkey rsa:4096 -keyout redis.key \
# -out redis.crt -days 365 -nodes数据导入与导出
redis-cli --rdb(备份)
redis-cli --rdb 执行在线备份 — 它将 RDB 流式传输到文件,不直接接触服务器的文件系统。它是非阻塞的(使用 BGSAVE 的后台分叉)且对生产安全。结合 SSH,您可以将远程 Redis 备份到本地文件。用 redis-check-rdb 验证备份。通过 cron 定期调度备份。始终测试恢复流程 — 未测试的备份不是备份。
# dump the RDB snapshot to a file (online backup)
redis-cli --rdb /backup/dump.rdb
# Output: RDB file written to /backup/dump.rdb
# dump RDB to a remote host's file (via SSH):
ssh remote-host "redis-cli --rdb /tmp/dump.rdb"
# the --rdb flag:
# - connects to Redis and requests a BGSAVE
# - streams the RDB to the specified file
# - non-blocking (uses the background fork)
# - safe to run on a production server
# verify the RDB file
redis-check-rdb /backup/dump.rdb
# restore: stop Redis, replace dump.rdb, restart
redis-cli SHUTDOWN
cp /backup/dump.rdb /var/lib/redis/dump.rdb
redis-server /etc/redis/redis.conf
# schedule regular backups:
# 0 2 * * * redis-cli --rdb /backup/dump-$(date +\%F).rdbredis-cli --pipe(批量插入)
redis-cli --pipe 是向 Redis 批量加载数据的最快方式 — 它流式传输原始 RESP 且直到最后才等待回复。用 printf 或脚本生成 RESP。--pipe 报告错误和总回复数。对于更简单/更小的加载,管道传输文本命令(echo | redis-cli)可行但较慢。始终进行基准测试:--pipe 可在几分钟内加载数百万个键。大型加载期间注意内存。
# --pipe: bulk load raw RESP data (fastest method)
# generate RESP and pipe it:
# example: bulk SET commands (bash)
for i in $(seq 1 100000); do
printf '*3\r\n$3\r\nSET\r\n$%d\r\nkey:%d\r\n$%d\r\nvalue%d\r\n' \
$((4 + ${#i})) $i $((5 + ${#i})) $i
done | redis-cli --pipe
# simpler text mode (slower but easier):
for i in $(seq 1 100000); do
echo "SET key:$i value$i"
done | redis-cli
# --pipe mode output:
# All data transferred. Waiting for the last reply...
# Last reply received from server.
# errors: 0, replies: 100000
# prepare RESP from a CSV/data file:
# (each line: key,value)
while IFS=, read -r key value; do
printf '*3\r\n$3\r\nSET\r\n$%d\r\n%s\r\n$%d\r\n%s\r\n' \
${#key} "$key" ${#value} "$value"
done < data.csv | redis-cli --pipeDUMP 与 RESTORE
DUMP 将键的值序列化为二进制 blob(包含类型和值,但不包含 TTL — 在 RESTORE 中以毫秒指定 TTL)。RESTORE 将其反序列化到键中,可跨不同 Redis 实例工作(非常适合迁移)。序列化格式是特定于版本的 — Redis 4.x 的 blob 可能无法在 7.x 中加载;始终验证兼容性。对于实例内复制,优先使用原生 COPY 命令(6.2+)而非 DUMP+RESTORE。
# DUMP: serialize a key's value (binary blob)
DUMP mykey
# returns a serialized blob (binary, includes type + value + TTL info)
# RESTORE: deserialize into a key
RESTORE newkey 0 <serialized_blob>
# 0 = no TTL (milliseconds); set a TTL otherwise
RESTORE newkey 3600000 <blob> # 1 hour TTL
RESTORE newkey 0 <blob> REPLACE # overwrite if exists
# copy a key (6.2+ does this natively with COPY)
# manual copy via DUMP + RESTORE:
SET source "value"
blob = DUMP source
RESTORE dest 0 blob
# DUMP/RESTORE preserve the type but NOT the TTL
# (TTL must be specified in RESTORE)
# DUMP/RESTORE work across different Redis instances
# (migrate data between servers)
# note: serialized format is version-specific
# a blob from Redis 4.x may not load in 7.x — verify compatibility
# use COPY (6.2+) instead for in-instance copies:
COPY source dest # copy without TTL
COPY source dest REPLACE # overwrite destination
COPY source dest DB 1 # copy to another database
COPY source dest REPLACE DB 1 # bothMIGRATE(在服务器间移动)
MIGRATE 原子性地在 Redis 实例间移动(或复制)键 — 键永远不会同时出现在两个地方(原子性)。它是集群重新分片的构建块。COPY 保留源键;默认删除它。REPLACE 覆盖已存在的目标键。对于多个键,传递空字符串作为键并用 KEYS 列出它们。为大值设置足够的超时。MIGRATE 在传输期间在两端都是阻塞的。
# MIGRATE: atomically move a key from one Redis to another
# syntax: MIGRATE host port key|"" dest-db timeout [COPY] [REPLACE]
# [AUTH password] [KEYS key1 key2 ...]
# move a single key (default: deletes from source after copy)
MIGRATE 192.168.1.20 6379 mykey 0 5000
# host, port, key, dest-db, timeout-ms
# copy without deleting from source
MIGRATE 192.168.1.20 6379 mykey 0 5000 COPY
# overwrite if the key exists on destination
MIGRATE 192.168.1.20 6379 mykey 0 5000 REPLACE
# migrate multiple keys (pass "" as the key, then KEYS ...)
MIGRATE 192.168.1.20 6379 "" 0 5000 KEYS key1 key2 key3
# authenticate with the destination
MIGRATE 192.168.1.20 6379 mykey 0 5000 AUTH destpassword
# MIGRATE is atomic: the key appears on exactly one instance at any moment
# timeout is in milliseconds — large keys need longer timeouts
# returns NOKEY if the key doesn't exist on sourceCOPY(实例内复制)
COPY(6.2+)在同一实例内原子性地复制键 — 比 DUMP+RESTORE 简单得多。它默认不复制 TTL(之后使用 EXPIRE)。COPY 通过 DB 选项跨数据库工作。REPLACE 覆盖已存在的目标。对于跨实例复制,使用 MIGRATE(在网络间是原子性的)。COPY 是 DUMP+RESTORE 模式用于实例内复制的现代替代品。