入门
连接 Redis
redis-cli 是交互式命令行客户端。默认连接本地 6379 端口。始终使用 PING 测试连接(返回 PONG 表示成功)。在远程连接时使用 -a 标志或 AUTH 命令提供密码。redis-cli 还支持管道输入、--stat 监控和 --bigkeys 扫描大键——它是日常运维瑞士军刀般的工具。
# 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 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 随机返回一个键。键名应简洁但具描述性,并避免过长的前缀以节省内存。
# 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,避免内存无限增长。
# 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 3600SCAN 与安全迭代
永远不要在生产环境使用 KEYS ——它会阻塞服务器遍历整个键空间。SCAN 是基于游标的替代方案,返回新游标直到为 0 结束。结果可能重复或遗漏已变更的键——这是设计上的权衡。MATCH 在服务端过滤(仍扫描所有键);COUNT 是提示而非保证。HSCAN、SSCAN、ZSCAN 用于遍历大集合字段。对后台任务和监控脚本,SCAN 是唯一安全的选择。
# 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数据类型概览
核心数据结构
Redis 提供 5 种核心类型及若干扩展类型。字符串存任意字节(最大 512MB);哈希存字段-值映射;列表是有序字符串序列;集合存无序唯一元素;有序集合按分数排序。4.8+ 新增 Streams(日志),3.2+ 新增地理空间 。选对类型至关重要——决定功能、性能和内存占用。Redis 在 Redis 7+ 中围绕每种类型实现了更高效的内部编码。
# 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 不会自动转换类型。
# 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 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 growsOBJECT 与内存命令
OBJECT ENCODING 查看键的内部表示,在调优时很有价值。OBJECT IDLETIME 报告最后一次访问以来的秒数(仅在使用近似 LRU/LFU 驱逐策略时收集)。MEMORY USAGE(4.0+)估算单个键占用的字节数——对排查大键很方便。MEMORY DOCTOR 在 4.0+ 中给出内存诊断。DEBUG OBJECT 提供原始内部信息但需谨慎(可能很慢)。这些工具帮助理解 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 类型,但需在服务端启用模块。
# 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字符串
基础字符串操作
字符串是 Redis 最基础类型。SET 是写,GET 是读,DEL 是删除。NX 选项表示仅在键不存在时设置(用于锁);XX 表示仅在已存在时设置。GETSET 原子地返回旧值并设置新值。字符串上限 512MB,但大字符串几乎总意味着设计错误——考虑用哈希或分片。SETRANGE 和 GETRANGE 修改子串(后者对二进制安全)。
# 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 脚本处理。
# 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——开销显著更低。
# 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 支持多字节字段操作。
# 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 位字段存用户的点赞数和被点赞数,仅占一个键。注意字段偏移以位为单位。
# 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哈希
哈希基础
哈希存储字段-值映射,是表示对象的理想结构(如 user:100 有 name、email、age 字段)。相比存 JSON 字符串,哈希允许原子地修改单个字段而无需读出-修改-写回整个对象。HSET 一次可设置多个字段。HGET 取单个,HMGET 取多个,HGETALL 取全部。小哈希使用 listpack 编码内存极省。每个哈希最多 2^32-1 个字段。
# 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 删除字段。这些操作都是原子的且只影响指定字段,无需锁定整个哈希。这使哈希成为存储和更新结构化数据的高效选择。
# 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 不保证返回顺序,且在迭代期间被修改的哈希可能返回重复或遗漏字段。
# 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+ 才能使用。
# 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。相比用多个字符串键表示对象字段,哈希在内存和管理上都更高效。唯一例外是需要对整个对象做范围查询或复杂索引时,考虑其他结构。
# 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列表
列表基础(推入/弹出)
列表是有序字符串序列,支持头尾两端 O(1) 推入/弹出。LPUSH/RPUSH 在头/尾添加;LPOP/RPOP 从头/尾弹出。LRANGE 查看范围(0 到 -1 查看全部)。LLEN 返回长度。BLPOP/BRPOP 是阻塞版本——当列表为空时挂起直到有元素或超时,是构建队列的利器。列表在 Redis 7+ 中使用 listpack(小)或 quicklist(大)编码。
# 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)。
# 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。
# 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 列表更适合队列和栈式的端点操作。
# 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。列表简单快速,但功能有限。
# 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集合
集合基础
集合存储无序的唯一元素。SADD 添加,SREM 删除,SISMEMBER 检查成员(O(1)),SMEMBERS 返回全部。SCARD 返回基数。小集合用 listpack 编码(元素少且都为整数时尤其高效),大集合用 hashtable。集合擅长成员测试和去重——判断某用户是否在黑名单、统计不重复的 IP 数等。每个集合最多 2^32-1 个元素。
# 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 随机返回元素。
# 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+)只返回交集大小不返回元素,更省内存和带宽。
# 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 快速判断。对需要按分数排序的唯一元素,用有序集合。对需要存储每个元素额外属性的映射,用哈希。
# 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 的类型各有所长。
# 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有序集合
有序集合基础
有序集合(ZSET)是 Redis 最强大的结构之一:每个元素带一个分数(double),按分数排序且唯一。ZADD 添加(可带 NX/XX/GT/LT 选项),ZSCORE 取分数,ZRANK 取排名(0 起),ZCARD 取基数。它用 skiplist+hashtable 实现,兼顾排序遍历和 O(1) 查找。小有序集合用 listpack 编码更省内存。是排行榜、时间线、范围查询的利器。
# 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 是返回数——非常高效。
# 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 中维护了排名信息。
# 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 仅在所有元素分数相同时才有意义,否则结果不可预期。
# 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 返回分数。权重参数让不同集合的贡献可调节。
# 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发布/订阅
发布/订阅基础
PUBLISH 向频道发消息,SUBSCRIBE 订阅频道,UNSUBSCRIBE 取消。PSUBSCRIBE 按模式订阅(如 user:* 匹配所有以 user: 开头的频道)。发布/订阅是即时的——发布时无订阅者则消息丢弃,无持久化。订阅是阻塞式的,收到消息时回调。这是最简单的实时消息传递方式,适合实时通知、聊天。但无消息保证——需要可靠性时用 Streams。
# 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 只在频道所属节点处理。订阅分片频道时连接必须路由到正确节点。对大规模发布/订阅(如每秒数万消息),分片版本显著降低集群负载。旧客户端可能不支持,需升级客户端库。
# 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 做确认机制)。
# 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 启用所有。典型用途:过期后清理关联资源、缓存失效联动。注意通知是尽力而为的——高负载下可能丢失,不要依赖其做关键逻辑。
# 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 做可靠传递,发布/订阅做旁路通知。选择取决于对消息丢失的容忍度。
# 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事务与管道
MULTI/EXEC 事务
MULTI 开启事务,后续命令入队不执行,EXEC 原子地按顺序执行全部。DISCARD 取消。Redis 事务没有回滚——命令入队时语法错误会拒绝 EXEC,但运行时错误(如对字符串 INCR)不会中断后续命令也不会回滚已执行的。这意味着事务保证的是顺序执行和原子性(期间不被其他命令打断),而非 ACID 的回滚。需要条件执行时用 WATCH。
# 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 commandWATCH(乐观锁)
WATCH 实现乐观并发控制:监视键,若在 EXEC 前被修改则整个事务失败(返回 nil)。典型用法:WATCH balance; GET balance; MULTI; DECRBY balance 100; INCRBY merchant 100; EXEC。若 EXEC 返回 nil 表示有人改了 balance,重试整个流程。WATCH 是 CAS(比较并交换)的基础。UNWATCH 取消监视。事务执行后 WATCH 自动清除。
# 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 条一批)。
# 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 替代。
# 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。
# 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)持久化
RDB 快照
RDB 是某一时刻的内存快照,压缩二进制文件。SAVE 同步阻塞生成(生产慎用),BGSAVE 后台 fork 子进程生成。配置 save 规则(如 save 900 1 表示 900 秒内 1 次修改触发)。RDB 文件紧凑、恢复快、适合备份。缺点是两次快照间的数据可能丢失。fork 在大内存实例上可能短暂阻塞(复制页表)。RDB 适合作为灾难恢复的备份手段。
# 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 snapshotsAOF(追加文件)
AOF 追加每条写命令到日志。fsync 策略:always(最安全最慢)、everysec(默认,最多丢 1 秒)、no(交由 OS)。AOF 比 RDB 更耐久但文件更大、恢复更慢。BGREWRITEAOF 压缩 AOF(重放当前状态生成最小命令集)。redis-check-aof 工具修复损坏的 AOF。AOF 适合对数据丢失零容忍的场景。Redis 7.0+ AOF 使用多部分文件格式(base + 增量)。
# 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 restartRDB + AOF 混合
同时开启 RDB 和 AOF:Redis 4.0+ 在 AOF 重写时生成 RDB 格式的 base 文件 + 增量 AOF 命令。恢复时先加载 RDB(快)再重放增量 AOF(补全)。这结合了 RDB 的快速恢复和 AOF 的数据完整性。aof-use-rdb-preamble yes(默认)启用此格式。生产环境推荐此混合模式——兼顾恢复速度和数据安全。需同时配置 save 规则和 appendonly yes。
# 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 的异地备份应对区域性故障。
# 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 延迟。
# 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)集群
Redis 集群基础
Redis Cluster 自动分片到多节点,用 16384 个哈希槽。CRC16(key) % 16384 决定槽位。每个主节点负责一部分槽。CLUSTER INFO 看状态,CLUSTER NODES 看节点。至少 3 主 3 从(6 节点)才能容忍单节点故障。集群提供高可用和水平扩展,但增加运维复杂度。客户端需支持集群协议(MOVED/ASK 重定向)。用 redis-cli --cluster 创建和管理集群。
# 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} 标签。注意标签内是字面匹配——设计不当会导致热点(某大用户的所有数据挤在一个节点)。权衡局部性和负载均衡。
# 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。在线运维但避开高峰期,迁移大键可能卡住。
# 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。
# 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 > 单机。
# 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 youSentinel(高可用)
Sentinel 设置
Sentinel 是独立进程监控主从集群,主故障时自动提升从节点。至少 3 个 Sentinel(奇数,避免脑裂)。配置 sentinel monitor mymaster <ip> <port> <quorum>,quorum 是同意故障的 Sentinel 数。Sentinel 间互相发现并交换主从状态。客户端连接 Sentinel 而非直接连 Redis——Sentinel 告知当前主地址。故障转移后客户端自动重连新主。
# 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 mymasterSentinel 命令
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 做故障转移。
# 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 获取新主地址。整个过程自动完成,通常数秒到数十秒。
# 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 不影响主)和分析查询。监控从节点的复制偏移量判断延迟。
# 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)决定断线容忍时长。监控复制延迟和主从链接状态。
# 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 resyncStreams(流)
Stream 基础(XADD/XREAD)
Streams 是 Redis 5.0+ 的日志数据结构,类似 Kafka 的分区日志。XADD 追加条目(自动生成时间戳 ID),XREAD 读取(可阻塞 XREAD BLOCK)。XLEN 返回长度。XRANGE/XREVRANGE 按范围读。MAXLEN 截断保留最近 N 条(用 ~ 近似截断更高效)。Streams 天生持久化、有序、可回放——比发布/订阅可靠得多。适合事件溯源、活动日志、任务队列。
# 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 转移给其他消费者(崩溃恢复)。消费者组保证每条消息只被组内一个消费者处理,且可回溯。这是构建可靠任务队列的基础。
# 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(多久未确认视为崩溃)。监控待处理消息数——堆积说明消费速度跟不上生产。
# 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 中最完整的消息传递方案。
# 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 dayStreams 与其他队列对比
Streams vs 列表:Streams 持久化、有消费者组、可回放;列表简单但无确认机制。Streams vs 发布/订阅:Streams 持久化、有消费者组、可回放;发布/订阅即时无持久。Streams vs Kafka:Streams 轻量、内嵌 Redis、无独立运维;Kafka 更强大、更高吞吐、更复杂。Redis 内的消息队列选 Streams;跨系统的大规模流处理用 Kafka。Streams 适合单 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 retryHyperLogLog、位图与地理空间
HyperLogLog(基数统计)
HyperLogLog 用极少内存(固定约 12KB)估算集合基数(不重复元素数),误差约 0.81%。PFADD 添加元素,PFCOUNT 估算基数,PFMERGE 合并多个 HLL。适合统计独立访客数、独立搜索数等海量去重场景——用 SET 存上亿元素不现实,HLL 仅 12KB。注意是估算非精确,且不存储元素本身(无法查询某元素是否存在)。对精度要求极高时用 SET 或位图。
# 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 更省内存。位图操作是原子的。
# 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 个成员。
# 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 将结果存入新有序集合,便于后续分页或缓存。
# 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 精度有限。理解取舍后才能选对类型,避免过度设计或欠设计。
# 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)安全
认证与 ACL
Redis 6+ ACL 提供细粒度权限控制。CONFIG SET requirepass 设全局密码(旧式,所有命令权限)。ACL SETUSER 创建用户并限制命令和键:on/off 启用停用,>password 设密码,~keypattern 限制键,+cmd/-cmd 限制命令。ACL WHOAMI 看当前用户,ACL LIST 列所有用户。生产环境应为不同应用创建最小权限用户——如只读用户、只能访问特定前缀的用户。默认 user 应禁用或限制。
# 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.aclTLS/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。
# 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。定期审计访问日志和客户端连接。
# 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)便于审计和告警。注意日志中可能含敏感数据——脱敏和访问控制。
# 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. 制定安全事件响应流程。
# 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)性能与优化
性能基础
Redis 单线程命令处理(6+ 用多线程仅做 I/O,命令仍串行),避免慢命令阻塞。KEYS、SMEMBERS 大集合、SORT 大集合、LRANGE 全列表都是隐患——用 SCAN、SSCAN、有序集合替代。watchdog 是 O(1) 但频繁调用累积开销。单个命令应 < 1ms。监控 instantaneous_ops_per_sec 和延迟。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 条/批。
# 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。
# 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 调整策略和容量。
# 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-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管理
服务器信息与监控
INFO 是最重要的监控命令——返回服务器、客户端、内存、持久化、统计、复制等各部分信息。MEMORY STATS 和 MEMORY DOCTOR 提供详细内存诊断。LATENCY DOCTOR 分析延迟事件。CLI 工具 --stat 持续监控关键指标,--latency 测往返延迟。生产环境用 Prometheus + redis_exporter 或 Redis Insight 做长期监控和可视化。关注内存使用、连接数、ops/sec、命中率、复制延迟。