Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

中间件 / Middleware

1. Web 中间件全景 / The Web Middleware Landscape

2. 中间件——流量洪峰下的系统保护 / Middleware for Protecting Systems Under Traffic Spikes

cache layer

1. 缓存架构全景 / Cache Architecture Overview

2. Redis 深入——为什么你的缓存总是出问题 / Redis Internals and Cache Failure Modes

3. Redis 数据结构场景应用——什么时候用什么 / Choosing Redis Data Structures for Real Applications

4. 缓存策略——库存变了,缓存怎么处理 / Cache Strategies and Inventory Consistency

5. Redis 缓存三剑客——穿透/击穿/雪崩 + 一致性策略 / Redis Cache Penetration, Breakdown, Avalanche, and Consistency

6. Redis 分布式锁——从 SETNX 到 Redisson / Redis Distributed Locks from SETNX to Redisson

7. Redis Cluster 与 Sentinel 高可用架构 / Redis Cluster and Sentinel High-Availability Architecture

8. Redis 高级特性——Stream / PubSub / Module / LLM 应用

9. Redis 架构深度分析——为什么 Redis 能这么快 / Redis Architecture and the Sources of Its Performance

10. Redis 全景——为什么你的系统需要一个缓存层 / The Redis Landscape and Why Systems Need a Cache Layer

message queue

1. 消息队列全景——为什么你的系统需要一个中间人 / Message Queue Overview and Why Your System Needs a Middleman

2. Kafka 核心——为什么你的消息总是"丢"了 / Kafka Fundamentals and Message Delivery Semantics

3. 消息队列高级——死信队列、延迟消息、消息积压 / Advanced Messaging with Dead Letters, Delays, and Backlogs

4. Kafka Streams 与 Connect —— 让数据自己流动起来 / Kafka Streams and Connect for Streaming Data Pipelines

5. RabbitMQ 深度解析 —— 灵活路由与消息可靠性 / RabbitMQ Deep Dive into Flexible Routing and Reliability

6. 消息队列对比——为什么最终选了 Kafka / Comparing Message Queues and Choosing Kafka

search engine

1. 搜索引擎知识体系 / Search Engine Knowledge System

2. Elasticsearch——为什么 Like 查询总是那么慢 / Elasticsearch for Full-Text Search at Scale

3. Elasticsearch 查询 DSL 深入——为什么你的搜索总是不准 / Elasticsearch Query DSL Deep Dive

4. Elasticsearch 集群规划与运维——为什么你的集群总是"黄" / Elasticsearch Cluster Planning and Operations

5. Meilisearch 与轻量搜索替代方案——当 ES 太重时 / Meilisearch and Lightweight Search Alternatives

infrastructure

1. 基础设施组件全景 / Infrastructure Components Landscape

2. Nginx 与反向代理 / Nginx and Reverse Proxy

3. 服务发现 / Service Discovery

4. 配置中心 / Configuration Center

本页目录

Redis 数据结构场景应用——什么时候用什么 / Choosing Redis Data Structures for Real Applications ​

📅 创建时间:2026-06-02 🏷️ 标签:#Redis #数据结构 #SDS #跳表 #Hash #List #Set #ZSet #场景决策 📚 前置知识:[[00-redis-overview]](Redis 全景) [[01-redis-architecture]](架构分析) [[/03-web/06-databases-and-data-access/05-redis]](Redis 命令速查) 📚 相关知识:[[04-redis-distributed-locks]](分布式锁) [[05-redis-advanced-features]](HyperLogLog / Bitmap / Stream)


场景:同样存用户信息,为什么有人用 String,有人用 Hash ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  工程师 A:HSET user:1001 name "张三" age "28"           │
│  工程师 B:SET user:1001 "{\"name\":\"张三\",\"age\":\"28\"}" │
│                                                             │
│  两个方案都能工作,但:                                      │
│  • Hash 可以只取 name,不用解析整个 JSON                   │
│  • String 在批量读取所有字段时只有 1 次 IO                │
│  • Hash 支持 field 级别的过期吗?(不支持)                 │
│                                                             │
│  不同的数据结构有不同的使用边界,                         │
│  选错了会导致代码难维护或者性能差。                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

第1节:五种核心数据结构的场景决策 ​

String——什么时候用它 ​

┌─────────────────────────────────────────────────────────────┐
│                 String 的最佳使用场景                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  String(SDS)最适合的场景:                                │
│                                                             │
│  1. 简单 KV:缓存单个值                                    │
│     product:1001 = '{"id":1001,"name":"iPhone","price":6999}'
│     → 直接序列化/反序列化,不需要 field 级操作             │
│                                                             │
│  2. 计数器                                                 │
│     INCR order:count         # 原子 +1                      │
│     INCRBY product:1001:views 100   # 批量增加浏览量       │
│     DECRBY stock:1001 1      # 原子 -1,扣库存              │
│     → 原子操作,不用担心并发问题                          │
│                                                             │
│  3. 分布式锁值                                             │
│     SET lock:order:123 "worker_001" NX EX 30             │
│     → value 存的是持有者的唯一标识(UUID)                  │
│                                                             │
│  4. Session Token                                          │
│     SET session:abc123 {user_id:1001} EX 7200             │
│     → 简单会话存储,不需要 field 级别操作                  │
│                                                             │
│  String 不适合的场景:                                      │
│  ❌ 需要按字段查询:user.name,而不是整个 user 对象        │
│  ❌ 字段经常部分更新:只改 age,不重序列化整个对象         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Hash——什么时候用它 ​

┌─────────────────────────────────────────────────────────────┐
│                 Hash 的最佳使用场景                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Hash 适合存"对象",特别是需要按字段读写的场景:           │
│                                                             │
│  1. 用户/商品等业务对象(字段经常部分更新)                 │
│     HSET user:1001 name "张三" age "28" city "北京"       │
│     HSET user:1001 age "29"         # 只改 age,不动其他  │
│     HGET user:1001 name             # 只读 name,一次 IO   │
│     → 相比 String(序列化 JSON),field 级操作更灵活       │
│                                                             │
│  2. 配置缓存                                               │
│     HSET config:system max_connections 1000                 │
│     HINCRBY config:system online_users 1                   │
│     HGETALL config:system                                │
│     → 配置项按 key 分组,方便管理                         │
│                                                             │
│  3. 购物车                                                 │
│     HSET cart:1001 product:001 '{"name":"iPhone","num":1}'│
│     HSET cart:1001 product:002 '{"name":"Mac","num":1}'    │
│     HINCRBY cart:1001 product:001 1   # 加一件 iPhone      │
│     → 每个商品独立字段,增量更新方便                      │
│                                                             │
│  ⚠️ Hash 的注意事项:                                      │
│  • 过期时间对整个 key 生效,不支持单 field 过期           │
│  • field 数量过多(超过 500)时考虑拆分成多个 Hash        │
│  • 小数据量时用 ziplist 编码(节省内存),大数据用 hashtable│
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

List——什么时候用它 ​

┌─────────────────────────────────────────────────────────────┐
│                 List 的最佳使用场景                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  List(双向链表)适合"有序、可重复、有进有出"的场景:      │
│                                                             │
│  1. 消息队列(简单场景,不需要消费者组)                   │
│     LPUSH task:queue '{"job_id":1,"type":"send_email"}'   │
│     BRPOP task:queue 0                                    │
│     → LPUSH 生产,BRPOP 消费(FIFO 队列)                 │
│     → BRPOP 阻塞等待,有新消息才返回                       │
│     → 比 Kafka 轻量,适合小规模异步任务                   │
│                                                             │
│  2. 最新消息列表(时间线)                                 │
│     LPUSH timeline:1001 "用户 1001 发布了一条动态..."     │
│     LTRIM timeline:1001 0 99      # 只保留最新 100 条    │
│     LRANGE timeline:1001 0 19     # 取最新 20 条          │
│     → 微博/Twitter 时间线就是这样的结构                    │
│                                                             │
│  3. 最新浏览记录                                           │
│     LPUSH history:1001 "product:001"                     │
│     LTRIM history:1001 0 49        # 只保留 50 条         │
│     LRANGE history:1001 0 9       # 显示最近 10 条        │
│     → 每个用户独立的浏览历史                              │
│                                                             │
│  ⚠️ List 的注意事项:                                     │
│  • BRPOP 多个 key:BRPOP key1 key2 key3 0(哪个有消息读哪个)│
│  • 消费者崩溃:消息被消费但未处理 → 丢失                 │
│     解法:用 Stream 的 ACK 机制                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Set——什么时候用它 ​

┌─────────────────────────────────────────────────────────────┐
│                 Set 的最佳使用场景                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Set(无序集合,自动去重)适合"不重复、无序、快速判断存在":│
│                                                             │
│  1. 标签系统                                              │
│     SADD product:001:tags "手机" "5G" "拍照"             │
│     SISMEMBER product:001:tags "5G"   # → 1(存在)       │
│     SINTER product:tags:手机 product:tags:5G              │
│     → 找同时有"手机"和"5G"标签的商品                      │
│     → 集合交集运算                                        │
│                                                             │
│  2. 关注关系/好友列表                                     │
│     SADD user:1001:followings 2001 2002 2003             │
│     SADD user:2001:followers 1001                       │
│     SINTER user:1001:followings user:2001:followings     │
│     → 互相关注(共同关注)                               │
│                                                             │
│  3. UV 统计(去重计数)                                   │
│     SADD page:view:2026-06-02 "user_001" "user_002"     │
│     SCARD page:view:2026-06-02    # → 2(独立访客数)   │
│     → 缺点:用户量大了内存开销大(用 HyperLogLog 替代)   │
│                                                             │
│  4. 黑名单/白名单                                         │
│     SADD blacklist:ip "1.2.3.4" "5.6.7.8"                │
│     SISMEMBER blacklist:ip "1.2.3.4"   # → 1(在黑名单)  │
│     → O(1) 判断是否存在,比数据库快很多                   │
│                                                             │
│  ⚠️ Set vs Hash:                                        │
│  • Set 存的是 member 本身,Hash 存的是 field-value 对   │
│  • 需要交集/并集/差集 → 用 Set                          │
│  • 需要按字段读写 → 用 Hash                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

ZSet——什么时候用它 ​

┌─────────────────────────────────────────────────────────────┐
│                 ZSet 的最佳使用场景                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ZSet(有序集合,自动按 score 排序)适合"带权重的排行榜":  │
│                                                             │
│  1. 实时排行榜                                             │
│     ZADD leaderboard:posts 150 "post_001"                 │
│     ZADD leaderboard:posts  89 "post_002"                 │
│     ZREVRANGE leaderboard:posts 0 9 WITHSCORES            │
│     → 查 Top 10,按分数降序                               │
│     ZINCRBY leaderboard:posts 1 "post_001"              │
│     → 增量更新分数,不用重新计算所有排名                  │
│                                                             │
│  2. 延迟队列                                               │
│     ZADD delay:queue {now+30s} "order:123:timeout"      │
│     ZRANGEBYSCORE delay:queue 0 {now} WITHSCORES         │
│     → 取所有已到期的延迟任务                               │
│     ZREMRANGEBYSCORE delay:queue 0 {now}                │
│     → 删除已处理的任务                                    │
│     → 订单 30 分钟未支付 → 自动取消                      │
│                                                             │
│  3. 带权重的优先级队列                                     │
│     ZADD priority:tasks 10 "task:001"    # 优先级 10 高  │
│     ZADD priority:tasks  5 "task:002"    # 优先级 5 低   │
│     ZRANGE priority:tasks 0 0             # 取最高优先级   │
│     → 任务调度系统,高优先级任务先执行                    │
│                                                             │
│  4. 滑动窗口限流                                          │
│     ZADD ratelimit:user:1001 {timestamp} {request_id}   │
│     ZREMRANGEBYSCORE ratelimit:user:1001 0 {now-60s}    │
│     ZCARD ratelimit:user:1001                          │
│     → 60 秒内超过阈值 → 限流                             │
│     → 比 String 计数器更精确(可精确到毫秒级时间窗口)    │
│                                                             │
│  ZSet vs List:                                           │
│  • List:按插入顺序排序,时间顺序                        │
│  • ZSet:按 score(分数)排序,业务权重                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

第2节:五种数据结构的决策矩阵 ​

┌─────────────────────────────────────────────────────────────┐
│              数据结构选择决策矩阵                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────┬────────────┬───────────┬─────────────────┐ │
│  │ 数据结构      │ 场景       │ 优势       │ 忌讳场景         │ │
│  ├──────────────┼────────────┼───────────┼─────────────────┤ │
│  │ String       │ KV、计数器 │ 原子操作   │ 频繁按字段更新   │ │
│  │              │ 锁值       │ 简单直接   │                 │ │
│  ├──────────────┼────────────┼───────────┼─────────────────┤ │
│  │ Hash         │ 业务对象   │ field 级  │ 集合运算(交并差)│ │
│  │              │ 配置缓存   │ 读写      │ 大集合计数       │ │
│  ├──────────────┼────────────┼───────────┼─────────────────┤ │
│  │ List         │ 队列      │ FIFO 有序  │ 需要消费者组/ACK  │ │
│  │              │ 时间线    │ 头尾操作   │ 需要随机访问     │ │
│  ├──────────────┼────────────┼───────────┼─────────────────┤ │
│  │ Set          │ 标签      │ 交并差运算 │ 需要按 score 排序 │ │
│  │              │ 去重计数  │ O(1) 存在  │ 有序集合        │ │
│  ├──────────────┼────────────┼───────────┼─────────────────┤ │
│  │ ZSet         │ 排行榜    │ 按权重排序 │ 只需要去重,不需排序│
│  │              │ 延迟队列  │ 范围查询   │                 │ │
│  │              │ 限流      │ O(log N)  │                 │ │
│  └──────────────┴────────────┴───────────┴─────────────────┘ │
│                                                             │
│  决策口诀:                                                │
│  简单值 → String                                           │
│  对象字段 → Hash                                           │
│  有序列表 → List(按时间)/ ZSet(按权重)                │
│  快速查找 → Set(存在判断)/ ZSet(按权重范围)           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

第3节:进阶数据结构的场景应用 ​

HyperLogLog——海量 UV 统计 ​

┌─────────────────────────────────────────────────────────────┐
│                 HyperLogLog:用 12KB 统计百万 UV              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  场景:统计每天有多少独立用户访问了你的网站                 │
│                                                             │
│  方案 A:Set(SADD + SCARD)                              │
│  • 100 万用户 = 100 万个 member → 占用 ~100MB 内存        │
│  • 精确计数                                               │
│                                                             │
│  方案 B:HyperLogLog                                       │
│  • 固定 12KB 内存                                        │
│  • 标准误差 ~0.81%                                        │
│  • 适合统计"大概有多少个"而非精确数字                       │
│                                                             │
│  适用场景:                                               │
│  • DAU(日活跃用户):每天一个 HyperLogLog key             │
│  • 页面 UV:每个页面每天一个 key                           │
│  • 接口调用量(去重):                                    │
│  PFADD api:hits:2026-06-02 "user:1001" "user:1002"       │
│  PFCOUNT api:hits:2026-06-02                            │
│                                                             │
│  不适用场景:                                             │
│  ❌ 需要精确去重(如订单数、支付数)→ 用 String 计数器     │
│  ❌ 用户量 < 1 万 → 直接用 Set 更精确                     │
│                                                             │
│  合并统计:PFMERGE week:uv day1 day2 day3 → 合并多天 UV  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

Bitmap——位图操作 ​

┌─────────────────────────────────────────────────────────────┐
│                 Bitmap:用 bit 位标记状态                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  原理:每个 bit 可以存 0 或 1,1 个字节 = 8 个 bit        │
│                                                             │
│  场景 1:用户签到(一年 = 365 bits = 46 bytes)           │
│                                                             │
│  # 2026 年用户 1001 的签到情况(偏移 = 月-1 × 31 + 日-1)│
│  SETBIT user:1001:signin:2026 $((6-1)*31+15) 1   # 6月15日│
│  GETBIT user:1001:signin:2026 $((6-1)*31+15)    # → 1    │
│  BITCOUNT user:1001:signin:2026                    # 总签到天数│
│  BITOP AND last7days day1 day2 day3 ... day7      # 近7天全签到│
│                                                             │
│  场景 2:在线状态(1 亿用户 = 100MB)                      │
│                                                             │
│  # bit 位表示用户 ID(用户 3 在线)                        │
│  SETBIT online:users 3 1                                  │
│  # 统计当前在线人数                                        │
│  BITCOUNT online:users                                    │
│  # 用户 3 是否在线                                         │
│  GETBIT online:users 3                                    │
│                                                             │
│  场景 3:权限位图                                          │
│                                                             │
│  # 8 个 bit 表示 8 种权限,第 0 位=读,第 1 位=写...      │
│  SET user:1001:permissions 5    # 0101 = 有读+权限管理权限│
│  GETBIT user:1001:permissions 1   # → 0(无写权限)      │
│                                                             │
│  一句话:需要"大量布尔状态 + 空间敏感" → Bitmap           │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Stream——真正可靠的消息队列 ​

┌─────────────────────────────────────────────────────────────┐
│                 Stream:Redis 原生消息队列                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  为什么需要 Stream?List 做队列有致命缺陷:                 │
│  • 消费者崩溃:消息被 BRPOP 拿走,但没处理完 → 消息丢失    │
│  • 多个消费者:无法让多个消费者各自拿到完整消息            │
│                                                             │
│  Stream 的改进:                                          │
│  1. 消息 ID(自动递增),可追踪                          │
│  2. 消费者组:多个消费者协作,每条消息只被一个消费者处理   │
│  3. ACK 确认:消息处理完后显式确认,未确认会重投          │
│  4. 持久化:存在 Redis 中,重启不丢                       │
│                                                             │
│  场景:订单超时处理                                        │
│                                                             │
│  # 1. 创建消费组(一次性)                                │
│  XGROUP CREATE orders:pending mygroup $ MKSTREAM          │
│                                                             │
│  # 2. 生产者:创建订单时写入 Stream                       │
│  XADD orders:pending * order_id "12345" amount "6999"    │
│                                                             │
│  # 3. 消费者:抢单                                       │
│  XREADGROUP GROUP mygroup consumer1 STREAMS orders:pending ">"│
│  # → 返回新消息,">" 表示只取未分配的消息                 │
│                                                             │
│  # 4. 消费者:处理完后 ACK                               │
│  XACK orders:pending mygroup 1700000000000-0             │
│                                                             │
│  # 5. 监控:查看未确认消息(超时未 ACK → 重投)           │
│  XPENDING orders:pending mygroup                         │
│                                                             │
│  适用场景:轻量级异步任务(不需要 Kafka 的吞吐量时)        │
│  不适用:需要极高吞吐量(100 万+/s)或消息持久化到磁盘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

第4节:编码转换——Redis 怎么自动优化存储 ​

┌─────────────────────────────────────────────────────────────┐
│                 Redis 内部编码转换                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Redis 会根据数据量自动选择最优编码,不需要手动干预:        │
│                                                             │
│  String:                                                  │
│  • embstr(< 39 字节):连续的内存块,1 次分配             │
│  • raw(≥ 39 字节):SDS 单独分配                        │
│  • int:纯整数,用 8 字节存储                             │
│                                                             │
│  Hash / List / Set / ZSet:                              │
│  • 小数据量:ziplist / intset(紧凑结构,节省内存)        │
│  • 大数据量:hashtable / skiplist / quicklist(高效操作) │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐ │
│  │  ziplist → hashtable  的触发条件(Hash):          │ │
│  │                                                     │ │
│  │  hash-max-ziplist-entries = 512                    │ │
│  │    → field 数量超过 512 → 转为 hashtable          │ │
│  │  hash-max-ziplist-value = 64                      │ │
│  │    → 单个 field value 超过 64 字节 → 转为 hashtable│ │
│  └─────────────────────────────────────────────────────┘ │
│                                                             │
│  ⚠️ 注意:ziplist 查询是 O(N),hashtable 是 O(1)        │
│  频繁按 field 查询的场景,避免 Hash 退化成 ziplist        │
│  (field 数量过多时反而影响性能)                         │
│                                                             │
│  查看编码:OBJECT ENCODING user:1001                       │
│  查看内部信息:DEBUG OBJECT user:1001                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

升华:数据结构选择的本质 ​

┌─────────────────────────────────────────────────────────────┐
│              选择数据结构的本质:根据你的操作选结构            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  核心问题只有三个:                                         │
│                                                             │
│  1. 数据要不要排序?                                       │
│     不排序 → Set                                           │
│     按权重/分数排序 → ZSet                                 │
│     按插入顺序 → List                                      │
│                                                             │
│  2. 数据要不要按字段读写?                                 │
│     整存整取(JSON) → String                             │
│     按字段读写 → Hash                                      │
│                                                             │
│  3. 操作是什么类型?                                       │
│     交并差运算 → Set                                       │
│     原子计数 → String(INCR/DECR)                       │
│     时间序列 → List                                        │
│     排行榜/权重队列 → ZSet                                │
│                                                             │
│  反面教材:                                                │
│  ❌ 用 ZSet 存用户对象(只为了按字段排序)                │
│  ❌ 用 String 存大型 JSON,每改一个字段要反序列化+序列化  │
│  ❌ 用 List 做排行榜(每次要LRANGE 全部再排序)           │
│                                                             │
│  正确做法:                                               │
│  先想清楚"我要对这个数据做什么操作",                     │
│  再根据操作类型选择最合适的数据结构。                     │
│  数据结构选对了,代码自然简单,性能自然好。               │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

"AI 可查 vs 必须理解"清单 ​

AI 可查:
✅ Redis 各数据结构的命令速查
✅ ziplist / intset / quicklist 的编码阈值配置
✅ DEBUG OBJECT / OBJECT ENCODING 的具体用法

必须理解:
🔴 什么时候用 String vs Hash(整存 vs field 级操作)
🔴 什么时候用 List vs ZSet(时间顺序 vs 权重排序)
🔴 HyperLogLog 的适用场景(海量 UV 统计)和不适用场景(精确计数)
🔴 Bitmap 的核心优势(位级操作,空间极省)和典型场景(签到/在线状态)
🔴 Stream 和 List 作为队列的区别(ACK 确认 vs 无确认)
🔴 Redis 内部编码转换的触发条件(entries 数量 / value 大小)
1
2
3
4
5
6
7
8
9
10
11
12

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇2. Redis 深入——为什么你的缓存总是出问题 / Redis Internals and Cache Failure Modes
下一篇4. 缓存策略——库存变了,缓存怎么处理 / Cache Strategies and Inventory Consistency

持续记录,持续成长

Copyright © Tidenflow