Skip to content

Redis 速查表

内存数据结构存储,可用作数据库、缓存和消息代理。

01

入门

连接 Redis

redis-cli 是交互式命令行客户端。默认连接本地 6379 端口。始终使用 PING 测试连接(返回 PONG 表示成功)。在远程连接时使用 -a 标志或 AUTH 命令提供密码。redis-cli 还支持管道输入、--stat 监控和 --bigkeys 扫描大键——它是日常运维瑞士军刀般的工具。

redis
# connect to a local server (default port 6379)
redis-cli

# connect with a specific host and port
redis-cli -h host.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

# test the connection
PING          # returns PONG
PING "hello"  # returns "hello"

数据库选择与基础

Redis 开箱即用地提供 16 个逻辑数据库(编号 0-15),由 SELECT 切换。它们共享同一个内存空间——不是真正的隔离。多个数据库主要用于在单个实例上分隔不同应用。FLUSHDB 清空当前数据库;FLUSHALL 清空所有数据库——生产环境中要极其小心使用。在现代部署中,建议为每个应用使用独立实例而非共享数据库。

redis
# 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

# get basic server info
INFO server
INFO memory
INFO replication

键与通用操作

键是二进制安全的——任何字节序列(包括空字节)都可用。约定使用冒号分隔的命名空间(如 user:100:profile),这在 Redis 仪表盘中可读性很高。DEL 是阻塞式的——删除大集合时会卡住服务器,改用 UNLINK 异步删除。TYPE 报告值的类型;RANDOMKEY 随机返回一个键。键名应简洁但具描述性,并避免过长的前缀以节省内存。

redis
# set and get a key
SET user:1 "Alice"
GET user:1

# check if a key exists
EXISTS user:1          # 1 if exists, 0 if not

# delete keys
DEL user:1
DEL key1 key2 key3     # returns number deleted

# set a key with expiration (seconds)
SET session:abc "data" EX 3600

# find keys matching a pattern (avoid in production)
KEYS user:*
SCAN 0 MATCH user:* COUNT 100   # safer iteration

# rename a key
RENAME oldkey newkey

# get the type of a value stored at a key
TYPE user:1

过期与 TTL

EX(秒)、PX(毫秒)、EXAT、PXAT 都在 SET 时设置过期。TTL/PTTL 返回剩余存活时间;-1 表示无过期,-2 表示键不存在。过期键采用惰性删除(访问时检查)加上定期采样主动删除。PERSIST 移除过期。过期精度为毫秒级但不保证精确——事件循环的频率决定实际删除时间。对于会话/缓存等易失数据始终设置 TTL,避免内存无限增长。

redis
# set expiration on an existing key
EXPIRE user:1 60        # 60 seconds
EXPIREAT user:1 1700000000  # Unix timestamp

# set expiration in milliseconds
PEXPIRE user:1 60000

# view remaining time to live (seconds)
TTL user:1              # -2 if no key, -1 if no expiry

# view TTL in milliseconds
PTTL user:1

# remove expiration (make key persistent)
PERSIST user:1

# set value AND expiration atomically
SET token "abc" EX 3600

SCAN 与安全迭代

永远不要在生产环境使用 KEYS ——它会阻塞服务器遍历整个键空间。SCAN 是基于游标的替代方案,返回新游标直到为 0 结束。结果可能重复或遗漏已变更的键——这是设计上的权衡。MATCH 在服务端过滤(仍扫描所有键);COUNT 是提示而非保证。HSCAN、SSCAN、ZSCAN 用于遍历大集合字段。对后台任务和监控脚本,SCAN 是唯一安全的选择。

redis
# SCAN: cursor-based iteration (non-blocking)
SCAN 0 MATCH user:* COUNT 100
# returns: [nextCursor, [key1, key2, ...]]
SCAN <nextCursor> MATCH user:* COUNT 100
# continue until cursor returns to 0

# scan a specific type
SCAN 0 TYPE hash

# HSCAN: iterate hash fields
HSCAN myhash 0 MATCH field* COUNT 100

# SSCAN: iterate set members
SSCAN myset 0 MATCH member* COUNT 100

# ZSCAN: iterate sorted set members
ZSCAN myzset 0 MATCH member* COUNT 100
02

数据类型概览

核心数据结构

Redis 提供 5 种核心类型及若干扩展类型。字符串存任意字节(最大 512MB);哈希存字段-值映射;列表是有序字符串序列;集合存无序唯一元素;有序集合按分数排序。4.8+ 新增 Streams(日志),3.2+ 新增地理空间。选对类型至关重要——决定功能、性能和内存占用。Redis 在 Redis 7+ 中围绕每种类型实现了更高效的内部编码。

redis
# Redis is a data structure server, not just a key-value store
# Each key holds a typed data structure:

# String: binary-safe blob (up to 512MB)
SET key "value"
SET counter 100

# List: ordered, linked-list of strings
LPUSH mylist "a" "b"
RPUSH mylist "c"

# Hash: field-value map (like a small object)
HSET user:1 name "Alice" age 30

# Set: unordered collection of unique strings
SADD tags "redis" "db"

# Sorted Set (ZSet): set scored by a float
ZADD leaderboard 100 "alice" 200 "bob"

# Stream: append-only log with IDs
XADD mystream * field value

# Bitmap, HyperLogLog, Geo: built on strings/zsets

选择正确的类型

按访问模式而非仅数据形状选择类型。要计数?用字符串 INCR。要存对象字段?用哈希 HSET(而非用 JSON 字符串阻塞整个值的修改)。要队列?用列表 LPUSH/RPOP 或 Streams(更健壮)。要排行榜?用有序集合。要唯一标签?用集合。用 TYPE 检查键类型;若期望 STR 却遇到 LIST,说明有 bug——Redis 不会自动转换类型。

redis
# Use case -> data type mapping:
# Cache a string  -> String
SET api:response "json_blob"

# Store an object -> Hash (one key, multiple fields)
HSET user:1 name "Alice" email "[email protected]" age 30

# Queue / stack   -> List
LPUSH tasks "job1"   # push to head
RPOP tasks           # pop from tail (FIFO queue)

# Unique tags      -> Set
SADD post:1:tags "redis" "db" "cache"

# Leaderboard      -> Sorted Set
ZADD scores 100 "alice" 250 "bob"
ZREVRANGE scores 0 9     # top 10

# Event log        -> Stream
XADD events * type click user 1

# Count unique     -> HyperLogLog (approximate, fixed memory)
PFADD visitors "user1" "user2"

内存编码

Redis 在底层为小结构使用紧凑编码以节省内存。小列表用 listpack(旧版 ziplist),成长到阈值后切换为 linkedlist/quicklist。小哈希和集合同样从 listpack 切换到 hashtable。小有序集合从 listpack 切换为 skiplist+hashtable。阈值由 list-max-listpack-size 等配置。OBJECT ENCODING 查看实际编码。紧凑编码省内存但某些操作变慢(O(n) 而非 O(1))——按数据规模调优。

redis
# Redis optimizes storage based on size
# Inspect the internal encoding of a key
OBJECT ENCODING mykey

# String encodings:
#   "embstr"  - short strings (< 44 bytes)
#   "raw"     - long strings
#   "int"     - integer values

# Hash/List/Set/ZSet encodings:
#   "listpack" / "intset" - small (compact, memory-efficient)
#   "hashtable" / "list" / "skiplist" - large (faster, more memory)

# check memory usage of a key
MEMORY USAGE mykey

# sample memory usage stats
MEMORY STATS

# the encoding changes automatically as the structure grows

OBJECT 与内存命令

OBJECT ENCODING 查看键的内部表示,在调优时很有价值。OBJECT IDLETIME 报告最后一次访问以来的秒数(仅在使用近似 LRU/LFU 驱逐策略时收集)。MEMORY USAGE(4.0+)估算单个键占用的字节数——对排查大键很方便。MEMORY DOCTOR 在 4.0+ 中给出内存诊断。DEBUG OBJECT 提供原始内部信息但需谨慎(可能很慢)。这些工具帮助理解 Redis 实际如何存储数据。

redis
# OBJECT subcommands for introspection
OBJECT ENCODING mykey     # internal encoding
OBJECT REFCOUNT mykey     # reference count
OBJECT IDLETIME mykey     # seconds since last access
OBJECT FREQ mykey         # access frequency (LFU mode)

# check memory usage (bytes) of a key
MEMORY USAGE mykey
MEMORY USAGE mykey SAMPLES 0   # exact count

# total memory used by the server
INFO memory | grep used_memory_human

# help understand a command
COMMAND INFO GET
COMMAND DOCS SET

# COMMAND lists all commands (huge output)
COMMAND

类型错误与类型检查

对某个键运行类型不匹配的命令会返回 WRONGTYPE 错误(如对列表执行 GET)。Redis 不会自动转换类型——这是有意设计,避免静默数据损坏。客户端代码应优雅处理 WRONGTYPE,因为它通常指示逻辑 bug。用 TYPE 命令在运行前检查类型。JSON 模块(RedisJSON)增加了一等公民的 JSON 类型,但需在服务端启用模块。

redis
# each key has ONE type; using the wrong command fails
SET mykey "hello"
LPUSH mykey "x"        # WRONGTYPE error

# WRONGTYPE Operation against a key holding the wrong kind of value

# check the type before operating
TYPE mykey             # "string"
if [ "$(TYPE mykey)" = "string" ]; then ...

# TYPE returns: string|list|hash|set|zset|stream|none

# rename preserves the type
RENAME mykey newkey
TYPE newkey            # still "string"

# type is immutable; delete + recreate to change types
DEL mykey
LPUSH mykey "x"        # now a list
03

字符串

基础字符串操作

字符串是 Redis 最基础类型。SET 是写,GET 是读,DEL 是删除。NX 选项表示仅在键不存在时设置(用于锁);XX 表示仅在已存在时设置。GETSET 原子地返回旧值并设置新值。字符串上限 512MB,但大字符串几乎总意味着设计错误——考虑用哈希或分片。SETRANGE 和 GETRANGE 修改子串(后者对二进制安全)。

redis
# set and get
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 mykey "new" EX 60 NX  # with expiry, only if new

# get and set atomically (returns old value)
GETSET counter 0        # returns old value, sets new

# append to a string
APPEND mykey " world"   # "hello world"

# get substring
GETRANGE mykey 0 4      # "hello"
SETRANGE mykey 6 "redis"# "hello redis"

# get string length
STRLEN mykey            # 11

数值计数器

若字符串内容是整数,INCR/DECR 会原子地增减。INCRBY/DECRBY 增减指定量;INCRBYFLOAT 支持浮点。这些操作完全原子——多个客户端并发 INCR 永远不会丢失更新,使其成为计数器、限流和 ID 生成的理想选择。注意 INCRBYFLOAT 可能引入浮点精度误差——对货币等敏感场景请考虑用整数存储(如以分计价)或 Lua 脚本处理。

redis
# increment a numeric string atomically
INCR counter            # counter = counter + 1
INCRBY counter 10       # counter = counter + 10

# decrement
DECR counter            # counter = counter - 1
DECRBY counter 5        # counter = counter - 5

# floating point operations
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

多键操作

MSET/MGET 批量设置/获取多个键,减少往返次数。原子性方面:MSET 是原子的(全部设置或全不),但不要与 MULTI 混淆。MSETNX 仅当所有键都不存在时设置。在集群模式下,多键命令要求所有键在同一槽位(用哈希标签 {} 保证)。对大量键优先用 MGET 而非多次 GET——开销显著更低。

redis
# set multiple keys at once (atomic)
MSET k1 "v1" k2 "v2" k3 "v3"

# get multiple keys at once
MGET k1 k2 k3           # ["v1", "v2", "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

# atomic get-and-delete (useful for queues)
GETDEL mykey            # returns value AND deletes key

位图(基于字符串)

位图操作将字符串视为位数组。SETBIT 设置某位,GETBIT 读取,BITCOUNT 统计 1 的个数,BITOP 做位运算。典型用途:每日用户活跃统计(每天一个键,用户 ID 对应位偏移)、布隆过滤器、特征标志。对稀疏位图,考虑用 SETBIT 时偏移很大可能导致内存分配——改用 HyperLogLog 做基数估计更省内存。BITFIELD 支持多字节字段操作。

redis
# 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:daily:2025-01-01 5 1
SETBIT users:daily:2025-01-02 5 1
BITOP AND active-both users:daily:2025-01-01 users:daily:2025-01-02
BITCOUNT active-both        # users active on both days

# find the first set bit
BITPOS user:1:active 1      # position of first 1-bit

位域操作

BITFIELD(3.2+)在单个字符串上操作任意宽度(最大 64 位)的整数字段。支持有符号/无符号,GET/SET/INCRBY 操作,以及溢出控制(WRAP 饱和、SAT、FAIL 拒绝)。非常适合在单个键中存储多个小计数器——比每计数器一个键省内存得多。例如用两个 32 位字段存用户的点赞数和被点赞数,仅占一个键。注意字段偏移以位为单位。

redis
# BITFIELD: multiple counters in one string (compact)
# store up to 2^63 counters, each 1-64 bits, in a single key

# set a 8-bit unsigned counter at offset 0
BITFIELD mycount SET u8 0 100

# increment a counter (with overflow control)
BITFIELD mycount INCRBY u8 0 10   # returns 110
BITFIELD mycount INCRBY u8 0 10   # returns 120

# overflow control: WRAP (default), SAT, FAIL
BITFIELD mycount OVERFLOW SAT INCRBY u8 0 200  # saturates at 255
BITFIELD mycount OVERFLOW FAIL INCRBY u8 0 999 # returns nil (overflow)

# read multiple counters in one call
BITFIELD mycount GET u8 0 GET u8 8 GET u8 16
04

哈希

哈希基础

哈希存储字段-值映射,是表示对象的理想结构(如 user:100 有 name、email、age 字段)。相比存 JSON 字符串,哈希允许原子地修改单个字段而无需读出-修改-写回整个对象。HSET 一次可设置多个字段。HGET 取单个,HMGET 取多个,HGETALL 取全部。小哈希使用 listpack 编码内存极省。每个哈希最多 2^32-1 个字段。

redis
# 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 multiple fields
HMGET user:1 name age       # ["Alice", "30"]

# get all fields and values
HGETALL user:1
# name  -> Alice
# age   -> 30
# email -> [email protected]

# get all field names or values
HKEYS user:1                # [name, age, email]
HVALS user:1                # [Alice, 30, [email protected]]

# get the number of fields
HLEN user:1                 # 3

哈希字段操作

HSETNX 仅在字段不存在时设置。HINCRBY/HINCRBYFLOAT 原子地增减字段值——适合在对象内维护计数器(如商品库存)。HLEN 返回字段数。HSTRLEN 返回某字段值的字节数。HDEL 删除字段。这些操作都是原子的且只影响指定字段,无需锁定整个哈希。这使哈希成为存储和更新结构化数据的高效选择。

redis
# 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 field

# check if a field exists
HEXISTS user:1 name          # 1 if exists, 0 if not

# increment a numeric field atomically
HINCRBY user:1 age 1         # age = 31
HINCRBY user:1 age -5        # age = 26
HINCRBYFLOAT user:1 score 0.5

# get the string length of a field's value
HSTRLEN user:1 name          # 5 (length of "Alice")

# set multiple fields (same as HSET with multiple)
HMSET user:1 a 1 b 2         # deprecated, use HSET

哈希迭代

HSCAN 增量迭代哈希字段,避免 HGETALL 在大哈希上阻塞。返回游标和匹配字段。HKEYS 返回所有字段名,HVALS 返回所有值——两者在集合较大时为 O(n),谨慎使用。HSCAN 在分页展示或后台导出时很有用。MATCH 参数在服务端过滤字段名。注意 HSCAN 不保证返回顺序,且在迭代期间被修改的哈希可能返回重复或遗漏字段。

redis
# HSCAN: cursor-based iteration (for large hashes)
HSCAN user:1 0 MATCH "na*" COUNT 10
# returns [cursor, [field, value, field, value, ...]]

# iterate all fields
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

# get a random field
HRANDFIELD user:1            # one random field name
HRANDFIELD user:1 3          # 3 random field names
HRANDFIELD user:1 3 WITHVALUES  # fields + values

哈希过期(Redis 7.4+)

Redis 7.4+ 终于支持对哈希的单个字段设置过期——HEXPIRE、HPEXPIRE、HEXPIREAT、HPEXPIREAT、HTTL、HPTTL、HPERSIST。在此之前只能对整个键设置 TTL,要实现字段过期需要应用层逻辑(如用独立键或定期清理脚本)。字段级过期对会话管理、带 TTL 的缓存字段等场景极有价值。HTTL 返回字段剩余秒数。旧版 Redis 需升级到 7.4+ 才能使用。

redis
# before 7.4, expiration was only at the key level
EXPIRE user:1 3600    # the whole hash expires

# Redis 7.4+: field-level TTL (HEXPIRE)
HEXPIRE user:1 60 FIELDS 1 session_token
# expires only the session_token field in 60 seconds

# get TTL of a specific field
HPTTL user:1 FIELDS 1 session_token

# persist a specific field (remove its TTL)
HPERSIST user:1 FIELDS 1 session_token

# get all fields and their expiration times
HEXPIRETIME user:1 FIELDS 1 session_token

哈希用例

哈希是 Redis 中表示对象最自然的方式:每个字段独立可读写,无需序列化/反序列化整个对象。对计数器场景(如商品库存、点赞数),HINCRBY 原子增减。对会话存储,HSET 存会话字段 + EXPIRE 设置整体 TTL。相比用多个字符串键表示对象字段,哈希在内存和管理上都更高效。唯一例外是需要对整个对象做范围查询或复杂索引时,考虑其他结构。

redis
# 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. Group small key-value pairs into one hash (memory savings)
# instead of SET k1 v1; SET k2 v2 -> HSET bucket k1 v1 k2 v2
05

列表

列表基础(推入/弹出)

列表是有序字符串序列,支持头尾两端 O(1) 推入/弹出。LPUSH/RPUSH 在头/尾添加;LPOP/RPOP 从头/尾弹出。LRANGE 查看范围(0 到 -1 查看全部)。LLEN 返回长度。BLPOP/BRPOP 是阻塞版本——当列表为空时挂起直到有元素或超时,是构建队列的利器。列表在 Redis 7+ 中使用 listpack(小)或 quicklist(大)编码。

redis
# lists are ordered sequences of strings (linked lists)
# push to head (left) or tail (right)
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
LPOP mylist 2             # ["a", ...]

# 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

列表索引与范围

LINDEX 按索引读取单个元素(负索引从末尾计)。LSET 修改指定索引的元素。LINSERT 在指定元素前或后插入。LTRIM 保留指定范围、删除其余——常与 LPUSH 配合实现固定长度的最近活动列表。LRANGE 是 O(s+n)(s 是起始偏移,n 是元素数),对大列表的分页访问应考虑其他结构(如有序集合)。避免对长列表用 LINDEX,它是 O(n)。

redis
# get element by index (0-based, O(N) traversal)
LINDEX mylist 0           # first element
LINDEX mylist -1          # last element

# get a range of elements
LRANGE mylist 0 -1        # all elements
LRANGE mylist 0 2         # first 3 elements
LRANGE mylist -3 -1       # last 3 elements

# set an element by index
LSET mylist 1 "new"

# get length
LLEN mylist

# trim to a range (removes everything outside)
LTRIM mylist 0 99         # keep only first 100 elements

# remove elements by value
LREM mylist 2 "value"     # remove first 2 occurrences of "value"
LREM mylist -2 "value"    # remove last 2 occurrences

列表作为队列

LPUSH + RPOP 构成 FIFO 队列;RPUSH + LPOP 同理。BRPOP/BLPOP 的阻塞版本让消费者在没有消息时等待而非轮询,大幅减少 CPU 和网络开销。RPOPLPUSH(及 LMOVE)原子地从源列表弹出到目标列表——用于可靠队列(处理中消息移到处理队列,完成后删除,崩溃可恢复)。对需要消息确认、消费者组和持久化的更复杂场景,使用 Streams。

redis
# 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
LPUSH source "item"
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 100

列表插入与删除

LINSERT BEFORE/AFTER 在指定元素前/后插入——需先找到元素,O(n) 操作。LREM 删除指定值的元素,可指定数量(正数从头、负数从尾、0 全部)。LREM 在大列表上很慢。LPOS(6.0+)查找元素索引。这些操作都是 O(n)——列表不适合频繁的中间操作,那是链表的场景。Redis 列表更适合队列和栈式的端点操作。

redis
# insert before or after a pivot element
LINSERT mylist BEFORE "pivot" "new"
LINSERT mylist AFTER "pivot" "new"

# remove elements by value
LREM mylist 1 "value"     # remove first occurrence from head
LREM mylist -1 "value"    # remove first occurrence from tail
LREM mylist 0 "value"     # remove ALL occurrences

# pop and push in one atomic operation
RPOPLPUSH source dest

# get and remove from both ends (6.2+)
LPOP mylist 2             # pop 2 from head
LMPOP 2 mylist LEFT COUNT 3  # (7.0+) pop from one of multiple lists

# find the position of an element (6.0+)
LPOS mylist "value"
LPOS mylist "value" RANK 2   # find 2nd occurrence

列表用例

列表天生适合队列和栈。LPUSH+BRPOP 实现简单的多消费者任务队列。LTRIM+LPUSH 实现固定长度的最近活动流(如用户最新 100 条操作)。RPOPLPUSH 实现可靠的工作队列。社交时间线可用列表(推模式:发布时推送到粉丝列表;拉模式:读取时合并)。需要去重、排序或消费者组时,升级到有序集合或 Streams。列表简单快速,但功能有限。

redis
# 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 <timestamp>
LTRIM rate:user:1 0 99
LLEN rate:user:1           # request count in window
06

集合

集合基础

集合存储无序的唯一元素。SADD 添加,SREM 删除,SISMEMBER 检查成员(O(1)),SMEMBERS 返回全部。SCARD 返回基数。小集合用 listpack 编码(元素少且都为整数时尤其高效),大集合用 hashtable。集合擅长成员测试和去重——判断某用户是否在黑名单、统计不重复的 IP 数等。每个集合最多 2^32-1 个元素。

redis
# sets: unordered collection of unique strings
SADD tags "redis" "db" "cache"
SADD tags "redis"          # ignored (already exists)

# remove a member
SREM tags "cache"

# check membership
SISMEMBER tags "redis"     # 1 if member, 0 if not

# get the number of members
SCARD tags                 # 2

# get all members (avoid on large sets)
SMEMBERS tags              # ["redis", "db"]

# get a random member
SRANDMEMBER tags           # one random member
SRANDMEMBER tags 3         # 3 random (may repeat)
SRANDMEMBER tags -3        # 3 unique random

# pop a random member (removes it)
SPOP tags                  # removes and returns one

集合运算

SINTER 交集、SUNION 并集、SDIFF 差集——这是集合的杀手锏。SINTERSTORE 等将结果存入新集合。运算在多集合间进行,常用于标签系统(求同时带多个标签的内容)、好友推荐(共同好友)。复杂度约 O(N*M)(N 是最小集合大小,M 是集合数)。大集合的交集运算可能很慢——考虑用 SINTERSTORE 缓存结果或预先维护。SRANDMEMBER 随机返回元素。

redis
# 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

集合迭代与移动

SMEMBERS 在大集合上为 O(n)——可能阻塞,生产环境用 SSCAN 增量迭代。SMOVE 原子地将元素从一个集合移到另一个,常用于状态转换(如从待处理集合移到已处理集合)。SPOP 随机弹出元素,可用于抽奖或随机任务分配。SINTERCARD(7.0+)只返回交集大小不返回元素,更省内存和带宽。

redis
# SSCAN: cursor-based iteration for large sets
SSCAN myset 0 MATCH "pre*" COUNT 100
# returns [cursor, [member1, member2, ...]]

# move a member from one set to another atomically
SMOVE source dest "member"

# pop multiple random members (6.2+)
SPOP myset 3               # removes and returns 3 members

# blocking set pop (7.0+) - waits until a member exists
BSPOP myset 30             # blocks up to 30 seconds

# check multiple memberships at once (7.4+)
SMISMEMBER myset "a" "b" "c"
# returns [1, 0, 1]

集合用例

集合天生适合唯一性和集合运算。标签系统:每个内容有标签集合,查询用交集。关注/粉丝关系:SINTER 求共同好友。去重:SADD 添加,判断是否已存在。抽奖:SPOP 随机抽取。IP 白名单/黑名单:SISMEMBER 快速判断。对需要按分数排序的唯一元素,用有序集合。对需要存储每个元素额外属性的映射,用哈希。

redis
# 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
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"

# 5. Lottery / random selection
SADD participants "u1" "u2" "u3" "u4"
SPOP participants 1        # draw a winner

集合与其他类型对比

集合 vs 列表:集合去重且无序,成员测试 O(1);列表有序可重复,成员测试 O(n)。集合 vs 有序集合:集合无排序概念,有序集合按分数排序且更省内存(小集合时)。集合 vs 哈希:集合只存元素,哈希存字段-值对。选类型按需求:只要唯一性用集合;要排序用有序集合;要附加数据用哈希;要队列用列表或 Streams。Redis 的类型各有所长。

redis
# Set: unordered, unique, O(1) membership check
SADD myset "a" "b"
SISMEMBER myset "a"       # O(1)

# Sorted Set: unique + ordered by score
ZADD myzset 1 "a" 2 "b"
ZRANK myzset "a"          # 0 (rank)

# List: ordered, allows duplicates, O(N) search
LPUSH mylist "a" "a"
LPOS mylist "a"           # 0 (first match)

# Hash: field-value pairs, no ordering
HSET myhash a 1 b 2

# Choose:
# - need uniqueness only?           -> Set
# - need uniqueness + ordering?     -> Sorted Set
# - need ordering + allow dups?     -> List
# - need key-value mapping?         -> Hash
07

有序集合

有序集合基础

有序集合(ZSET)是 Redis 最强大的结构之一:每个元素带一个分数(double),按分数排序且唯一。ZADD 添加(可带 NX/XX/GT/LT 选项),ZSCORE 取分数,ZRANK 取排名(0 起),ZCARD 取基数。它用 skiplist+hashtable 实现,兼顾排序遍历和 O(1) 查找。小有序集合用 listpack 编码更省内存。是排行榜、时间线、范围查询的利器。

redis
# 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: conditional)
ZADD leaderboard NX 300 "new"
ZADD leaderboard GT 300 "alice"  # only if new score is greater

# 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

范围查询

ZRANGE(统一版本,6.2+)取代了 ZRANGEBYSCORE/ZREVRANGE 等。按索引范围:ZRANGE key 0 9(前 10);按分数范围:ZRANGE key 100 200 BYSCORE。REV 降序,LIMIT 分页。ZRANGESTORE 将结果存入新键。排行榜取 Top N 用 ZRANGE key 0 N-1 REV WITHSCORES。范围查询是 O(log(N)+M),N 是总元素数,M 是返回数——非常高效。

redis
# 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)

# get members by score range
ZRANGEBYSCORE leaderboard 100 200    # scores 100-200
ZRANGEBYSCORE leaderboard 100 +inf   # scores >= 100
ZRANGEBYSCORE leaderboard (100 200   # scores > 100 (exclusive)

# descending order
ZREVRANGE leaderboard 0 2            # top 3 (highest scores)
ZREVRANGEBYSCORE leaderboard 200 100 # 200 down to 100

# 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

分数操作与排名

ZINCRBY 原子地增减分数(排行榜更新分数的常用命令)。ZSCORE 取分数,ZRANK/ZREVRANK 取排名(升序/降序,0 起)。ZMSCORE(6.2+)批量取分数。分数相同时按元素字典序排序。排名操作是 O(log(N))。对需要频繁取排名的场景,有序集合比应用层排序高效得多——Redis 在 skiplist 中维护了排名信息。

redis
# increment a member's score
ZINCRBY leaderboard 50 "alice"     # alice += 50

# get the score
ZSCORE leaderboard "alice"

# remove a member
ZREM leaderboard "bob"

# remove members by rank range
ZREMRANGEBYRANK leaderboard 0 9    # remove lowest 10
ZREMRANGEBYSCORE leaderboard 0 100 # remove scores <= 100

# count members in a score range
ZCOUNT leaderboard 100 200

# get the rank
ZRANK leaderboard "alice"          # ascending rank
ZREVRANK leaderboard "alice"       # descending rank (0 = highest)

# get multiple members' scores at once
ZMSCORE leaderboard "alice" "bob"  # ["250", "200"]

字典序范围(BYLEX)

BYLEX 按元素字典序范围查询(分数相同才有意义)。语法用 [ 表示闭区间、( 表示开区间、- + 表示负正无穷。常用于自动补全:用有序集合存候选词(分数相同),按前缀查询。ZRANGEBYLEX 在 6.2+ 中由 ZRANGE ... BYLEX 取代。注意 BYLEX 仅在所有元素分数相同时才有意义,否则结果不可预期。

redis
# 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

有序集合聚合

ZUNIONSTORE/ZINTERSTORE 对多个有序集合做并集/交集,可指定权重和聚合函数(SUM/MIN/MAX)。ZUNION(6.2+)直接返回结果不存储。ZDIFFSTORE/ZDIFF 做差集。ZMPOP(7.0+)按分数弹出最小/最大元素。聚合常用于多维度排序(如搜索结果按相关性+新鲜度+点击数加权)。WITHSCORES 返回分数。权重参数让不同集合的贡献可调节。

redis
# ZUNIONSTORE / ZINTERSTORE: 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
08

发布/订阅

发布/订阅基础

PUBLISH 向频道发消息,SUBSCRIBE 订阅频道,UNSUBSCRIBE 取消。PSUBSCRIBE 按模式订阅(如 user:* 匹配所有以 user: 开头的频道)。发布/订阅是即时的——发布时无订阅者则消息丢弃,无持久化。订阅是阻塞式的,收到消息时回调。这是最简单的实时消息传递方式,适合实时通知、聊天。但无消息保证——需要可靠性时用 Streams。

redis
# subscribe to channels (in one client)
SUBSCRIBE news alerts

# publish a message to a channel (from another client)
PUBLISH news "Breaking: Redis 7.4 released"

# pattern subscription (glob-style)
PSUBSCRIBE news.*

# unsubscribe
UNSUBSCRIBE news
PUNSUBSCRIBE news.*

# note: published messages are NOT persisted
# if no subscriber is listening, the message is lost
# for persistent messaging, use Streams instead

分片发布/订阅(7.0+)

SPUBLISH/SSUBSCRIBE(7.0+)是分片版本:频道属于集群的特定槽位,使发布/订阅在集群中可扩展。普通 PUBLISH 在集群中广播到所有节点,开销大;SPUBLISH 只在频道所属节点处理。订阅分片频道时连接必须路由到正确节点。对大规模发布/订阅(如每秒数万消息),分片版本显著降低集群负载。旧客户端可能不支持,需升级客户端库。

redis
# 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)

发布/订阅模式与用例

发布/订阅适合一对多通知:用户上线通知、配置变更广播、聊天室。模式订阅(user:*)实现灵活的路由。注意发布/订阅无消息历史——新订阅者收不到旧消息。需要回放的用 Streams。发布/订阅消息不保证顺序跨频道——同一频道的消息有序,跨频道无序。对关键消息,订阅者应确认处理(结合列表或 Streams 做确认机制)。

redis
# 1. Real-time chat
# client subscribes to their room
SUBSCRIBE chat:room:42
# server publishes messages
PUBLISH chat:room:42 "Alice: hello"

# 2. Cache invalidation
# subscribe to invalidation channel
SUBSCRIBE cache:invalidate
# when data changes, publish
PUBLISH cache:invalidate "user:123"

# 3. Event notifications (with PSUBSCRIBE patterns)
PSUBSCRIBE user:*:login
# matches user:1:login, user:2:login, etc.

# 4. Live dashboards
SUBSCRIBE metrics:*
# push real-time updates to dashboards

# 5. Multiplayer game state
PUBLISH game:room:1 "player_moved:5:10"

键空间通知

键空间通知在键发生事件(过期、删除、修改等)时发布到 __keyspace@<db>:<key> 和 __keyevent@<db>:<event> 频道。需用 CONFIG SET notify-keyspace-events 启用。E 表示键事件通知,K 表示键空间通知,组合如 KEA 启用所有。典型用途:过期后清理关联资源、缓存失效联动。注意通知是尽力而为的——高负载下可能丢失,不要依赖其做关键逻辑。

redis
# 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

发布/订阅与 Streams 对比

发布/订阅:无持久化、无消费者组、即时传递、简单。Streams:持久化、消费者组、消息回放、ACK 确认、更复杂。简单实时通知用发布/订阅;需要可靠性、历史、多消费者公平分配用 Streams。发布/订阅消息发完即弃,Streams 像 Kafka 的日志。两者可结合:Streams 做可靠传递,发布/订阅做旁路通知。选择取决于对消息丢失的容忍度。

redis
# Pub/Sub: fire-and-forget, no persistence
PUBLISH channel "msg"   # lost if no subscribers

# Streams: persistent, replayable, consumer groups
XADD mystream * msg "hello"   # stored forever (or capped)

# Pub/Sub pros: simple, low latency, fan-out
# Pub/Sub cons: no persistence, no replay, no consumer groups

# Streams pros: persistent, replayable, consumer groups, ACK
# Streams cons: more complex, requires cleanup (XADD MAXLEN)

# when to use each:
# - real-time notifications (loss OK)     -> Pub/Sub
# - chat with presence                    -> Pub/Sub + Streams
# - reliable task queue                   -> Streams
# - event sourcing / audit log            -> Streams
# - fan-out to transient subscribers      -> Pub/Sub
09

事务与管道

MULTI/EXEC 事务

MULTI 开启事务,后续命令入队不执行,EXEC 原子地按顺序执行全部。DISCARD 取消。Redis 事务没有回滚——命令入队时语法错误会拒绝 EXEC,但运行时错误(如对字符串 INCR)不会中断后续命令也不会回滚已执行的。这意味着事务保证的是顺序执行和原子性(期间不被其他命令打断),而非 ACID 的回滚。需要条件执行时用 WATCH。

redis
# MULTI starts a transaction; commands are queued
MULTI
SET counter 1
INCR counter
INCR counter
GET counter
EXEC
# returns: [OK, 2, 3, "3"]

# discard a transaction (cancels all queued commands)
MULTI
SET x 1
DISCARD

# errors during queuing (syntax) abort the whole transaction
# errors during EXEC (e.g., wrong type) skip only that command

WATCH(乐观锁)

WATCH 实现乐观并发控制:监视键,若在 EXEC 前被修改则整个事务失败(返回 nil)。典型用法:WATCH balance; GET balance; MULTI; DECRBY balance 100; INCRBY merchant 100; EXEC。若 EXEC 返回 nil 表示有人改了 balance,重试整个流程。WATCH 是 CAS(比较并交换)的基础。UNWATCH 取消监视。事务执行后 WATCH 自动清除。

redis
# WATCH monitors keys; if any change before EXEC, the 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

管道(批量往返)

管道是客户端特性:一次性发送多条命令不等回复,减少往返延迟。与事务不同,管道不保证原子性——其他客户端的命令可能穿插。管道可将数千次往返压缩为一次,在跨地域或高延迟场景下提升巨大。大多数客户端(redis-py、ioredis)提供 pipeline API。注意管道会缓冲所有回复,命令过多会消耗内存——分批管道(如每 1000 条一批)。

redis
# pipelining sends multiple commands in one network round-trip
# (no atomicity guarantee, just reduced latency)

# in a client library (e.g., Node.js / redis):
const pipe = client.multi()
pipe.set("k1", "v1")
pipe.set("k2", "v2")
pipe.incr("counter")
const results = await pipe.exec()

# vs MULTI/EXEC: pipelining does NOT guarantee atomicity
# other clients can interleave between pipelined commands

# raw protocol example (RESP):
# *1\r\n$4\r\nPING\r\n*1\r\n$4\r\nPING\r\n
# (send multiple commands at once, read multiple replies)

Lua 脚本(原子性)

Lua 脚本是 Redis 实现原子多命令操作的真正方式——脚本执行期间服务器阻塞,无其他命令穿插。EVAL 运行脚本,EVALSHA 用缓存哈希运行。redis.call 在错误时中断,redis.pcall 捕获。脚本可访问 KEYS 和 ARGV 参数。比 MULTI/EXEC 更强大:支持条件逻辑、循环和复杂数据处理。缺点是脚本太长会阻塞服务器。Redis 7.0+ 推荐用 Functions 替代。

redis
# Lua scripts run atomically — no other command runs during execution
# EVAL: run a script
EVAL "return redis.call('GET', KEYS[1])" 1 mykey

# 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"

# load a script once, then run by SHA hash (efficient)
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# returns a SHA1 hash
EVALSHA <sha1> 1 mykey

# check if a script is loaded
SCRIPT EXISTS <sha1>

函数(Redis 7.0+)

Functions(7.0+)是脚本的进化版:注册一次、命名调用、持久化到 RDB/AOF。FUNCTION LOAD 注册库,FCALL 调用函数。相比 EVAL 的临时脚本,Functions 更像数据库的存储过程——有命名、可版本管理、重启不丢失。适合需要在多个客户端复用的复杂原子逻辑。库可包含多个函数。FUNCTION DUMP/RESTORE 支持库的备份和迁移。新项目推荐用 Functions 替代 EVAL。

redis
# 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

# get the function's code
FUNCTION DUMP mylib

# delete a library
FUNCTION DELETE mylib

# functions persist across restarts (unlike EVAL scripts)
10

持久化

RDB 快照

RDB 是某一时刻的内存快照,压缩二进制文件。SAVE 同步阻塞生成(生产慎用),BGSAVE 后台 fork 子进程生成。配置 save 规则(如 save 900 1 表示 900 秒内 1 次修改触发)。RDB 文件紧凑、恢复快、适合备份。缺点是两次快照间的数据可能丢失。fork 在大内存实例上可能短暂阻塞(复制页表)。RDB 适合作为灾难恢复的备份手段。

redis
# RDB: point-in-time snapshot of the dataset (compact binary)
# trigger a snapshot manually:
SAVE              # blocks until done (use with care)
BGSAVE            # forks a background process (non-blocking)
LASTSAVE          # Unix timestamp of the last successful save

# 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 (default: dump.rdb in the working dir)
CONFIG GET dbfilename
CONFIG GET dir

# RDB pros: compact, fast restart, great for backups
# RDB cons: potential data loss between snapshots

AOF(追加文件)

AOF 追加每条写命令到日志。fsync 策略:always(最安全最慢)、everysec(默认,最多丢 1 秒)、no(交由 OS)。AOF 比 RDB 更耐久但文件更大、恢复更慢。BGREWRITEAOF 压缩 AOF(重放当前状态生成最小命令集)。redis-check-aof 工具修复损坏的 AOF。AOF 适合对数据丢失零容忍的场景。Redis 7.0+ AOF 使用多部分文件格式(base + 增量)。

redis
# 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

# AOF auto-rewrite thresholds:
#   auto-aof-rewrite-percentage 100
#   auto-aof-rewrite-min-size 64mb

# AOF pros: minimal data loss (1 sec max with everysec)
# AOF cons: larger files, slower load on restart

RDB + AOF 混合

同时开启 RDB 和 AOF:Redis 4.0+ 在 AOF 重写时生成 RDB 格式的 base 文件 + 增量 AOF 命令。恢复时先加载 RDB(快)再重放增量 AOF(补全)。这结合了 RDB 的快速恢复和 AOF 的数据完整性。aof-use-rdb-preamble yes(默认)启用此格式。生产环境推荐此混合模式——兼顾恢复速度和数据安全。需同时配置 save 规则和 appendonly yes。

redis
# use BOTH RDB and AOF for the best of both worlds
# redis.conf:
#   save 900 1          # RDB snapshots
#   appendonly yes      # AOF enabled
#   aof-use-rdb-preamble yes  # RDB header in AOF (faster load)

# on restart, Redis loads AOF (more complete)
# the AOF file can start with an RDB snapshot (faster load)
# followed by incremental AOF commands

# disaster recovery: keep both RDB and AOF backups
# RDB for fast restores, AOF for minimal data loss

# check what's enabled
CONFIG GET save
CONFIG GET appendonly

# monitor persistence operations
INFO persistence

备份与恢复

定期将 RDB/AOF 文件复制到异地(如对象存储)。BACKUP 策略:BGSAVE 生成 RDB 后复制 dump.rdb;或直接复制 AOF 文件。恢复:停止 Redis、替换数据目录文件、启动。注意版本兼容性——降级恢复可能不兼容。测试备份的可恢复性!许多团队备份正常却从未验证恢复。用 redis-check-rdb/aof 验证文件完整性。跨可用区/region 的异地备份应对区域性故障。

redis
# backup: copy the RDB file while Redis runs
cp /var/lib/redis/dump.rdb /backup/dump-$(date +%F).rdb

# or trigger a BGSAVE first for a consistent snapshot
BGSAVE
# wait for LASTSAVE to update, then copy the file

# AOF backup: copy the AOF manifest + files
cp /var/lib/redis/appendonly.aof.* /backup/

# restore: stop Redis, replace the data files, start
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)
redis-check-rdb /var/lib/redis/dump.rdb
redis-check-aof /var/lib/redis/appendonly.aof

持久化权衡

纯 RDB:恢复快、文件小、可能丢数据(两次快照间)。纯 AOF:数据全、文件大、恢复慢。混合(推荐):两者兼得。纯缓存场景可关闭持久化(save '' + appendonly no)换取性能。fork 在大内存实例上昂贵——考虑用 replica 持久化减轻主节点压力。持久化是性能与数据安全的权衡——按业务对数据丢失的容忍度选择。监控 BGSAVE/BGREWRITEAOF 的耗时和 fork 延迟。

redis
# choosing a persistence strategy:

# 1. RDB only (cache, re-buildable data)
#    save 3600 1
#    appendonly no
#    -> fast restart, some data loss on crash

# 2. AOF only (durability-critical, everysec)
#    save ""
#    appendonly yes
#    appendfsync everysec
#    -> up to 1s data loss, larger files

# 3. RDB + AOF (recommended production default)
#    save 3600 1
#    appendonly yes
#    aof-use-rdb-preamble yes
#    -> durability + fast restart

# 4. No persistence (pure cache, ephemeral)
#    save ""
#    appendonly no
#    -> maximum speed, all data lost on restart

# 5. Replica-only persistence (offload from primary)
#    primary: no persistence (max performance)
#    replica: RDB + AOF (persist here instead)
11

集群

Redis 集群基础

Redis Cluster 自动分片到多节点,用 16384 个哈希槽。CRC16(key) % 16384 决定槽位。每个主节点负责一部分槽。CLUSTER INFO 看状态,CLUSTER NODES 看节点。至少 3 主 3 从(6 节点)才能容忍单节点故障。集群提供高可用和水平扩展,但增加运维复杂度。客户端需支持集群协议(MOVED/ASK 重定向)。用 redis-cli --cluster 创建和管理集群。

redis
# Redis Cluster shards data across multiple nodes (16,384 slots)
# each key maps to a slot: CRC16(key) % 16384

# 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 status
redis-cli -p 7000 CLUSTER INFO
redis-cli -p 7000 CLUSTER NODES

# count slots assigned
CLUSTER COUNTKEYSINSLOT 1234

# get the slot for a key
CLUSTER KEYSLOT mykey   # e.g., 1234

# multi-key operations require keys in the SAME slot
# 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 {})

哈希标签与多键操作

哈希标签 {} 让键的哈希只计算大括号内部分——user:{100}:profile 和 user:{100}:cart 落同一槽位。这样多键命令(MGET、事务、Lua 脚本)才能在集群中运行。设计键名时考虑哪些键需要同槽——如某用户的所有数据用 {user:100} 标签。注意标签内是字面匹配——设计不当会导致热点(某大用户的所有数据挤在一个节点)。权衡局部性和负载均衡。

redis
# hash tags: the substring inside {} determines the slot
# {user:1}:profile and {user:1}:cart share a slot
# enabling multi-key operations:

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)
# - too narrow -> can't do multi-key ops

集群重新分片

redis-cli --cluster reshard 在线迁移槽位。逐个槽位地从源节点 MIGRATE 到目标节点。过程中集群正常服务,迁移中的槽位对客户端返回 ASK 重定向。reshard 后用 --cluster check 验证。添加新节点:--cluster add-node,再 reshard 分配槽位。删除节点:先 reshard 移走其槽位再 --cluster del-node。在线运维但避开高峰期,迁移大键可能卡住。

redis
# move slots from one node to another (resharding)
redis-cli --cluster reshard host1:7000
# prompts for: how many slots, target node, source nodes

# add a new node to the cluster
redis-cli --cluster add-node newhost:7000 host1:7000

# add a new replica to an existing primary
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>

# rebalance slots across all nodes
redis-cli --cluster rebalance host1:7000

# check cluster health
redis-cli --cluster check host1:7000
redis-cli --cluster fix host1:7000   # repair issues

集群故障转移

主节点故障时,其从节点发起选举(gossip 协议),获多数票后晋升为新主。CLUSTER FAILOVER 在从节点上手动触发故障转移(维护时有用)。TAKEOVER 不需多数票(紧急情况)。集群半数以上主节点同时故障会停止服务(cluster_require_full_coverage yes 时)。网络分区的少数派不可写。监控集群状态——CLUSTER INFO 的 cluster_state 应为 ok。

redis
# when a primary fails, its replica is promoted automatically
# manual failover (for maintenance):
redis-cli -p 7001 CLUSTER FAILOVER         # replica -> primary
redis-cli -p 7001 CLUSTER FAILOVER FORCE   # without primary agreement
redis-cli -p 7001 CLUSTER FAILOVER TAKEOVER # force, may split brain

# view the cluster topology
CLUSTER NODES
CLUSTER SHARDS    # (7.0+)

# a node flagged as FAIL after timeout is removed
# cluster- node-timeout 15000   (15 seconds in redis.conf)

# if a cluster has no replica for a failing primary,
# that slot range becomes unavailable until the primary returns
# (or you manually fix it)

# CLUSTER RESET resets a node (HARD removes all data)

集群 vs Sentinel vs 单机

单机:简单、无高可用、无扩展。Sentinel:主从+哨兵监控自动故障转移,适合数据量不大但需高可用。Cluster:分片+高可用,适合数据量超单机内存或高吞吐。选择:数据 < 单机内存且无分片需求用 Sentinel;需水平扩展用 Cluster。Cluster 有一些限制(多键操作需同槽、事务受限、SELECT 只能用 0)。客户端需集群感知。运维复杂度 Cluster > Sentinel > 单机。

redis
# Single Redis: one instance, no HA, no scaling
#    simplest, fine for dev/small cache

# Sentinel: 1 primary + replicas + sentinel monitors
#    HA (automatic failover), no sharding
#    good when dataset fits on one machine
#    sentinel.conf: sentinel monitor mymaster host 6379 2

# Cluster: sharded across N primaries + replicas
#    HA + horizontal scaling
#    good for large datasets / high throughput
#    more complex (multi-key limitations)

# choose:
# - dev / tiny cache              -> single
# - production, data fits in RAM  -> sentinel (HA)
# - data > single machine RAM     -> cluster (sharding)
# - very high write throughput    -> cluster (shard writes)

# managed Redis (Redis Cloud, ElastiCache) handles
# sentinel/cluster ops for you
12

Sentinel(高可用)

Sentinel 设置

Sentinel 是独立进程监控主从集群,主故障时自动提升从节点。至少 3 个 Sentinel(奇数,避免脑裂)。配置 sentinel monitor mymaster <ip> <port> <quorum>,quorum 是同意故障的 Sentinel 数。Sentinel 间互相发现并交换主从状态。客户端连接 Sentinel 而非直接连 Redis——Sentinel 告知当前主地址。故障转移后客户端自动重连新主。

redis
# 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 命令

SENTINEL masters 列出所有监控的主。SENTINEL master <name> 看详情。SENTINEL replicas <name> 列从节点。SENTINEL sentinels <name> 列其他 Sentinel。SENTINEL get-master-addr-by-name <name> 获取当前主地址(客户端用此发现主)。SENTINEL failover <name> 手动触发故障转移。SENTINEL reset <pattern> 重置状态。SENTINEL ckquorum 检查是否有足够 Sentinel 做故障转移。

redis
# query sentinel state
redis-cli -p 26379 SENTINEL masters          # all monitored primaries
redis-cli -p 26379 SENTINEL master mymaster  # details of one primary
redis-cli -p 26379 SENTINEL replicas mymaster
redis-cli -p 26379 SENTINEL sentinels mymaster

# find the current primary address
SENTINEL get-master-addr-by-name mymaster

# force a failover (manual, for maintenance)
SENTINEL failover mymaster

# reset a master's state (clears replicas/sentinels discovered)
SENTINEL reset mymaster

# check the number of OK sentinels
SENTINEL ckquorum mymaster
# "OK 3 usable Sentinels" or "NOQUORUM" warning

# clients should connect to sentinels, NOT directly to the primary
# libraries: redis-py, jedis, ioredis all support sentinel mode

故障转移过程

故障转移:Sentinel 们通过心跳发现主不可达(down-after-milliseconds 后标记主观下线 SDOWN)。足够数量(quorum)同意后标记客观下线 ODOWN。选举 Sentinel leader 执行转移:选最优从节点(优先级、偏移量、runid),让其 SLAVEOF NO ONE 升主,通知其他从节点复制新主,更新配置。客户端重连时从 Sentinel 获取新主地址。整个过程自动完成,通常数秒到数十秒。

redis
# when a primary is down longer than down-after-milliseconds:
# 1. sentinels mark it 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-*

读副本与扩展

从节点可承担读请求扩展读吞吐。注意复制是异步的——从节点数据可能滞后,强一致读必须读主。READONLY 命令让从节点接受读请求。读写分离要容忍最终一致性。从节点不参与写——所有写仍走主节点。写扩展需用集群分片而非单纯加从节点。从节点也用于备份(在其上 BGSAVE 不影响主)和分析查询。监控从节点的复制偏移量判断延迟。

redis
# replicas can serve reads (eventually consistent)
REPLICAOF 192.168.1.10 6379    # become a replica of this primary
REPLICAOF NO ONE               # promote to primary (manual)

# check replication status
INFO replication
# role:slave, master_host, master_link_status:up, ...

# replica lag (bytes and seconds lagging behind primary)
# master_repl_offset vs slave_repl_offset

# clients can route reads to replicas for read scaling
# but beware: replica data may be stale (replication lag)

# Redis Sentinel + clients: configure read preference
#   READONLY command enables reads on a replica

# writable replicas (Redis 4.0+): accept writes that are
# local-only (not replicated) — useful for ephemeral data
#   replica-read-only no   (in redis.conf)

复制内部机制

复制分全量和增量。全量:从节点首次连接或断开过久时,主节点 BGSAVE 生成 RDB 发给从节点,期间新写命令缓存在复制积压缓冲区,RDB 发完再发缓冲命令。增量:从节点用 replid 和 offset 断点续传——断线重连时若 offset 在积压缓冲区内则只补差异,否则全量。PSYNC 命令协商。积压缓冲区大小(repl-backlog-size)决定断线容忍时长。监控复制延迟和主从链接状态。

redis
# replication is asynchronous: primary -> replica (one-way)
# initial sync: full RDB transfer + buffered commands
# subsequent: streaming command log

# trigger a full resync (force replica to reload everything)
redis-cli -p 6380 REPLICAOF NO ONE
redis-cli -p 6380 REPLICAOF 192.168.1.10 6379

# partial resync (PSYNC): if the replica disconnects briefly,
# it can resume from the replication backlog (no full resync)
#   repl-backlog-size 1mb    (tune for longer disconnects)

# monitor replication in real-time
redis-cli -p 6379 INFO replication
# master_repl_offset, slave_read_repl_offset, backlog_size

# chain replication: a replica of a replica (tree topology)
# reduces load on the primary for many replicas
REPLICAOF replica-host 6380   # chain off another replica

# replication ID changes on every primary restart -> full resync
13

Streams(流)

Stream 基础(XADD/XREAD)

Streams 是 Redis 5.0+ 的日志数据结构,类似 Kafka 的分区日志。XADD 追加条目(自动生成时间戳 ID),XREAD 读取(可阻塞 XREAD BLOCK)。XLEN 返回长度。XRANGE/XREVRANGE 按范围读。MAXLEN 截断保留最近 N 条(用 ~ 近似截断更高效)。Streams 天生持久化、有序、可回放——比发布/订阅可靠得多。适合事件溯源、活动日志、任务队列。

redis
# streams are append-only logs with IDs (durable, replayable)
# XADD: add an entry (auto-generate ID with *)
XADD mystream * sensor temp 25.5
# returns an ID like "1700000000000-0" (timestamp-sequence)

# add with a specific ID (must be greater than the last)
XADD mystream 1700000001000-0 sensor temp 26.0

# read entries
XREAD COUNT 10 STREAMS mystream 0        # from the beginning
XREAD COUNT 10 STREAMS mystream $        # new entries only (tail)

# block for new entries (like BLPOP for lists)
XREAD BLOCK 30000 STREAMS mystream $     # blocks up to 30s

# get the length
XLEN mystream                            # number of entries

# capped stream (keep only last N entries)
XADD mystream MAXLEN 1000 * field value
XADD mystream MAXLEN ~ 1000 * field value  # ~ = approx (faster)

消费者组

消费者组让多个消费者公平分配 Streams 消息。XGROUP CREATE 创建组。XREADGROUP 读消息(> 表示新消息,ID 表示指定)。XACK 确认处理完成——未确认的消息留在待处理列表(PEL)。XPENDING 查看待处理消息,XCLAIM 转移给其他消费者(崩溃恢复)。消费者组保证每条消息只被组内一个消费者处理,且可回溯。这是构建可靠任务队列的基础。

redis
# create a consumer group at a specific position
XGROUP CREATE mystream mygroup $        # only new entries
XGROUP CREATE mystream mygroup 0        # all existing entries
XGROUP CREATE mystream mygroup 0 MKSTREAM  # create stream if missing

# read entries as a consumer (assigns pending entries)
XREADGROUP GROUP mygroup consumer1 COUNT 10 STREAMS mystream >
# '>' = never-delivered entries

# read pending entries (for this consumer, not yet acked)
XREADGROUP GROUP mygroup consumer1 STREAMS mystream 0

# acknowledge processing (removes from pending)
XACK mystream mygroup <id1> <id2>

# view pending entries (pending entries list)
XPENDING mystream mygroup
# returns: count, min-id, max-id, consumers

# view a consumer's pending entries
XPENDING mystream mygroup - + 10 consumer1

流处理与恢复

消费者崩溃后其未确认消息停留。XPENDING 列出待处理消息及空闲时间。XCLAIM 将超时消息转给其他消费者。XAUTOCLAIM(6.2+)自动认领超时消息。XINFO STREAM/GROUPS/CONSUMERS 查看 Streams 和消费者状态。设计消费者组时设置合理的 min-idle-time(多久未确认视为崩溃)。监控待处理消息数——堆积说明消费速度跟不上生产。

redis
# XCLAIM: reassign a pending entry to another consumer
# (after the original consumer died or is too slow)
XCLAIM mystream mygroup consumer2 60000 <entry-id>
# 60000 = min idle time (ms) before claiming

# XAUTOCLAIM (6.2+): auto-claim stale pending entries
XAUTOCLAIM mystream mygroup consumer2 60000 0 COUNT 10
# claims entries idle > 60s, returns them to consumer2

# view stream info
XINFO STREAM mystream FULL
XINFO GROUPS mystream
XINFO CONSUMERS mystream mygroup

# delete an entry (rarely needed)
XDEL mystream <id>

# trim the stream (free memory)
XTRIM mystream MAXLEN 10000
XTRIM mystream MINID 1700000000000   # remove older than this ID

# range queries
XRANGE mystream - +                   # all entries
XRANGE mystream 1700000000000 - 1700000009999 -

Stream 用例

事件溯源:所有状态变更追加到 Streams,可重放重建状态。活动日志:用户操作按时间记录。任务队列:消费者组提供可靠分发和确认。数据变更捕获(CDC):Streams 模拟数据库 binlog。指标收集:时序数据写入 Streams 定期聚合。消息回放:新消费者从任意位置回放历史。Streams 的持久化和消费者组使其成为 Redis 中最完整的消息传递方案。

redis
# 1. Reliable task queue (replaces lists + BRPOPLPUSH)
XADD tasks * type email payload '{"to":"[email protected]"}'
XREADGROUP GROUP workers worker1 COUNT 1 STREAMS tasks >
# process and XACK on success

# 2. Event sourcing / audit log
XADD events * user 1 action login ip 1.2.3.4
# replay with XRANGE for reconstruction

# 3. Real-time analytics (time-windowed)
XADD metrics * cpu 75 mem 60
XRANGE metrics - +           # query by time range

# 4. Chat history (capped)
XADD chat:room:1 MAXLEN ~ 1000 * user alice msg "hello"
XREAD COUNT 20 STREAMS chat:room:1 0

# 5. IoT sensor data (with TTL via trimming)
XADD sensors:temp MAXLEN ~ 86400 * value 22.5  # keep last day

Streams 与其他队列对比

Streams vs 列表:Streams 持久化、有消费者组、可回放;列表简单但无确认机制。Streams vs 发布/订阅:Streams 持久化、有消费者组、可回放;发布/订阅即时无持久。Streams vs Kafka:Streams 轻量、内嵌 Redis、无独立运维;Kafka 更强大、更高吞吐、更复杂。Redis 内的消息队列选 Streams;跨系统的大规模流处理用 Kafka。Streams 适合单 Redis 内的可靠消息传递。

redis
# Redis Streams vs Lists (for queues):
# Lists: LPUSH/BRPOP, simple, NO retry/recovery
# Streams: consumer groups, ACK, pending, XAUTOCLAIM

# Redis Streams vs Kafka:
# Streams: in-memory (RAM-bounded), simpler, single-node or cluster
# Kafka: disk-based, partitioned, higher throughput at scale

# Redis Streams vs RabbitMQ:
# Streams: simpler, fewer features, in-memory
# RabbitMQ: rich routing, exchanges, acknowledgments, disk

# Streams capacity: limited by RAM (use MAXLEN/MINID to trim)
# Streams persistence: RDB/AOF (like any Redis data)

# choose Streams when:
# - you need a queue but already use Redis
# - dataset fits in RAM
# - you want simplicity over Kafka's scale
# - you need consumer groups with retry
14

HyperLogLog、位图与地理空间

HyperLogLog(基数统计)

HyperLogLog 用极少内存(固定约 12KB)估算集合基数(不重复元素数),误差约 0.81%。PFADD 添加元素,PFCOUNT 估算基数,PFMERGE 合并多个 HLL。适合统计独立访客数、独立搜索数等海量去重场景——用 SET 存上亿元素不现实,HLL 仅 12KB。注意是估算非精确,且不存储元素本身(无法查询某元素是否存在)。对精度要求极高时用 SET 或位图。

redis
# HyperLogLog: approximate unique count with fixed 12KB memory
# perfect for counting unique visitors, IPs, etc. at scale

# add elements (returns 1 if the cardinality changed)
PFADD visitors:2025-01-01 "user:1" "user:2" "user:3"

# get the approximate unique count
PFCOUNT visitors:2025-01-01        # ~3

# merge multiple HLLs (e.g., monthly uniques from daily)
PFMERGE visitors:2025-01 visitors:2025-01-01 visitors:2025-01-02

# accuracy: ~0.81% standard error
# memory: always ~12KB regardless of element count (up to 2^64)

# vs a Set for unique counting:
# SADD + SCARD: exact, but O(N) memory (grows with count)
# PFADD + PFCOUNT: approximate, fixed 12KB memory
# 1 million uniques: Set ~10MB, HLL always 12KB

位图深入

位图基于字符串实现。SETBIT/GETBIT 设/取位,BITCOUNT 数 1,BITOP 做位运算。典型场景:用户每日活跃(每天一键,用户 ID 为位偏移)、布隆过滤器、特征开关。1 亿用户每日活跃仅约 12MB。BITFIELD 支持多字节字段。位图适合密集且元素有自然整数 ID 的场景。对稀疏数据(用户 ID 跨度大但实际少)用 HLL 或 SET 更省内存。位图操作是原子的。

redis
# bitmaps: boolean operations on a string's bits
# ideal for per-user flags over a large user base (user ID = bit position)

# mark user 7 as "active today"
SETBIT active:2025-01-01 7 1

# check if user 7 was active
GETBIT active:2025-01-01 7          # 1

# count active users
BITCOUNT active:2025-01-01

# users active on BOTH days (AND)
BITOP AND active:both active:2025-01-01 active:2025-01-02
BITCOUNT active:both

# users active on EITHER day (OR)
BITOP OR active:either active:2025-01-01 active:2025-01-02

# users active on day 1 but NOT day 2 (XOR or AND NOT)
BITOP XOR diff active:2025-01-01 active:2025-01-02

# find the first active user
BITPOS active:2025-01-01 1         # position of first 1-bit

# memory: 1 million users = 125KB (1 bit per user)

地理空间(GEO)

GEO 基于有序集合实现,存储经纬度并支持距离和范围查询。GEOADD 添加成员及坐标,GEOPOS 取坐标,GEODIST 算距离,GEORADIUSBYMEMBER/GEORADIUS 找附近成员(6.2+ 用 GEOSEARCH/GEOSEARCHSTORE)。坐标用 Geohash 编码为有序集合分数,支持高效的范围查询。适合找附近的人/店/车。精度在厘米级,对大多数应用足够。每个键最多 2^32-1 个成员。

redis
# GEO: built on sorted sets, stores (lng, lat, name) points
# add locations (longitude, latitude, name)
GEOADD places -73.99 40.73 "Park" -73.98 40.75 "Museum" -74.00 40.70 "Cafe"

# get coordinates of a member
GEOPOS places "Park"               # [["-73.99","40.73"]]

# distance between two members (meters by default)
GEODIST places "Park" "Museum"     # "1623.5"
GEODIST places "Park" "Museum" km  # "1.623"

# find members within a radius (meters)
GEOSEARCH places FROMMEMBER "Park" BYRADIUS 1000 m ASC
# members within 1km of Park, sorted by distance

# find members within a bounding box
GEOSEARCH places FROMLONLAT -73.99 40.73 BYBOX 2 2 km

# GEOSEARCH also returns distance and coordinates
GEOSEARCH places FROMMEMBER "Park" BYRADIUS 500 m WITHCOORD WITHDIST

地理空间操作

GEOSEARCH(6.2+)是现代地理查询命令:FROMMEMBER 或 FROMLONLAT 指定中心,BYRADIUS 或 BYBOX 指定范围,可带 ASC/DESC 排序、COUNT 限制、WITHCOORD/WITHDIST/WITHHASH 返回额外信息。典型用例:找 5 公里内的咖啡店按距离升序返回前 20 个。单位支持 m/km/mi/ft。GEOSEARCHSTORE 将结果存入新有序集合,便于后续分页或缓存。

redis
# add multiple points at once
GEOADD cities -122.41 37.78 "San Francisco" -74.00 40.71 "New York"

# get the geohash of a member
GEOHASH places "Park"              # ["dr5reg..."]

# remove a member (uses ZREM internally)
ZREM places "Cafe"

# count members in an area (using ZCOUNT on the underlying zset)
ZCOUNT places -180 180             # all members

# GEOSEARCH with a center point and limiting results
GEOSEARCH places FROMLONLAT -73.99 40.73 \
  BYRADIUS 2000 m ASC COUNT 5      # nearest 5 within 2km

# GEOSEARCHSTORE: store results in a new key (6.2+)
GEOSEARCHSTORE nearby FROMLONLAT -73.99 40.73 BYRADIUS 1000 m

# the underlying structure is a sorted set, so:
ZRANGE places 0 -1                 # list all members
ZSCORE places "Park"               # geohash score

选择专用类型

特殊类型按场景选:基数估算(不重复数)用 HLL(12KB 固定,海量数据);密集布尔状态(如每日活跃)用位图;地理范围查询用 GEO;时序数据用 Streams 或 Timeseries 模块。不要滥用——简单场景用字符串/哈希/集合即可。特殊类型有取舍:HLL 不精确不存元素;位图要元素有整数 ID;GEO 精度有限。理解取舍后才能选对类型,避免过度设计或欠设计。

redis
# Cardinality counting (unique elements):
#   exact, need membership test  -> Set (SADD/SISMEMBER/SCARD)
#   approximate, huge scale      -> HyperLogLog (PFADD/PFCOUNT)
#   per-time-bucket uniques      -> HLL per day, PFMERGE for ranges

# Boolean flags over a dense ID space:
#   small number of flags        -> Set of active IDs
#   millions of users            -> Bitmap (SETBIT/BITCOUNT)
#   multiple counters compactly  -> BITFIELD

# Geographic points:
#   "find nearby" queries        -> GEO (GEOADD/GEOSEARCH)
#   complex GIS                  -> use PostGIS/external

# Time-series data:
#   simple, capped               -> Stream (XADD MAXLEN)
#   dedicated TS module          -> RedisTimeSeries (module)

# Probabilistic structures (modules):
#   Bloom filter                 -> RedisBloom (BF.ADD/BF.EXISTS)
#   Top-K                        -> RedisBloom (TOPK)
15

安全

认证与 ACL

Redis 6+ ACL 提供细粒度权限控制。CONFIG SET requirepass 设全局密码(旧式,所有命令权限)。ACL SETUSER 创建用户并限制命令和键:on/off 启用停用,>password 设密码,~keypattern 限制键,+cmd/-cmd 限制命令。ACL WHOAMI 看当前用户,ACL LIST 列所有用户。生产环境应为不同应用创建最小权限用户——如只读用户、只能访问特定前缀的用户。默认 user 应禁用或限制。

redis
# set a password (simple, legacy)
CONFIG SET requirepass "strongpassword"
AUTH "strongpassword"

# Redis 6+: ACL system with users and permissions
# add a user with limited access
ACL SETUSER appuser on >secretpassword ~app:* +get +set +del -@dangerous

# list users
ACL LIST
ACL WHOAMI                # current user

# view a user's permissions
ACL GETUSER appuser

# delete a user
ACL DELUSER appuser

# save ACLs to a file (persists across restarts)
ACL SAVE
ACL LOAD                  # reload from file

# aclfile in redis.conf:
#   aclfile /etc/redis/users.acl

TLS/SSL 加密

Redis 6+ 原生支持 TLS。编译时启用 BUILD_TLS=yes,配置 tls-port、tls-cert-file、tls-key-file、tls-ca-cert-file。客户端用 rediss:// 协议连接。TLS 加密传输防窃听和中间人。注意 TLS 增加约 10-15% CPU 开销和连接建立延迟——用连接池缓解。内部网络可不用 TLS 依赖网络隔离,跨网络必用。证书定期轮换。Sentinel 和集群节点间也可启用 TLS。

redis
# enable TLS in redis.conf (Redis 6+)
# generate certs first (or use a real CA):
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-cluster yes              # TLS between cluster nodes
tls-replication yes          # TLS between primary/replica
tls-auth-clients yes         # require client certs

# mix TLS and plain text (transition period)
# port 6380
# tls-port 6379

网络安全

绝不要将 Redis 暴露到公网——Redis 设计上信任网络,无内置防护。用防火墙/安全组限制仅应用服务器可访问。bind 配置只监听内网地址。改默认端口(虽非真正安全但减少扫描噪音)。用 VPN 或 SSH 隧道远程管理。Redis 6+ 的 ACL 和 TLS 增加了安全层,但网络隔离仍是第一道防线。云上用私有网络/VPC,禁止公网 IP。定期审计访问日志和客户端连接。

redis
# bind to specific interfaces (never 0.0.0.0 in prod)
bind 127.0.0.1 10.0.0.5
protected-mode yes            # blocks external access if no password

# require authentication
requirepass "strong-random-password"

# rename dangerous commands (or disable them)
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG "CONFIG_9f8a7b"
rename-command KEYS ""

# Redis 6+: better to use ACLs instead of rename-command
# ACL SETUSER default -flushall -config -keys

# disable debug commands
# ACL SETUSER default -@dangerous

# use a firewall / security group to restrict access
# Redis should NEVER be directly internet-accessible
# (the 2018+ ransom attacks scanned for open Redis instances)

审计与日志

CONFIG SET loglevel debug/verbose/notice/warning 控制日志级别。logfile 配置日志文件(默认 stdout)。SLOWLOG 记录慢命令。MONITOR 实时监控所有命令(仅调试,生产严重影响性能)。ACL LOG(6+)记录 ACL 相关事件。LATENCY 框架记录延迟事件。将这些日志聚合到集中系统(如 ELK)便于审计和告警。注意日志中可能含敏感数据——脱敏和访问控制。

redis
# log slow commands (slowlog)
CONFIG SET slowlog-log-slower-than 10000   # 10ms in microseconds
CONFIG SET slowlog-max-len 128             # keep 128 entries
SLOWLOG GET 10                             # last 10 slow commands
SLOWLOG RESET

# log to a file (redis.conf)
logfile /var/log/redis/redis.log
loglevel notice    # debug|verbose|notice|warning

# monitor all commands (debugging only — huge perf impact)
MONITOR

# latency monitoring
CONFIG SET latency-monitor-threshold 100   # 100ms
LATENCY HISTORY event-name
LATENCY DOCTOR                              # analysis report

# client list (who's connected)
CLIENT LIST
CLIENT GETNAME
CLIENT SETNAME "my-app"

安全最佳实践

1. 不暴露公网,用网络隔离。2. 启用 ACL,最小权限原则。3. 用 TLS 加密跨网络传输。4. 禁用危险命令(FLUSHALL、CONFIG、KEYS)或限制仅管理员。5. 定期更新 Redis 版本修复漏洞。6. 审计日志集中管理。7. 备份加密存储。8. 敏感数据存 Redis 前应用层加密(Redis 不加密存储)。9. 监控异常连接和命令模式。10. 制定安全事件响应流程。

redis
# 1. NEVER expose Redis to the public internet
bind 127.0.0.1
protected-mode yes

# 2. Use strong passwords or ACLs
requirepass "<64-char-random-string>"
# or ACLs with least privilege

# 3. Enable TLS for transit encryption
tls-port 6379

# 4. Restrict dangerous commands
ACL SETUSER default -@dangerous -flushall -keys

# 5. Use a firewall / security group
# allow only app servers on the Redis port

# 6. Rotate credentials regularly
ACL SETUSER appuser on >newpassword ~app:* +@read +@write

# 7. Audit access
LOGICALDB MONITOR  # or use an audit module

# 8. Encrypt sensitive data at rest (disk encryption)
# RDB/AOF files can contain sensitive data

# 9. Keep Redis updated (security patches)
# 10. Isolate environments (separate dev/staging/prod)
16

性能与优化

性能基础

Redis 单线程命令处理(6+ 用多线程仅做 I/O,命令仍串行),避免慢命令阻塞。KEYS、SMEMBERS 大集合、SORT 大集合、LRANGE 全列表都是隐患——用 SCAN、SSCAN、有序集合替代。watchdog 是 O(1) 但频繁调用累积开销。单个命令应 < 1ms。监控 instantaneous_ops_per_sec 和延迟。Redis 在内存中操作极快——瓶颈通常在网络、慢命令或内存不足时的驱逐。

redis
# Redis is single-threaded (mostly) — avoid blocking commands
# DANGEROUS on large datasets (block the server):
KEYS *              # use SCAN instead
SMEMBERS bigset     # use SSCAN
HGETALL bighash     # use HSCAN
DEL hugekey         # use UNLINK (async delete)
FLUSHDB             # use FLUSHDB ASYNC

# use UNLINK instead of DEL for large keys (non-blocking)
UNLINK bigkey       # deletes in a background thread

# favor pipelining and batch commands
MGET k1 k2 k3       # one round-trip, not three GETs
LPUSH list a b c    # one command, not three

# prefer structurally efficient commands
# O(1): GET, SET, HGET, SISMEMBER, ZSCORE
# O(log N): ZADD, ZRANGE
# O(N): LRANGE, SMEMBERS, KEYS, SORT

管道与批量操作

管道将多条命令一次发送减少往返。MGET/MSET 批量读写。SUNIONSTORE 等批量运算。批量操作可提升 10-100 倍吞吐(尤其在跨地域)。注意:管道缓冲所有回复,命令过多耗内存——分批(如每 1000 条)。Lua 脚本将多操作打包成一次往返且原子。用 pipeline API 时检查是否支持自动分批。基准测试不同批量大小找到最优——通常 100-1000 条/批。

redis
# pipelining: send many commands without waiting for replies
# (reduces round-trip time — huge speedup over network)

# example (Node.js with ioredis):
const pipeline = redis.pipeline()
for (let i = 0; i < 1000; i++) {
  pipeline.set(`key:${i}`, "value")
}
await pipeline.exec()        # one round-trip for 1000 commands

# transactions (MULTI/EXEC) are automatically pipelined
redis.multi().set("k", "v").incr("c").exec()

# MSET/MGET batch in one command
MSET k1 v1 k2 v2 k3 v3
MGET k1 k2 k3

# benchmark pipelining
redis-benchmark -t set -n 100000 -P 16   # -P = pipeline size

# don't pipeline too many at once (memory for queued replies)
# batch in groups of 100-1000

内存优化

选对类型省内存:小哈希/集合/有序集合用 listpack 编码(调整 *-max-listpack-size 和 entries 阈值)。用哈希存对象字段而非多个字符串键(每个键有元数据开销)。短键名省内存但牺牲可读性——权衡。ziplist/listpack 在元素少时极省内存。BITFIELD 用一位存布尔。HLL 12KB 估算亿级基数。监控 used_memory 和 used_memory_rss,碎片率 >1.5 时考虑 activedefrag。

redis
# use the right data structure (memory vs speed trade-offs)
# store small objects as hashes (listpack encoding is compact)
HSET user:1 name "Alice" age 30   # better than 3 string keys

# tune encoding thresholds (redis.conf)
hash-max-listpack-entries 128     # hash -> hashtable above this
hash-max-listpack-value 64        # field value size limit
list-max-listpack-size -2         # listpack max size
set-max-intset-entries 512        # intset -> hashtable above this
zset-max-listpack-entries 128

# use hashes for many small keys (key bucketing)
# instead of SET user:1:clicks 5; SET user:2:clicks 3
# use HSET clicks user:1 5 user:2 3

# monitor memory
INFO memory | grep used_memory_human
MEMORY STATS

# set maxmemory and an eviction policy
maxmemory 4gb
maxmemory-policy allkeys-lru

驱逐策略

maxmemory 设上限,maxmemory-policy 决定满时行为:noeviction(拒绝写,默认)、allkeys-lru/volatile-lru(最近最少使用)、allkeys-lfu/volatile-lfu(最不常用,4.0+)、allkeys-random/volatile-random(随机)、volatile-ttl(最短 TTL)。LRU 近似(采样),LFU 用计数器。缓存场景用 allkeys-lru/lfu;数据库场景用 noeviction 或 volatile-*。监控 evicted_keys 调整策略和容量。

redis
# when maxmemory is reached, Redis evicts keys per the policy
# maxmemory-policy options:

# no eviction (writes fail with OOM error)
maxmemory-policy noeviction

# LRU (Least Recently Used) — approximate LRU
allkeys-lru         # evict any key (cache use case)
volatile-lru        # only evict keys with TTL set

# LFU (Least Frequently Used) — better for skewed access
allkeys-lfu
volatile-lfu

# random eviction
allkeys-random
volatile-random

# TTL-based (evict soonest-expiring)
volatile-ttl

# for a cache: allkeys-lru or allkeys-lfu (best hit rate)
# for a database: noeviction (never lose data silently)
# mixed (some persistent, some cache): volatile-lru

基准测试

redis-benchmark 是内置压测工具。-c 并发数、-n 请求数、-d 数据大小、-t 测试命令、-P 管道大小、--cluster 集群模式。先基线测试再调优对比。注意生产环境压测会影响服务——用独立实例或 replica。关注 p99/p999 延迟而非仅平均值——长尾延迟影响用户体验。瓶颈通常在网络(带宽/延迟)、CPU(慢命令)、内存(驱逐)。瓶颈定位后针对性优化,避免盲目调参。

redis
# redis-benchmark: built-in performance testing
redis-benchmark -t set,get -n 100000 -c 50
# -t: test commands, -n: number of requests, -c: concurrent clients

# test with pipelining
redis-benchmark -t set -n 100000 -c 50 -P 16

# test a specific command with a specific key
redis-benchmark -t get -n 100000 -r 100000 -q
# -r: random keys (INSERT random keys for testing)

# latency check (100 requests, shows distribution)
redis-cli --latency
redis-cli --latency-history    # rolling update
redis-cli --latency-dist       # histogram

# in-memory test (no network overhead)
redis-benchmark -t set -n 100000 -q --threads 4

# monitor while benchmarking
redis-cli INFO stats | grep instantaneous_ops_per_sec
17

管理

服务器信息与监控

INFO 是最重要的监控命令——返回服务器、客户端、内存、持久化、统计、复制等各部分信息。MEMORY STATS 和 MEMORY DOCTOR 提供详细内存诊断。LATENCY DOCTOR 分析延迟事件。CLI 工具 --stat 持续监控关键指标,--latency 测往返延迟。生产环境用 Prometheus + redis_exporter 或 Redis Insight 做长期监控和可视化。关注内存使用、连接数、ops/sec、命中率、复制延迟。

redis
# 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 keyspace         # keys per database

# real-time stats
LATENCY DOCTOR        # latency analysis
MEMORY DOCTOR         # memory analysis

# live monitoring (subscribe to a special channel)
SUBSCRIBE __redis__:invalidate

# slow query log
SLOWLOG GET 10
SLOWLOG LEN

# client connections
CLIENT LIST
CLIENT KILL ID <id>   # disconnect a client

客户端管理

CLIENT LIST 列所有连接及状态(地址、空闲时间、当前命令、DB 等)。CLIENT KILL 终止连接(按 ID 或地址)。CLIENT SETNAME 给连接命名便于识别。CLIENT PAUSE 暂停所有客户端(故障注入测试)。CLIENT NO-EVICT 让某连接的写不触发驱逐。监控 connected_clients 和阻塞客户端数。连接泄漏(应用不释放连接)会耗尽 maxclients——用连接池管理。CLIENT INFO 和 CLIENT GETNAME 查当前连接信息。

redis
# list all connected clients
CLIENT LIST
# id, addr, name, age, idle, flags, db, cmd

# name your connections (for identification)
CLIENT SETNAME "my-app-worker-1"

# get the current client's info
CLIENT INFO

# get the current client's ID
CLIENT ID

# kill a client
CLIENT KILL ADDR 1.2.3.4:12345
CLIENT KILL ID 42
CLIENT KILL TYPE normal    # kill all normal clients

# pause clients (for maintenance, 10 seconds)
CLIENT PAUSE 10000

# set a timeout for idle clients
CONFIG SET timeout 300    # 5 minutes

# max clients
CONFIG SET maxclients 10000

配置

CONFIG GET 查配置,CONFIG SET 改运行时配置(部分需重启才生效)。CONFIG REWRITE 将运行时修改写回配置文件。建议用配置文件管理(版本控制),CONFIG SET 仅用于临时调整。关键配置:maxmemory、maxmemory-policy、save(持久化)、appendonly、timeout(空闲连接超时)、tcp-keepalive、bind、requirepass。改配置前理解影响——如 save 规则过频会增加 fork 开销。

redis
# view config (runtime changes with CONFIG SET)
CONFIG GET maxmemory
CONFIG GET maxmemory-policy
CONFIG GET save
CONFIG GET appendonly

# change config at runtime
CONFIG SET maxmemory 4gb
CONFIG SET maxmemory-policy allkeys-lru
CONFIG SET slowlog-log-slower-than 10000

# persist config changes to redis.conf
CONFIG REWRITE

# view all config (large output)
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

调试与故障排除

DEBUG SLEEP 模拟延迟(测试用)。DEBUG OBJECT 查看键内部信息(可能慢)。OBJECT ENCODING 看编码。SLOWLOG 找慢命令。LATENCY DOCTOR 分析延迟。MONITOR 实时看所有命令(仅调试,严重影响性能)。MEMORY USAGE 量键大小。redis-cli --bigkeys 找大键。故障排除思路:先看 INFO 和 SLOWLOG,再查大键和慢命令,最后检查网络和客户端。保持冷静——Redis 通常很稳定,问题多在应用层。

redis
# check if a key exists and its type
EXISTS mykey
TYPE mykey
OBJECT ENCODING mykey
OBJECT IDLETIME mykey     # seconds since last access

# memory usage per key
MEMORY USAGE mykey

# find keys blocking the server (DEBUG SLEEP for testing)
DEBUG SLEEP 2             # blocks for 2 seconds (testing only)

# check the latency
LATENCY HISTORY event
LATENCY GRAPH event

# object frequency (LFU mode)
OBJECT FREQ mykey

# inspect a key's internal structure (debugging)
DEBUG OBJECT mykey        # raw internal info

# last saved RDB timestamp
LASTSAVE

# active defragmentation (4.0+)
CONFIG SET activedefrag yes
MEMORY MALLOC-STATS

升级与维护

零停机升级:先升级从节点,故障转移到新从节点,再升级旧主节点。集群逐节点滚动重启(先故障转移主节点再重启)。升级前务必备份(BGSAVE + 复制 RDB)。阅读版本发布说明关注破坏性变更。在预发布环境测试。Redis 跨小版本向后兼容,但大版本可能有迁移步骤。维护窗口仍推荐——即使设计为零停机,也应有回滚预案。升级后监控关键指标确认正常。

redis
# upgrade a standalone Redis:
# 1. save the dataset
SAVE
# 2. stop Redis
redis-cli SHUTDOWN
# 3. replace the binary
# 4. start the new version
redis-server /etc/redis/redis.conf

# upgrade a primary/replica pair (zero downtime):
# 1. upgrade the REPLICA first
SHUTDOWN
# 2. start new version on replica
# 3. promote replica to primary (SENTINEL FAILOVER)
# 4. upgrade the old primary (now a replica)
# 5. failback if desired

# upgrade a cluster:
# 1. upgrade one node at a time (rolling)
# 2. for each node: CLUSTER FAILOVER (if primary), upgrade, restart

# always back up before upgrading
# check the release notes for breaking changes
# test the upgrade in staging first

# rolling restart of a cluster (script)
for node in $nodes; do
  redis-cli -h $node CLUSTER FAILOVER
  redis-cli -h $node SHUTDOWN
  ssh $node "systemctl start redis"
done
18

常见模式

缓存模式(旁路缓存)

旁路缓存是最常见模式:先查缓存,未命中查数据库再回填。设 TTL 限制陈旧。写时可选写穿(写数据库时同步更新缓存)或失效(删缓存键)。失效更安全(无重填竞态),写穿保持缓存热。缓存雪崩防护:用锁或 dogpile 模式防止未命中时大量请求同时击穿到数据库。监控缓存命中率——低于 90% 时调查键的 TTL 和访问模式。

redis
# cache-aside: app checks cache, falls back to DB, fills cache
# pseudo-code:
function get_user(id):
  # 1. check cache
  data = GET("user:" + id)
  if data:
    return JSON.parse(data)

  # 2. cache miss -> query database
  data = db.query("SELECT * FROM users WHERE id = ?", id)

  # 3. fill cache (with TTL to avoid stale data)
  SET("user:" + id, JSON.stringify(data), "EX", 3600)

  return data

# write-through: update cache when DB changes
function update_user(id, data):
  db.update(id, data)
  SET("user:" + id, JSON.stringify(data), "EX", 3600)

# cache invalidation:
DEL("user:" + id)   # when the DB record is deleted

分布式锁

SET NX EX 实现简单分布式锁:设 TTL 防止持有者崩溃后死锁,用唯一 token 释放时只删自己的锁(Lua 脚本原子 check-and-delete)。Redlock 算法在多个独立 Redis 实例上获取锁提高安全性。注意锁非绝对安全——时钟漂移、GC 暂停都可能破坏。强一致场景考虑 etcd/ZooKeeper。对大多数实际协调场景 Redis 锁足够。锁的 TTL 要大于业务执行时间,否则业务未完锁已释放。

redis
# SET NX with expiry = simple distributed lock
SET lock:resource "token-abc" NX EX 10
# returns OK if acquired, nil if already held

# release: only delete if the value matches (avoid releasing others' locks)
# (requires a Lua script for atomicity)
EVAL "
  if redis.call('GET', KEYS[1]) == ARGV[1] then
    return redis.call('DEL', KEYS[1])
  end
  return 0
" 1 lock:resource "token-abc"

# Redlock algorithm (multi-instance, higher safety):
# 1. get the time
# 2. SET NX on N (usually 5) independent Redis instances
# 3. if acquired on majority (3/5) within validity window -> locked
# 4. otherwise, release on all instances and retry

# lock options:
#   EX 10        # expire after 10 seconds (safety)
#   NX           # only if not exists
#   "token"      # unique value per lock holder (for safe release)

限流

固定窗口(INCR+EXPIRE)最简单但窗口边界可能 2 倍突发。滑动窗口(时间戳的有序集合)更准确但耗内存。令牌桶允许突发到上限。Lua 脚本保证原子性。大规模每用户限流固定窗口通常够用且内存高效。GCRA(通用信元速率算法)是优雅的单键版本。限流值根据业务定——API 100 次/分钟、登录 5 次/分钟等。监控被限流的请求比例调整阈值。

redis
# fixed window rate limiter (simple)
# INCR + EXPIRE on first request
count = INCR("rate:user:1")
if count == 1:
  EXPIRE("rate:user:1", 60)   # 60-second window
if count > 100:
  return "Rate limited"

# sliding window rate limiter (sorted set)
now = current_time_millis()
ZADD("rate:user:1", now, now)         # add current request
ZREMRANGEBYSCORE("rate:user:1", 0, now - 60000)  # remove old
EXPIRE("rate:user:1", 60)
count = ZCARD("rate:user:1")          # current count
if count > 100:
  return "Rate limited"

# token bucket (Lua script for atomicity)
# maintains a bucket of tokens refilled over time
# see: https://redis.io/docs/patterns/distributed-locks/

# GCRA (Generic Cell Rate Algorithm) — elegant single-key version

排行榜与排名

有序集合天生适合排行榜:ZADD 更新分数,ZREVRANGE 取 Top N,ZREVRANK 取排名。ZINCRBY 原子更新分数。分时段排行榜(日/周)用带 TTL 的分时段键。分页用 ZREVRANGE 加偏移。百分位排名结合 ZCARD 和 ZREVRANK 计算。这是 Redis 最自然和强大的用例之一——无需应用层排序,所有操作 O(log N) 高效。

redis
# sorted set = perfect for leaderboards
ZADD leaderboard 100 "alice" 250 "bob" 150 "carol"

# top 10 players (highest scores)
ZREVRANGE leaderboard 0 9 WITHSCORES

# a player's rank
ZREVRANK leaderboard "alice"     # 0-indexed from top

# 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

会话存储

Redis 是经典的会话存储:快速、支持 TTL(自动过期)、跨应用服务器共享。每个会话用哈希存字段并设滑动 TTL(每次请求刷新)。用集合跟踪用户的会话列表实现“全设备登出”。集群中用哈希标签 {user:1} 让用户的会话和索引在同一分片。权限变更时轮换会话 ID 增强安全。

redis
# Redis is ideal for web sessions (fast, expiring, shared)
# store session as a hash with TTL
HSET session:abc123 user_id 1 last_active 1700000000 data "..."
EXPIRE session:abc123 1800     # 30-minute timeout

# update session (and refresh TTL)
HSET session:abc123 last_active 1700001000
EXPIRE session:abc123 1800     # sliding expiration

# destroy a session
DEL session:abc123

# list active sessions for a user (index by user)
SADD user:1:sessions "session:abc123"
SMEMBERS user:1:sessions       # all session IDs

# cluster-friendly: use a hash tag for the user's sessions
SADD {user:1}:sessions "session:abc123"
# enables multi-key operations on the user's session data

# rotate session ID on privilege change (security)
RENAME session:old session:new
EXPIRE session:new 1800
19

Lua 脚本

EVAL 基础

EVAL 原子地运行 Lua 脚本——脚本执行时无其他命令运行,适合多步骤原子操作。KEYS 和 ARGV 从调用方传参;切勿在脚本中用字符串拼接构造键名(违反集群规则)。redis.call 出错时中断(停止脚本),redis.pcall 返回错误表(可处理)。脚本按 SHA1 哈希缓存,重复调用可用 EVALSHA 提效。返回类型支持字符串/整数/布尔/表(nil/true/false)。

redis
# run a Lua script inline
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 mykey "hello"
# 1 = number of keys
# mykey = KEYS[1]
# "hello" = ARGV[1]

# return types: string/integer/boolean/table (nil/true/false)
EVAL "return 42" 0
EVAL "return {1, 2, 3}" 0
EVAL "return redis.call('GET', KEYS[1])" 1 mykey

# 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" 0

EVALSHA 与脚本缓存

EVALSHA 用先前加载脚本的 SHA1 哈希执行,避免每次调用重传脚本体——对重复运行相同脚本的应用是重大带宽优化。缓存未命中(SCRIPT FLUSH 后或重启)返回 NOSCRIPT;客户端捕获后回退到 EVAL + SCRIPT LOAD。始终用客户端库(redis-py、ioredis)透明处理此逻辑。SCRIPT EXISTS 检查是否缓存,SCRIPT FLUSH 清空缓存,SCRIPT DEBUG 调试模式。

redis
# load script once, get its SHA1
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
# returns: "fa00..." (the SHA1)

# execute by hash (saves bandwidth on repeated calls)
EVALSHA "fa00..." 1 mykey

# check if a script is cached
SCRIPT EXISTS "fa00..." "abcd..."
# returns: 1) 1  2) 0

# flush the script cache
SCRIPT FLUSH

# list all running scripts (NOSCRIPT errors = cache miss -> reload)
SCRIPT DEBUG SYNC   # blocking debug mode
SCRIPT DEBUG NO     # disable

原子操作

Lua 脚本是 Redis 中实现原子多命令操作的规范方式(无 MULTI/EXEC 竞态)。比较并设置、信号量获取、队列迁移都受益。脚本运行时阻塞服务器——保持简短,避免循环遍历大量键(会卡住其他客户端)。对非常长的工作,用 Redis Functions(7.0+)——它们像存储过程一样声明和版本管理。原子比较并设置、原子“正数才递减”、原子列表迁移是典型用例。

redis
# atomic compare-and-set (only update if value matches)
local cur = redis.call('GET', KEYS[1])
if cur == ARGV[1] then
  return redis.call('SET', KEYS[1], ARGV[2])
end
return 0

# atomic 'decrement if positive' (semaphore / rate limit)
local val = tonumber(redis.call('GET', KEYS[1]) or 0)
if val > 0 then
  redis.call('DECR', KEYS[1])
  return 1
end
return 0

# atomic list pop + push (move between queues)
local item = redis.call('RPOP', KEYS[1])
if item then
  redis.call('LPUSH', KEYS[2], item)
end
return item

Redis 函数(7.0+)

Redis Functions(7.0+)是临时 EVAL 脚本的现代替代:注册、命名、版本管理,像数据库存储过程。库捆绑多个函数,FCALL 调用。函数持久化到 RDB/AOF(重启不丢失),不像临时 EVAL 缓存。对非平凡逻辑更易维护,更易审计和版本控制。FUNCTION LOAD 注册库,FUNCTION LIST 列库,FUNCTION DELETE 删库,FUNCTION DUMP/RESTORE 支持备份迁移。

redis
# register a library of functions
FUNCTION LOAD "#!lua name=mylib
redis.register_function('myset', function(keys, args)
  return redis.call('SET', keys[1], args[1])
end)

redis.register_function('double', function(keys, args)
  local v = tonumber(redis.call('GET', keys[1]))
  return redis.call('SET', keys[1], v * 2)
end)
"

# call a function
FCALL myset 1 mykey "hello"
FCALL double 1 counter

# list libraries
FUNCTION LIST
FUNCTION LIST WITHCODE

# delete a library
FUNCTION DELETE mylib

# dump and restore (for backup / migration)
FUNCTION DUMP
FUNCTION RESTORE <payload>

调试脚本

用 redis.log() 向 Redis 日志输出调试信息不影响返回值。redis-cli --ldb 是交互式 Lua 调试器:设断点、单步、检查局部变量。SCRIPT DEBUG SYNC 让服务器阻塞在脚本上以便调试器单步。始终在预发布环境测试脚本——失控循环可能锁死整个 Redis 服务器。为复制正确性保持脚本确定性(不用随机/时间函数)。pcall 捕获错误便于诊断。

redis
# LOG writes to the Redis log (visible in redis-cli)
EVAL "redis.log(redis.LOG_NOTICE, 'hello from lua'); return 1" 0
# levels: LOG_DEBUG, LOG_VERBOSE, LOG_NOTICE, LOG_WARNING

# return the error object on pcall failure
EVAL "local ok, err = pcall(function()
  error('something broke')
end)
return {ok=tostring(ok), err=tostring(err)}" 0

# debugger (Redis 5.0+)
SCRIPT DEBUG SYNC   # synchronous, blocking
TDEBUG              # step / continue / print
# use redis-cli --ldb to launch debugger:
#   redis-cli --ldb --eval myscript.lua key1 key2 , arg1 arg2

# print variables in the debugger
print myvar
step
continue
20

监控与故障排除

INFO 与 LATENCY

INFO 是最重要的监控命令——一次报告内存、客户端、复制和吞吐。关注 mem_fragmentation_ratio(理想 ~1.0–1.5);>1.5 浪费内存,<1 表示 Redis 在交换(对延迟是灾难性的)。LATENCY DOCTOR 分析近期慢事件并建议修复。监控 keyspace_misses 和 keyspace_hits 追踪缓存命中率。LATENCY HISTORY 看延迟事件历史,LATENCY RESET 清除。LATENCY GRAPH 用 ASCII 图可视化。

redis
# INFO: server, clients, memory, stats, replication, cpu
INFO                # everything
INFO memory         # memory section only
INFO replication    # replica lag, master link
INFO stats          # ops/sec, keyspace hits/misses

# key metrics to watch:
#   used_memory_rss        # RSS from OS (real footprint)
#   mem_fragmentation_ratio # >1.5 = fragmentation, <1 = swapping (bad!)
#   connected_clients       # client connections
#   instantaneous_ops_per_sec
#   keyspace_misses         # cache hit ratio = hits / (hits + misses)

# LATENCY: samples slow events
LATENCY HISTORY event-name
LATENCY DOCTOR      # human-readable diagnosis
LATENCY RESET

# LATENCY GRAPH event-name (ASCII art)

SLOWLOG 与 CLIENT LIST

SLOWLOG 捕获超过阈值(默认 10ms)的命令——对找到导致延迟尖峰的查询极有价值。KEYS * 和对大集合的 SMEMBERS 等键空间操作是常见元凶。CLIENT LIST 显示每个连接的空闲时间和当前命令;用 CLIENT SETNAME 给应用的连接命名,便于识别卡住的客户端属于哪个连接池。CLIENT KILL 按 ID 或地址终止连接。SLOWLOG GET 查看慢命令,SLOWLOG RESET 清除。

redis
# SLOWLOG: record slow commands (over slowlog-log-slower-than, default 10ms)
CONFIG SET slowlog-log-slower-than 10000   # 10ms in microseconds
CONFIG SET slowlog-max-len 128             # keep last 128 entries

SLOWLOG GET          # last 10 slow commands
SLOWLOG GET 5        # last 5
SLOWLOG RESET        # clear

# each entry: id, timestamp, duration (us), command args, client, addr

# CLIENT LIST: see all connected clients
CLIENT LIST
# fields: id, addr, fd, name, age, idle, flags, db, cmd, ...

# kill a stuck client
CLIENT KILL ADDR 10.0.0.1:12345
CLIENT KILL ID 42

# name your connections for easier debugging
CLIENT SETNAME my-app-worker

MEMORY 与 OBJECT

MEMORY USAGE 报告单个键的精确字节数(SAMPLES 0 对大集合全量准确)。OBJECT ENCODING 揭示 Redis 内部格式——小结构用紧凑编码(listpack、ziplist)省 CPU 和内存,随增长自动升级为 hashtable/skiplist。了解编码有助于解释为何某键比预期大或慢。MEMORY DOCTOR 给出内存诊断。OBJECT IDLETIME 报告最后访问以来的秒数,OBJECT FREQ 报告 LFU 访问频率。

redis
# MEMORY USAGE: bytes used by a single key
MEMORY USAGE key
MEMORY USAGE bigset SAMPLES 0   # scan all elements (accurate)

# MEMORY STATS: detailed allocator stats
MEMORY STATS

# MEMORY DOCTOR: diagnosis
MEMORY DOCTOR

# OBJECT ENCODING: see internal representation
OBJECT ENCODING mylist      # quicklist / ziplist / listpack / hashtable
OBJECT REFCOUNT mykey
OBJECT IDLETIME mykey       # seconds since last access
OBJECT FREQ mykey           # LFU access frequency (only with maxmemory-policy LFU)

# why is my key so big? use DEBUG OBJECT (careful, can be slow)
DEBUG OBJECT mykey

大键与键空间扫描

永远不要在生产环境用 KEYS ——它会阻塞服务器。SCAN 是基于游标的非阻塞替代;迭代到游标返回 0 结束。--bigkeys 和 --memkeys 是基于 SCAN 的 CLI 助手,不阻塞地发现大键。大键(尤其是数百万元素的集合)在过期/驱逐时导致延迟尖峰——找到并拆分它们(如按时间分片巨大的有序集合)。HSCAN/SSCAN/ZSCAN 增量迭代集合字段。TTL/PTTL 查键剩余存活时间。

redis
# find big keys safely (production-safe, samples)
redis-cli --bigkeys
# scans keyspace, reports largest of each type

# more accurate: --memkeys (memory usage per key)
redis-cli --memkeys
redis-cli --hotkeys      # with LFU policy, finds frequently-accessed keys

# SCAN: iterate keys without blocking (never use KEYS *)
SCAN 0 MATCH user:* COUNT 100
# returns: next-cursor, [key1, key2, ...]
SCAN <next-cursor> ...
# cursor 0 = done

# scan a hash field-by-field
HSCAN myhash 0 COUNT 100
SSCAN myset 0 MATCH prefix*
ZSCAN myzset 0

# inspect a key's TTL (left to live)
TTL mykey           # seconds (-1 = no expire, -2 = no key)
PTTL mykey          # milliseconds

集群健康与故障转移

CLUSTER NODES 和 CLUSTER INFO 是集群健康的主要检查——找 cluster_state:ok 且所有槽位已分配(共 16384)。CLUSTER FAILOVER 在从节点上触发受控主切换;FORCE 跳过初始同步(更快但略有数据丢失风险),TAKEOVER 绕过集群共识(仅紧急情况)。重新分片或事故后始终运行 --cluster check 捕获孤立槽位或脑裂状态。CLUSTER KEYSLOT 查键所属哈希槽,--cluster fix 自动修复小问题。

redis
# cluster node status
CLUSTER NODES
# fields: id, addr, flags (master/slave/fail?), slots, ping/pong

# cluster-wide health
CLUSTER INFO
# cluster_state:ok / cluster_slots_assigned / cluster_slots_ok
# cluster_known_nodes

# slot ownership (which node owns a key)
CLUSTER KEYSLOT mykey    # hash slot number
CLUSTER COUNTKEYSINSLOT 1234

# reshard slots (use redis-cli --cluster reshard)
redis-cli --cluster reshard 127.0.0.1:7000 \
  --cluster-from <node-id> --cluster-to <node-id> \
  --cluster-slots 1000 --cluster-yes

# manual failover (on a replica)
CLUSTER FAILOVER          # wait for sync
CLUSTER FAILOVER FORCE    # skip sync (faster, slight data loss risk)
CLUSTER FAILOVER TAKEOVER # no consensus (emergency only)

# verify cluster after changes
redis-cli --cluster check 127.0.0.1:7000
redis-cli --cluster fix 127.0.0.1:7000    # auto-repair minor issues

这篇内容对您有帮助吗?