主题
第十章:Redis 常用数据结构与典型使用场景
Q26:Redis 常用的数据结构有哪些?分别适合什么场景?
答:
Redis 不是简单的 key-value 缓存。它提供了多种数据结构,不同结构解决的问题不同。选型时不要只问“能不能存”,而要问:数据是否需要按字段更新、是否需要排序、是否需要去重、是否需要阻塞消费、是否需要可靠确认、是否允许近似统计、是否需要按地理位置查询。
Redis 常用数据结构总览图
教科书式可编辑画板版总图: [redis-ch10-data-structures-overview.html](file:///Users/binny/workspace-sdd/aiNotes/redis/redis-ch10-data-structures-overview.html)
Excalidraw 源文件: [redis-ch10-data-structures-overview.excalidraw](file:///Users/binny/workspace-sdd/aiNotes/redis/redis-ch10-data-structures-overview.excalidraw)
下面保留一份纯文本版总览图,便于在不支持画板预览的 Markdown 阅读器中快速查看结构:
text
Redis
│
┌───────────────┬───────┼───────────────┬───────────────┐
│ │ │ │ │
String Hash List / Stream Set / ZSet 其他模块
│ │ │ │ │
单值缓存、计数 对象字段 队列/消息流 去重/排序/排名 JSON、搜索、图形化、统计
│ │ │
阻塞消费 会员关系 向量、过滤器、指标可以先用下面这张表建立整体印象:
| 数据结构 / 能力 | 核心特点 | 最典型场景 | 常用命令 |
|---|---|---|---|
| String | 单 key 单 value,可做字符串、数字、JSON、二进制 | 缓存、计数器、验证码、分布式锁 | GET、SET、INCR、SET NX EX |
| Hash | 一个 key 下多个 field | 用户资料、任务进度、商品详情 | HSET、HGET、HGETALL、HINCRBY |
| List | 有序列表,两端插入/弹出 | 任务队列、最新列表、栈 | LPUSH、BRPOP、LRANGE、LTRIM |
| Set | 无序去重集合 | 点赞用户、标签、共同关注 | SADD、SISMEMBER、SINTER |
| ZSet | 带 score 的有序集合 | 排行榜、延迟队列、时间线排序 | ZADD、ZREVRANGE、ZRANGEBYSCORE |
| Stream | 带消息 ID 的日志型消息流 | 可靠队列、消费组、事件日志 | XADD、XREADGROUP、XACK |
| Bitmap | bit 位状态存储 | 签到、活跃用户、灰度标记 | SETBIT、GETBIT、BITCOUNT |
| BitField | 在字符串中按位段读写整数 | 多个小整数状态压缩存储 | BITFIELD |
| HyperLogLog | 大规模去重数量估算 | UV、访问人数估算 | PFADD、PFCOUNT |
| GEO | 经纬度位置索引 | 附近门店、附近的人 | GEOADD、GEOSEARCH、GEODIST |
| Pub/Sub | 实时发布订阅 | 实时通知、配置变更广播 | PUBLISH、SUBSCRIBE |
| RedisJSON | JSON 文档读写,需模块支持 | 复杂对象缓存、局部 JSON 更新 | JSON.SET、JSON.GET |
| Bloom Filter | 概率型存在性判断,需模块支持 | 防缓存穿透、黑名单预判 | BF.ADD、BF.EXISTS |
| TimeSeries | 时间序列数据,需模块支持 | 监控指标、埋点趋势 | TS.ADD、TS.RANGE |
| RedisSearch | 全文检索和二级索引,需模块支持 | 商品搜索、文档搜索、任务检索 | FT.CREATE、FT.SEARCH |
| Vector Set / Vector Search | 向量相似度检索,需 Redis Stack / 新版本能力 | 语义搜索、相似视频、RAG 检索 | FT.SEARCH、VSIM |
| Cuckoo Filter | 可删除的概率型存在性判断,需模块支持 | 可删除黑名单、临时去重 | CF.ADD、CF.EXISTS、CF.DEL |
| Count-Min Sketch | 频次估算,需模块支持 | 热点 key、热门搜索词、点击频次估算 | CMS.INCRBY、CMS.QUERY |
| TopK | 高频元素近似统计,需模块支持 | 热门视频、热门标签、热门搜索词 | TOPK.ADD、TOPK.LIST |
| TDigest | 分位数统计,需模块支持 | P95/P99 延迟、耗时分布 | TDIGEST.ADD、TDIGEST.QUANTILE |
Redis 常用数据结构的图形化、CLI 与案例一体化展示
下面把每种数据结构的场景概述、图形化展示、redis-cli 命令和 Python 示例放在同一个小节里,便于横向对照阅读。
1. String:字符串 / 数值 / JSON 缓存 / 分布式锁
场景概述: String 适合存放单个值、计数器、缓存和锁标记。
图形化展示:
text
user:1001 ──► "{name: 'Tom', level: 3}"
video:6:view_count ──► "1024"
lock:task:6 ──► "random-token"redis-cli 命令:
bash
redis-cli SET task:6:status RUNNING EX 900
redis-cli GET task:6:status
redis-cli INCR video:6:view_count
redis-cli SET lock:task:6 abc123 NX EX 300Python 示例:
python
redis_client.set("task:6:status", "RUNNING", ex=900)
redis_client.incr("video:6:view_count")适合整体读写单个值、计数、验证码、登录态、分布式锁。
2. Hash:对象字段缓存 / 任务进度 / 用户信息
场景概述: Hash 适合保存对象型数据并进行字段级更新。
图形化展示:
text
task:6:progress
├── stage ──► IMAGE
├── percent ──► 45
└── message ──► 正在生成分镜图redis-cli 命令:
bash
redis-cli HSET task:6:progress stage IMAGE percent 45 message "正在生成分镜图"
redis-cli HGET task:6:progress stage
redis-cli HGETALL task:6:progressPython 示例:
python
redis_client.hset("task:6:progress", mapping={"stage": "IMAGE", "percent": 45})
progress = redis_client.hgetall("task:6:progress")适合一个对象有多个字段、并且经常局部更新。
3. List:队列 / 栈 / 最新列表 / 简单任务流
场景概述: List 适合实现队列、栈和简单的消息流。
图形化展示:
text
task:queue
LPUSH 入队 -> [ task:9 ] -> [ task:8 ] -> [ task:7 ] -> [ task:6 ] <- BRPOP 出队redis-cli 命令:
bash
redis-cli LPUSH task:queue 6
redis-cli BRPOP task:queue 5
redis-cli LRANGE user:1001:feeds 0 19Python 示例:
python
redis_client.lpush("task:queue", 6)
result = redis_client.brpop("task:queue", timeout=5)适合简单队列、最新列表、撤销栈。
4. Set:去重集合 / 标签 / 点赞 / 关系运算
场景概述: Set 适合去重、标签关系和集合运算。
与 ZSet 的区别:
| 维度 | Set | ZSet |
|---|---|---|
| 底层数据结构 | 哈希表(dict) | 跳表(skiplist)+ 字典(dict) |
| 是否去重 | 是 | 是 |
| 是否有序 | 否 | 是,按 score 排序 |
| 元素结构 | 只有 member | member + score |
| 典型用途 | 去重、标签、共同关注 | 排行榜、延迟队列、时间线 |
| 适合“只关心是否存在” | 是 | 也可以,但通常没必要 |
| 适合“要按顺序取前 N 个” | 不适合 | 很适合 |
Set 更像“成员名单”,ZSet 更像“带分数的排名名单”。
底层结构说明:
Set 内部通常使用哈希表存储成员,查找、插入、删除平均都是 O(1)。 ZSet 同时维护字典和跳表:字典负责快速按 member 查找,跳表负责按 score 排序和范围查询。
图形化展示:
text
video:6:liked_users
{ 1001, 1002, 1003, 1008 }redis-cli 命令:
bash
redis-cli SADD video:6:liked_users 1001 1002 1003
redis-cli SISMEMBER video:6:liked_users 1001
redis-cli SINTER user:1:follows user:2:followsPython 示例:
python
redis_client.sadd("video:6:liked_users", 1001, 1002, 1003)
liked = redis_client.sismember("video:6:liked_users", 1001)适合去重、共同关注、抽奖用户池。
5. ZSet:排行榜 / 权重排序 / 延迟队列 / 时间线
场景概述: ZSet 适合排行榜、延迟任务和按分值排序。
与 Set 的区别:
| 维度 | Set | ZSet |
|---|---|---|
| 是否去重 | 是 | 是 |
| 是否有序 | 否 | 是,按 score 排序 |
| 元素结构 | 只有 member | member + score |
| 典型用途 | 去重、标签、共同关注 | 排行榜、延迟队列、时间线 |
| 取前 N 个元素 | 只能随机或集合运算 | 可以按分值直接取前 N 名 |
Set 更像“无序去重名单”,ZSet 更像“带分数的排序名单”。
字母 z 的来源:
ZSet 全名是 Sorted Set。Redis 早期命名时为了和 Set 区分,就在前面加了一个 z,表示 sorted,所以 zset 可以理解为“有序集合”。
为什么 ZADD 里 score 写在 member 前面?score 到底在第几位?
先厘清一个最关键的前提:“score 在第几位”取决于你用哪一层,同一个 ZSet 操作在不同客户端里的书写顺序是不一样的:
| 层 | 写法 | score 的位置 |
|---|---|---|
| Redis 原生命令 | ZADD key 98.5 video:8 | 第 3 个 token 里的值(紧跟 key 后) |
| redis-cli | ZADD key score member | 排在 key 之后、member 之前 |
| Python(redis-py) | zadd(name, {member: score}) | 字典的值(value),member 是键(key) |
也就是说:在 Redis 命令层,score 排在 key 之后、member 之前;而在 Python 客户端里,score 反而成了字典的“值”,member 成了“键”。这两者不矛盾——原因是 Redis 命令用“位置”表达含义,redis-py 用“键值对”表达含义(member 必须唯一所以当键,score 作为它的值)。理解这一点,就不会纠结“到底谁在前”了。
下面重点解释命令层 ZADD key score member 为什么把 score 写在 member 前面:
score 是排序依据(先决条件),member 是被排序对象(被挂上去的东西)。ZSet 的一切能力——排序、范围、排名——都靠 score 驱动。把 score 写在前面,语义是“先给一个分数,再把这个分数挂到某个 member 上”,读作“给 video:8 一个 98.5 分”,符合“依据在前、对象在后”的表达直觉。
固定「score + member」顺序,才能稳定解析批量写入。
ZADD支持一次写多组:ZADD key 98.5 video:8 91.0 video:3 76.5 video:6。每组都是「score、member」两两成对。解析器只需固定地“读一个 score、再读一个 member”循环到底,无需判断边界。若反过来改成「member、score」,在连续多组时解析器无法仅凭值本身判断哪里是一组的起点,必须额外依赖“score 只能是数字”去回溯,写起来更脆弱、也更容易出错。与 ZSet 的底层实现呼应。ZSet 内部用跳表(skiplist)按 score 排序 + 字典(dict)按 member 定位。score 决定元素在有序结构里的“落点位置”,member 决定元素“是谁”。参数顺序让 score 先出现,正好对应“score 决定位置、member 决定身份”的职责分工。
一句话总结:命令层采用「score 在前、member 在后」,因为 score 是排序与定位的依据(放前)、member 是被排序的对象(放后),并且「score+member」成对且顺序固定,能让批量写入被稳定解析;而在 Python 客户端里,score 被放进字典的值、member 当键,只是同一语义换成了“键值对”形式来表达。
图形化展示:
text
rank:video:hot
98.5 ──► video:8
91.0 ──► video:3
76.5 ──► video:6redis-cli 命令:
bash
redis-cli ZADD rank:video:hot 98.5 video:8 91.0 video:3 76.5 video:6
redis-cli ZREVRANGE rank:video:hot 0 9 WITHSCORES
redis-cli ZADD delay:order:cancel 1787797000 order:1001
redis-cli ZRANGEBYSCORE delay:order:cancel -inf 1787797060Python 示例:
python
redis_client.zadd("delay:order:cancel", {"order:1001": 1787797000})
expired = redis_client.zrangebyscore("delay:order:cancel", "-inf", 1787797060)适合排行榜、延迟任务、订单超时取消。
6. Stream:可靠消息流 / 消费组 / 事件日志
场景概述: Stream 适合可靠消息流、消费组和事件追踪。
图形化展示:
text
stream:task
1690000000000-0 ──► task_id=6, stage=SCRIPT
1690000001000-0 ──► task_id=6, stage=IMAGEredis-cli 命令:
bash
redis-cli XADD stream:task * task_id 6 stage SCRIPT
redis-cli XGROUP CREATE stream:task video-workers 0 MKSTREAM
redis-cli XREADGROUP GROUP video-workers worker-1 COUNT 10 BLOCK 5000 STREAMS stream:task >
redis-cli XACK stream:task video-workers 1690000000000-0Python 示例:
python
redis_client.xadd("stream:task", {"task_id": "6", "stage": "SCRIPT"})适合可靠消息流、事件日志、异步任务消费。
7. Bitmap:签到 / 活跃用户 / 布尔状态压缩存储
场景概述: Bitmap 适合高密度布尔状态和签到统计。
图形化展示:
text
active:2026-08-27
用户偏移: 1001 1002 1003 1004
bit 值: 1 0 1 1redis-cli 命令:
bash
redis-cli SETBIT active:2026-08-27 1001 1
redis-cli GETBIT active:2026-08-27 1001
redis-cli BITCOUNT active:2026-08-27Python 示例:
python
redis_client.setbit("active:2026-08-27", 1001, 1)
count = redis_client.bitcount("active:2026-08-27")适合签到、活跃用户、开关状态。
8. BitField:多个小整数状态压缩存储
场景概述: BitField 适合压缩存储多个小整数状态。
图形化展示:
text
score:user:1001:2026-08
| day1 4bit | day2 4bit | day3 4bit |
| 8 | 6 | 9 |redis-cli 命令:
bash
redis-cli BITFIELD score:user:1001:2026-08 SET u4 0 8 SET u4 4 6 GET u4 0 GET u4 4Python 示例:
python
redis_client.execute_command("BITFIELD", "score:user:1001:2026-08", "SET", "u4", 0, 8)适合评分、等级、小整数压缩。
9. HyperLogLog:大规模 UV 去重估算
场景概述: HyperLogLog 适合大规模去重计数和 UV 估算。
图形化展示:
text
uv:video:6
1001, 1002, 1003, 1001, 1008, 1002
└── HLL 估算唯一访问数redis-cli 命令:
bash
redis-cli PFADD uv:video:6 1001 1002 1003
redis-cli PFCOUNT uv:video:6
redis-cli PFMERGE uv:site:range uv:day1 uv:day2Python 示例:
python
redis_client.pfadd("uv:video:6", 1001, 1002, 1003)
uv = redis_client.pfcount("uv:video:6")适合 UV 估算、访问人数统计。
10. GEO:附近的人 / 门店距离 / 地理位置查询
场景概述: GEO 适合附近位置查询和距离计算。
图形化展示:
text
geo:stores
store:1 ──► (116.397128, 39.916527)
store:2 ──► (116.410000, 39.920000)redis-cli 命令:
bash
redis-cli GEOADD geo:stores 116.397128 39.916527 store:1
redis-cli GEOSEARCH geo:stores FROMLONLAT 116.4 39.9 BYRADIUS 5 km WITHDIST
redis-cli GEODIST geo:stores store:1 store:2 kmPython 示例:
python
redis_client.geoadd("geo:stores", {"store:1": (116.397128, 39.916527)})适合附近门店、同城服务、地理围栏。
11. Pub/Sub:实时发布订阅 / 通知广播
场景概述: Pub/Sub 适合实时广播和轻量通知。
图形化展示:
text
PUBLISH config:changed
├── 订阅者 A
├── 订阅者 B
└── 订阅者 Credis-cli 命令:
bash
redis-cli SUBSCRIBE config:changed
redis-cli PUBLISH config:changed "video:max_workers"Python 示例:
python
redis_client.publish("config:changed", "video:max_workers")适合实时广播、配置变更通知。
12. RedisJSON:复杂 JSON 文档缓存
场景概述: RedisJSON 适合嵌套 JSON 文档和局部更新。
图形化展示:
text
json:task:6:config
{ model, ratio, frames[] }redis-cli 命令:
bash
redis-cli JSON.SET json:task:6:config $ '{"model":"seedance-2.0","ratio":"16:9"}'
redis-cli JSON.GET json:task:6:config $Python 示例:
python
redis_client.execute_command("JSON.SET", "json:task:6:config", "$", '{"model":"seedance-2.0"}')适合嵌套 JSON 配置、局部更新。
13. Bloom Filter:存在性预判 / 防缓存穿透
场景概述: Bloom Filter 适合做存在性预判和缓存穿透防护。
图形化展示:
text
bf:goods:ids
已加入: goods:1001, goods:1002, goods:1003redis-cli 命令:
bash
redis-cli BF.ADD bf:goods:ids goods:1001
redis-cli BF.EXISTS bf:goods:ids goods:1002Python 示例:
python
redis_client.execute_command("BF.ADD", "bf:goods:ids", "goods:1001")
exists = redis_client.execute_command("BF.EXISTS", "bf:goods:ids", "goods:1002")适合防缓存穿透、黑名单预判。
14. TimeSeries:监控指标 / 趋势数据
场景概述: TimeSeries 适合记录时间序列指标和趋势。
图形化展示:
text
ts:queue:task:length
1787796000000 -> 12
1787796060000 -> 18redis-cli 命令:
bash
redis-cli TS.ADD ts:queue:task:length * 12
redis-cli TS.RANGE ts:queue:task:length - +Python 示例:
python
redis_client.execute_command("TS.ADD", "ts:queue:task:length", int(time.time() * 1000), 12)适合队列长度、QPS、耗时趋势。
15. RedisSearch:全文检索 / 二级索引 / 多条件筛选
场景概述: RedisSearch 适合全文检索、索引和组合筛选。
图形化展示:
text
idx:tasks
├── title 可搜
├── stage 可筛选
└── user_id 可过滤redis-cli 命令:
bash
redis-cli FT.CREATE idx:tasks ON HASH PREFIX 1 task:search: SCHEMA title TEXT stage TAG user_id NUMERIC
redis-cli FT.SEARCH idx:tasks "@stage:{IMAGE} 海边"Python 示例:
python
redis_client.execute_command("FT.SEARCH", "idx:tasks", "@stage:{IMAGE} 海边")适合全文检索和组合筛选。
16. Vector Search:向量相似度检索
场景概述: Vector Search 适合相似检索和语义搜索。
图形化展示:
text
video:6 -> [0.12, 0.88, 0.31]
video:7 -> [0.10, 0.86, 0.35]
查询向量 q -> 找最相似的视频redis-cli 命令:
bash
redis-cli FT.CREATE idx:vec ON HASH PREFIX 1 video: SCHEMA embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 3 DISTANCE_METRIC COSINE
redis-cli FT.SEARCH idx:vec '*=>[KNN 3 @embedding $vec]' PARAMS 2 vec "..." SORTBY __vector_score DIALECT 2Python 示例:
python
redis_client.execute_command("FT.SEARCH", "idx:vec", "*=>[KNN 3 @embedding $vec]")适合相似视频推荐、语义搜索、RAG 检索。
17. Cuckoo Filter:可删除的存在性判断
场景概述: Cuckoo Filter 适合可删除的去重和存在性判断。
图形化展示:
text
cf:task:ids
CF.ADD task:6
CF.EXISTS task:6
CF.DEL task:6redis-cli 命令:
bash
redis-cli CF.ADD cf:task:ids task:6
redis-cli CF.EXISTS cf:task:ids task:6
redis-cli CF.DEL cf:task:ids task:6Python 示例:
python
redis_client.execute_command("CF.ADD", "cf:task:ids", "task:6")适合可删除去重、临时黑名单。
18. Count-Min Sketch:大规模频次估算
场景概述: Count-Min Sketch 适合大规模频次估算。
图形化展示:
text
cms:keywords
redis -> 12000
video -> 8500
worker -> 4300redis-cli 命令:
bash
redis-cli CMS.INCRBY cms:keywords redis 1 video 1 worker 1
redis-cli CMS.QUERY cms:keywords redis video workerPython 示例:
python
redis_client.execute_command("CMS.INCRBY", "cms:keywords", "redis", 1)适合热点词、频次趋势。
19. TopK:高频元素近似统计
场景概述: TopK 适合近似统计高频元素。
图形化展示:
text
topk:videos
video:8, video:6, video:3, video:12, video:1redis-cli 命令:
bash
redis-cli TOPK.RESERVE topk:videos 5
redis-cli TOPK.ADD topk:videos video:8 video:6 video:3
redis-cli TOPK.LIST topk:videosPython 示例:
python
redis_client.execute_command("TOPK.ADD", "topk:videos", "video:8", "video:6")适合热门视频、热门词榜单。
20. TDigest:分位数统计 / P95 / P99
场景概述: TDigest 适合分位数和延迟分布统计。
图形化展示:
text
td:video:duration
样本 -> P50 / P95 / P99redis-cli 命令:
bash
redis-cli TDIGEST.CREATE td:video:duration
redis-cli TDIGEST.ADD td:video:duration 2.1 2.4 3.0 8.2 15.6 30.1
redis-cli TDIGEST.QUANTILE td:video:duration 0.95Python 示例:
python
redis_client.execute_command("TDIGEST.ADD", "td:video:duration", 2.1, 2.4, 3.0)适合延迟分布、耗时分位数。
15. 使用场景中函数参数含义速查
第十章中的示例代码涉及较多 Redis 命令和 Python 辅助函数。阅读这些案例时,重点不是只记住函数名,而是理解每个参数对应 Redis 中的 key、value、field、score、member、offset、过期时间或消费组语义。
String / 通用 key 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.set(name, value, ex=None, nx=False) | set("task:6:status", "RUNNING", ex=900) | name 是 Redis key;value 是要保存的值;ex 是秒级过期时间;nx=True 表示 key 不存在时才写入,常用于分布式锁 |
redis_client.get(name) | get("task:6:status") | name 是要读取的 Redis key,返回该 key 当前保存的 value |
redis_client.incr(name, amount=1) | incr("video:6:view_count") | name 是计数器 key;amount 是递增步长,默认加 1 |
redis_client.expire(name, time) | expire(key, 60) | name 是要设置过期时间的 key;time 是过期秒数 |
redis_client.delete(*names) | delete(lock_key) | names 是一个或多个要删除的 key。分布式锁中不建议直接删除,容易误删别人的锁 |
redis_client.eval(script, numkeys, *keys_and_args) | eval(UNLOCK_SCRIPT, 1, lock_key, lock_token) | script 是 Lua 脚本;numkeys 表示后面有几个参数属于 Redis key;前 numkeys 个参数进入 Lua 的 KEYS;剩余参数进入 Lua 的 ARGV |
Hash 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.hset(name, key=None, value=None, mapping=None) | hset("task:6:progress", "stage", "IMAGE") | name 是 Hash key;key 是字段名 field;value 是字段值;mapping 可一次写入多个 field-value |
redis_client.hget(name, key) | hget(f"order:{oid}", "status") | name 是 Hash key;key 是要读取的字段名 |
redis_client.hgetall(name) | hgetall("task:6:progress") | name 是 Hash key,返回这个 Hash 下所有 field-value |
redis_client.hincrby(name, key, amount=1) | hincrby("cart:user:1001", "goods:2001", 1) | name 是 Hash key;key 是字段名;amount 是该字段的整数增量 |
List 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.lpush(name, *values) | lpush("task:queue", 6) | name 是 List key;values 是要从左侧压入的一个或多个元素 |
redis_client.brpop(keys, timeout=0) | brpop("task:queue", timeout=5) | keys 是一个 key 或多个 key;timeout 是最大阻塞等待秒数,0 表示一直等待 |
redis_client.lrange(name, start, end) | lrange("user:1001:feeds", 0, 19) | name 是 List key;start 是起始下标;end 是结束下标,包含该位置,-1 表示最后一个元素 |
redis_client.ltrim(name, start, end) | ltrim("user:1001:feeds", 0, 19) | name 是 List key;只保留 start 到 end 范围内的元素,常用于限制最新列表长度 |
redis_client.lpop(name) | lpop("undo:user:1001") | name 是 List key,从左侧弹出一个元素 |
redis_client.llen(name) | llen("task:queue") | name 是 List key,返回列表长度 |
Set 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.sadd(name, *values) | sadd("video:6:liked_users", 1001) | name 是 Set key;values 是要加入集合的成员,重复成员会自动去重 |
redis_client.sismember(name, value) | sismember("video:6:liked_users", 1001) | name 是 Set key;value 是要判断是否存在的成员 |
redis_client.scard(name) | scard("video:6:liked_users") | name 是 Set key,返回集合成员数量 |
redis_client.sinter(keys, *args) | sinter("user:1:follows", "user:2:follows") | 参数是多个 Set key,返回这些集合的交集 |
redis_client.srandmember(name, number=None) | srandmember("lottery:202608:users", 2) | name 是 Set key;number 是随机返回的成员数量 |
ZSet 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.zadd(name, mapping) | zadd("delay:order:cancel", {order_id: expire_at}) | name 是 ZSet key;mapping 是 {member: score} 字典,member 是成员,score 是排序分数 |
redis_client.zincrby(name, amount, value) | zincrby("rank:video:hot", 1, "video:6") | name 是 ZSet key;amount 是分数增量;value 是要增加分数的成员 |
redis_client.zrevrange(name, start, end, withscores=False) | zrevrange("rank:video:hot", 0, 9, withscores=True) | name 是 ZSet key;start/end 是排名范围;withscores=True 表示同时返回 score |
redis_client.zrevrank(name, value) | zrevrank("rank:video:hot", "video:6") | name 是 ZSet key;value 是成员,返回从高到低排序时的名次 |
redis_client.zrangebyscore(name, min, max, start=None, num=None) | zrangebyscore("delay:tasks", 0, now, start=0, num=10) | name 是 ZSet key;min/max 是 score 范围;start 是偏移量;num 是最多返回数量 |
redis_client.zrem(name, *values) | zrem("delay:order:cancel", oid) | name 是 ZSet key;values 是要删除的成员。多 Worker 场景中,谁 ZREM 成功谁获得处理权 |
Stream 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.xadd(name, fields, id="*") | xadd("stream:task", {"task_id": "6"}) | name 是 Stream key;fields 是消息字段字典;id="*" 表示 Redis 自动生成消息 ID |
redis_client.xgroup_create(name, groupname, id="0", mkstream=False) | xgroup_create("stream:task", "video-workers", id="0", mkstream=True) | name 是 Stream key;groupname 是消费组名;id 表示从哪个消息 ID 开始消费;mkstream=True 表示 Stream 不存在时自动创建 |
redis_client.xreadgroup(groupname, consumername, streams, count=None, block=None) | xreadgroup("video-workers", "worker-1", {"stream:task": ">"}, count=10, block=5000) | groupname 是消费组;consumername 是消费者名;streams 指定要读的 Stream 和起始 ID;count 是最多读取条数;block 是阻塞等待毫秒数 |
redis_client.xack(name, groupname, *ids) | xack("stream:task", "video-workers", message_id) | name 是 Stream key;groupname 是消费组;ids 是要确认完成的消息 ID |
redis_client.xrange(name, min="-", max="+") | xrange("stream:video:event", min="-", max="+") | name 是 Stream key;min/max 是消息 ID 范围,- 表示最早,+ 表示最新 |
Bitmap / BitField 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.setbit(name, offset, value) | setbit("active:2026-08-27", 1001, 1) | name 是 Bitmap key;offset 是 bit 下标;value 只能是 0 或 1 |
redis_client.getbit(name, offset) | getbit("signin:2026-08:user:1001", day_index) | name 是 Bitmap key;offset 是要读取的 bit 下标 |
redis_client.bitcount(key) | bitcount("active:2026-08-27") | key 是 Bitmap key,返回值为 1 的 bit 数量 |
redis_client.bitop(operation, dest, *keys) | bitop("AND", "active:and", "active:day1", "active:day2") | operation 是位运算类型,如 AND/OR/XOR/NOT;dest 是结果 key;keys 是参与运算的源 key |
redis_client.bitfield(key, *args) | bitfield("score:user:1001:2026-08", "SET", "u4", 26 * 4, 9) | key 是底层字符串 key;args 是 BitField 子命令序列,例如 SET/GET、整数类型 u4、bit 偏移量和写入值 |
HyperLogLog / GEO / Pub/Sub 操作
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.pfadd(name, *values) | pfadd("uv:video:6", 1001, 1002, 1003) | name 是 HyperLogLog key;values 是要参与去重估算的元素 |
redis_client.pfcount(*sources) | pfcount("uv:video:6") | sources 是一个或多个 HyperLogLog key,返回不重复数量估算值 |
redis_client.pfmerge(dest, *sources) | pfmerge("uv:site:range", "uv:day1", "uv:day2") | dest 是合并后的目标 key;sources 是要合并的多个 HyperLogLog key |
redis_client.geoadd(name, values) | geoadd("geo:stores", [116.397128, 39.916527, "store:1"]) | name 是 GEO key;values 按经度、纬度、成员名传入 |
redis_client.geosearch(name, longitude=None, latitude=None, member=None, radius=None, unit="m") | geosearch("geo:stores", longitude=116.4, latitude=39.9, radius=5, unit="km") | name 是 GEO key;可用经纬度或已有成员作为中心点;radius 是半径;unit 是距离单位 |
redis_client.geodist(name, place1, place2, unit=None) | geodist("geo:stores", "store:1", "store:2", unit="km") | name 是 GEO key;place1/place2 是两个成员;unit 是返回距离单位 |
redis_client.publish(channel, message) | publish("config:changed", "video:max_workers") | channel 是频道名;message 是要发布的消息内容 |
redis_client.pubsub() | pubsub() | 创建一个 Pub/Sub 订阅对象,后续可通过该对象订阅频道并读取消息 |
Redis Stack / 模块命令
| 函数 | 示例 | 参数含义 |
|---|---|---|
redis_client.execute_command("JSON.SET", key, path, value) | execute_command("JSON.SET", "json:task:6:config", ".", json_text) | 第 1 个参数是模块命令名;key 是 JSON key;path 是 JSON 路径;value 是要写入的 JSON 字符串 |
redis_client.execute_command("JSON.GET", key, path) | execute_command("JSON.GET", "json:task:6:config", ".") | key 是 JSON key;path 是要读取的 JSON 路径 |
redis_client.execute_command("BF.ADD", key, item) | execute_command("BF.ADD", "bf:goods:ids", "goods:2001") | key 是 Bloom Filter key;item 是要加入过滤器的元素 |
redis_client.execute_command("BF.EXISTS", key, item) | execute_command("BF.EXISTS", "bf:goods:ids", "goods:9999") | key 是 Bloom Filter key;item 是要判断是否可能存在的元素 |
redis_client.execute_command("TS.ADD", key, timestamp, value) | execute_command("TS.ADD", "ts:queue:task:length", ts, queue_length) | key 是时间序列 key;timestamp 是毫秒时间戳;value 是指标值 |
redis_client.execute_command("TS.RANGE", key, from_ts, to_ts, ...) | execute_command("TS.RANGE", "ts:queue:task:length", "-", "+") | key 是时间序列 key;from_ts/to_ts 是时间范围,- 到 + 表示全量范围 |
redis_client.execute_command("FT.CREATE", index, ...) | execute_command("FT.CREATE", "idx:task", "ON", "HASH", ...) | index 是索引名;后续参数定义索引数据来源、前缀、字段类型和排序规则 |
redis_client.execute_command("FT.SEARCH", index, query, ...) | execute_command("FT.SEARCH", "idx:task", "@stage:{IMAGE}") | index 是索引名;query 是查询表达式;后续参数可指定排序、分页、返回字段等 |
redis_client.execute_command("CF.ADD", key, item) | execute_command("CF.ADD", "cf:task:ids", "task:6") | key 是 Cuckoo Filter key;item 是要加入的元素 |
redis_client.execute_command("CF.EXISTS", key, item) | execute_command("CF.EXISTS", "cf:task:ids", "task:6") | key 是 Cuckoo Filter key;item 是要判断是否可能存在的元素 |
redis_client.execute_command("CF.DEL", key, item) | execute_command("CF.DEL", "cf:task:ids", "task:6") | key 是 Cuckoo Filter key;item 是要从过滤器中删除的元素 |
redis_client.execute_command("CMS.INCRBY", key, item, increment) | execute_command("CMS.INCRBY", "cms:keywords", "redis", 3) | key 是 Count-Min Sketch key;item 是统计对象;increment 是增加次数 |
redis_client.execute_command("CMS.QUERY", key, *items) | execute_command("CMS.QUERY", "cms:keywords", "video", "redis") | key 是 Count-Min Sketch key;items 是要查询频次的多个元素 |
redis_client.execute_command("TOPK.RESERVE", key, k, width, depth, decay) | execute_command("TOPK.RESERVE", "topk:videos", 10, 2000, 7, 0.9) | key 是 TopK key;k 是保留前多少名;width/depth/decay 控制近似统计的空间、精度和衰减策略 |
redis_client.execute_command("TOPK.ADD", key, *items) | execute_command("TOPK.ADD", "topk:videos", "video:1") | key 是 TopK key;items 是要计入高频统计的元素 |
redis_client.execute_command("TOPK.LIST", key) | execute_command("TOPK.LIST", "topk:videos") | key 是 TopK key,返回当前近似 TopK 列表 |
redis_client.execute_command("TDIGEST.CREATE", key) | execute_command("TDIGEST.CREATE", "td:video:duration") | key 是 TDigest key,用于保存分位数统计结构 |
redis_client.execute_command("TDIGEST.ADD", key, value) | execute_command("TDIGEST.ADD", "td:video:duration", 12.5) | key 是 TDigest key;value 是要加入统计的数值样本 |
redis_client.execute_command("TDIGEST.QUANTILE", key, quantile) | execute_command("TDIGEST.QUANTILE", "td:video:duration", 0.95) | key 是 TDigest key;quantile 是分位点,例如 0.95 表示 P95 |
Python 辅助函数
| 函数 | 示例 | 参数含义 |
|---|---|---|
time.time() | int(time.time()) + 300 | 不需要参数,返回当前 Unix 时间戳,单位是秒,常用于计算延迟任务执行时间 |
int(value) | int(time.time()) | value 是要转换成整数的值,常用于把时间戳或 Redis 返回的字符串任务 ID 转成整数 |
str(value) | str(uuid.uuid4()) | value 是要转换成字符串的对象,分布式锁中用于把 UUID 转成可保存的 token 字符串 |
uuid.uuid4() | uuid.uuid4() | 不需要参数,生成随机 UUID,适合作为分布式锁的唯一 lock_token |
16. 本项目中的推荐使用方式
结合当前视频任务系统,可以这样选择:
| 业务数据 | 推荐结构 | 原因 |
|---|---|---|
| 待处理任务队列 | List | LPUSH + BRPOP 简单、高效,适合 Worker 阻塞消费 |
| 任务执行进度 | Hash | 可以单独更新 stage、percent、message 等字段 |
| 任务状态缓存 | String / Hash | 简单状态用 String,多字段状态用 Hash |
| 用户提交频率限制 | String | 使用 INCR + EXPIRE 实现限流计数 |
| 视频播放量 | String | 使用 INCR 做计数器 |
| 视频点赞用户 | Set | 自动去重,方便判断用户是否点过赞 |
| 视频热度排行 | ZSet | 用热度分或播放量作为 score 排序 |
| 延迟重试任务 | ZSet / Stream | 简单延迟用 ZSet,需要 ACK 和恢复用 Stream |
| 可靠事件日志 | Stream | 如果后续需要 ACK、重试、消费组,可从 List 升级到 Stream |
| 在线配置广播 | Pub/Sub | 通知在线服务刷新配置,消息不需要持久化 |
| 队列积压趋势 | TimeSeries / 普通指标系统 | 如果需要趋势图和聚合,可以记录时间序列 |
| 防止无效 ID 穿透数据库 | Bloom Filter | 大规模 ID 存在性预判,减少无效 DB 查询 |
17. 选型口诀表
| 需求关键词 | 优先选择 | 典型命令 | 示例场景 | 注意点 |
|---|---|---|---|---|
| 一个 key 存一个完整值 | String | SET、GET、EXPIRE | 验证码、登录 token、任务状态缓存 | 适合整体读写,不适合频繁局部更新对象字段 |
| 原子计数、自增自减 | String | INCR、DECR、INCRBY | 播放量、点赞数、接口调用次数 | 计数器可以配合 EXPIRE 做限流窗口 |
| 一个对象有多个字段 | Hash | HSET、HGET、HGETALL | 用户资料、任务进度、视频元信息 | 适合字段级更新,不适合按字段值排序 |
| 简单队列、阻塞消费 | List | LPUSH、BRPOP | 视频任务队列、异步任务分发 | 适合简单队列,不支持 ACK 和消费组 |
| 最新列表、操作历史 | List | LPUSH、LRANGE、LTRIM | 最新动态、最近操作、撤销栈 | 要配合 LTRIM 控制列表长度 |
| 去重、判断是否存在 | Set | SADD、SISMEMBER、SCARD | 点赞用户、抽奖参与用户、黑名单 | 无序集合,不适合按时间或分数排序 |
| 集合关系运算 | Set | SINTER、SUNION、SDIFF | 共同关注、共同标签、权限对比 | 数据量很大时要注意集合运算成本 |
| 排名、权重、分数排序 | ZSet | ZADD、ZREVRANGE、ZREVRANK | 视频热榜、用户积分榜、优先级队列 | score 是排序核心,更新频繁时要评估写入成本 |
| 时间戳排序、延迟执行 | ZSet | ZADD、ZRANGEBYSCORE、ZREM | 延迟任务、最近访问、定时重试 | 多 Worker 取任务时要用 ZREM 或 Lua 保证不重复执行 |
| 可靠消息、消费组、ACK | Stream | XADD、XREADGROUP、XACK | 可靠任务队列、订单事件、视频生成事件流 | 比 List 复杂,但支持 pending、ACK 和故障恢复 |
| 大量是/否状态 | Bitmap | SETBIT、GETBIT、BITCOUNT | 用户签到、日活统计、功能灰度 | offset 要可控,稀疏大 ID 可能浪费空间 |
| 多个小整数状态压缩 | BitField | BITFIELD | 每日评分、设备状态档位、小型状态机 | 可读性较差,适合内存极度敏感场景 |
| 只统计不重复数量,不关心成员 | HyperLogLog | PFADD、PFCOUNT、PFMERGE | 网站 UV、视频 UV、活动访问人数 | 结果是估算值,不能列出具体成员 |
| 经纬度附近查询 | GEO | GEOADD、GEOSEARCH、GEODIST | 附近门店、附近设备、同城资源 | 适合基础附近查询,不替代专业 GIS |
| 在线实时广播 | Pub/Sub | PUBLISH、SUBSCRIBE | 配置变更通知、WebSocket 网关广播 | 消息不持久化,订阅者离线会丢消息 |
| 复杂 JSON 局部更新 | RedisJSON | JSON.SET、JSON.GET | 复杂配置、用户画像、商品详情缓存 | 需要 RedisJSON 模块支持,标准 Redis 默认没有 |
| 大规模存在性预判 | Bloom Filter | BF.ADD、BF.EXISTS | 防缓存穿透、URL 去重、风险名单预判 | 有误判率,不能替代精确集合 |
| 指标趋势和时间范围查询 | TimeSeries | TS.ADD、TS.RANGE | 队列长度趋势、接口 QPS、任务耗时监控 | 需要 RedisTimeSeries 模块支持,也可交给专业监控系统 |