SuperAPI AI Directory

redis

Data & analytics

View repository

Updated 10-15 21:48

About this skill

Redis — In-Memory Data Structure Store

Redis (Remote Dictionary Server) is an open-source in-memory data structure store, used as a database, cache, and message broker.

Workflow — Usage Flow

遇到 Redis 相关需求时,按以下顺序决策:

Step 1: 明确场景
├── 缓存加速?     → 转到 Step 2
├── 数据结构存储? → 转到 Step 2
├── 消息队列?     → 转到 1.7 Stream 章节
├── 分布式锁?     → 转到 2.4 分布式锁
├── 高可用/集群?  → 转到 4. 高可用架构
└── 性能问题?     → 转到 6. 性能优化

Step 2: Choose Data Structure ├── Simple key-value / Counters / Session → String ├── Object storage (multiple fields) → Hash ├── Queue / Timeline / Logs → List ├── Tags / Social relationships / Deduplication → Set ├── Leaderboards / Delayed queues / Range queries → ZSet ├── Check-ins / Online status / Bloom filtering → Bitmap ├── UV statistics / Deduplication counting → HyperLogLog └── Nearby people / Geofencing → Geo

Step 3: Determine Persistence Strategy ├── Allow a small amount of data loss? → RDB (default configuration) ├── High data safety requirements? → AOF appendfsync everysec ├── Best cost-performance? → Hybrid persistence (recommended) └── Pure cache (no persistence)? → Disable persistence

Step 4: Deployment Architecture ├── Standalone (<16GB, downtime tolerable) → Standalone + AOF ├── Master-slave (heavy read traffic) → Master-slave replication ├── High availability (automatic failover) → Sentinel (3 nodes) ├── Horizontal scaling (> single-machine memory) → Cluster (6 nodes minimum) └── Read-write separation + high availability → Sentinel + read-write separation


## When to Use (and When NOT to)

| ✅ Use When | ❌ Skip When |
|------------|-------------|
| 需要极低延迟(<1ms)的键值存取 | 需要复杂 SQL 关联查询和事务性报表 |
| 热点数据缓存以减轻数据库压力 | 数据持久性要求超过内存可承受范围 |
| 高并发计数器(INCR/DECR) | 需要完整的 ACID 事务保证(用关系型数据库) |
| 排行榜/时间线/消息队列等数据结构场景 | 数据量远大于内存容量且不需要高速访问 |
| 分布式锁/限流/会话管理 | 已经使用成熟的分布式缓存中间件且不迁移 |
| 实时分析、地理位置搜索、UV 统计 | 需要数据之间强引用约束和外键 |

**核心原则:Redis 是内存数据库,不是关系型数据库替代品。**

## Boundary — 能力边界

| ✅ 完全适用 | ⚠️ 有条件适用 | ❌ 不适用 |
|------------|--------------|---------|
| 缓存加速、API 响应提速 | 强一致性要求场景(需配合 DB + 锁) | 代替关系型数据库作为唯一存储 |
| 计数器、排行榜、时间线 | Redis 作为消息队列(Stream 替代 Pub/Sub) | 复杂 SQL 查询和 JOIN |
| 分布式锁、限流、Session 存储 | 生产数据量 > 内存的场景(需 Cluster) | 存储大文件或二进制数据 |
| 实时排行榜、地理空间查询 | 作为主数据库存储核心业务事务 | 需要外键约束和引用完整性 |
| 去重统计(HyperLogLog/Bitmap) | 跨业务共享 Redis 实例(需 ACL 隔离) | 替代搜索引擎做全文搜索(用 RediSearch) |

**超出范围时**:请使用 PostgreSQL(关系型数据)、MongoDB(文档型)、Elasticsearch(全文搜索)、RabbitMQ/Kafka(消息队列)。

## When to trigger this skill

**ALWAYS use this skill when the user mentions:**
- "Redis", "缓存", "cache", "Session 存储", "分布式锁"
- "String", "Hash", "List", "Set", "ZSet", "Sorted Set", "Geo", "HyperLogLog", "Stream"
- "SET / GET / INCR /... (any Redis command)"
- "RDB", "AOF", "持久化", "Persistence"
- "主从", "Sentinel", "Cluster", "哨兵", "集群", "切片"
- "redis.conf", "redis-cli", "redis-benchmark"
- "Lua 脚本", "事务 / MULTI / EXEC", "Pipeline", "Pub/Sub"
- "缓存穿透", "缓存雪崩", "缓存击穿", "Big Key", "Hot Key"
- "Redis 性能优化", "慢查询", "内存淘汰"

---

## 1. Redis 核心数据结构与命令

### 1.1 数据结构选型速查表

| 数据结构 | 底层实现 | 最佳场景 | 复杂度 | 最大容量 |
|---------|---------|---------|-------|---------|
| **String** | SDS (Simple Dynamic String) | 缓存、计数器、Session、分布式锁 | O(1) | 512MB |
| **Hash** | ziplist / dict | 对象存储(用户、文章)、字段更新 | O(n) n为字段数 | 4294967295 字段 |
| **List** | quicklist | 消息队列、时间线、日志 | O(1) 头尾 | 4294967295 元素 |
| **Set** | intset / dict | 标签、社交关系、去重 | O(1) 增删查 | 4294967295 成员 |
| **ZSet** | ziplist / skiplist+dict | 排行榜、延时队列、范围查询 | O(log n) 跳表 | 4294967295 成员 |
| **Bitmap** | String 位操作 | 签到/活跃用户统计、布隆过滤 | O(1) 位操作 | 512MB (2^32位) |
| **HyperLogLog** | 概率数据结构 | UV 统计、去重计数 | O(1) | 12KB/键 (0.81%误差) |
| **Geo** | ZSet 封装 | 附近的人、地理围栏 | O(log n) | 同 ZSet |
| **Stream** | radix tree | 消息队列、事件溯源、消费组 | O(log n) | 4294967295 消息 |

### 1.2 String 命令与示例

```redis
# 基础操作
SET key "value"                    -- 设置值
SET key "value" EX 60              -- 设置值 + 60s 过期
SET key "value" NX                 -- 仅 key 不存在时设置 (分布式锁)
GET key                            -- 获取值
GETSET key "new"                   -- 设置新值返回旧值 (原子操作)
STRLEN key                         -- 获取字符串长度
APPEND key "suffix"                -- 追加

# 数字操作 (Redis 内部数字存储为字符串)
INCR counter                       -- 原子 +1 (自增)
INCRBY counter 10                  -- 原子 +10
DECR counter                       -- 原子 -1
DECRBY counter 5                   -- 原子 -5
INCRBYFLOAT price 1.5              -- 浮点数增加

# 批量操作
MSET k1 v1 k2 v2                   -- 批量设置 (非原子)
MGET k1 k2                         -- 批量获取
MSETNX k1 v1 k2 v2                 -- 仅当所有 key 都不存在时设置 (原子)

# 子串操作
GETRANGE key 0 -1                  -- 获取全部子串
SETRANGE key 6 "Redis"             -- 从偏移 6 覆写

# 带过期操作
SETEX key 3600 "value"             -- 设置值 + 秒级过期
PSETEX key 3000 "value"            -- 设置值 + 毫秒级过期

String encoding selection: for values ≤ 44 bytes use embstr(single memory allocation), for > 44 bytes use raw(two allocations).

1.3 Hash Commands and Examples

HSET user:1001 name "Alice" age 30     -- 设置多字段
HGET user:1001 name                     -- 获取单字段
HMGET user:1001 name age                -- 获取多字段
HGETALL user:1001                       -- 获取所有字段 (慎用,大 key 场景阻塞)
HKEYS user:1001                         -- 获取所有字段名
HVALS user:1001                         -- 获取所有字段值
HDEL user:1001 age                      -- 删除字段
HEXISTS user:1001 name                  -- 检查字段是否存在
HLEN user:1001                          -- 字段数量
HINCRBY user:1001 score 10              -- 字段数值增加
HSETNX user:1001 email "a@b.com"        -- 仅字段不存在时设置

Hash encoding selection: when the number of fields < 512 and each value length < 64 bytes use ziplist(saves memory), otherwise upgrade to dict(hash table). Configurable hash-max-ziplist-entries and hash-max-ziplist-value。

1.4 List Commands and Examples

# 右进左出 (队列模式 - FIFO)
RPUSH queue "job1" "job2"              -- 右边推入一个或多个
LPOP queue                             -- 左边弹出

# 左进右出 (栈模式 - LIFO)
LPUSH stack "a" "b"                    -- 左边推入
RPOP stack                             -- 右边弹出

# 范围操作
LRANGE list 0 -1                       -- 获取全部元素
LINDEX list 0                          -- 获取指定索引元素
LLEN list                              -- 列表长度
LTRIM list 0 99                        -- 修剪保留前 100 个
LREM list 2 "value"                    -- 移除 2 个匹配的值
LSET list 0 "new"                      -- 设置指定索引的值
LINSERT list BEFORE "b" "a"            -- 在元素前插入

# 阻塞操作 (超时秒数,0 表示无限等待)
BLPOP queue 5                          -- 阻塞式左弹出,超时 5s
BRPOP queue 5                          -- 阻塞式右弹出

List encoding selection: after the number of elements or the length of a single element exceeds the threshold, switch from quicklist mode (ziplist node linked list) to linkedlist.

Typical scenarios:

  • LPUSH + LTRIM = Fixed-length list of latest messages
  • RPUSH + BLPOP = Reliable message queue
  • BRPOPLPUSH = Safe queue (backed up to backup list)

1.5 Set Commands and Examples

SADD tags "redis" "database"          -- 添加成员
SREM tags "database"                   -- 移除成员
SMEMBERS tags                          -- 获取所有成员 (慎用于大 set)
SISMEMBER tags "redis"                 -- 判断成员 O(1)
SCARD tags                             -- 成员数量

# 集合运算 (支持集合间操作)
SINTER set1 set2                       -- 交集
SUNION set1 set2                       -- 并集
SDIFF set1 set2                        -- 差集
SINTERSTORE dest s1 s2                 -- 交集存到新 key
SUNIONSTORE dest s1 s2                 -- 并集存到新 key
SDIFFSTORE dest s1 s2                  -- 差集存到新 key

# 随机操作
SRANDMEMBER key 3                      -- 随机返回 3 个成员 (不删除)
SPOP key 2                             -- 随机弹出 2 个成员 (删除)

SSCAN key 0 COUNT 100                  -- 渐进迭代 (避免阻塞)

1.6 ZSet (Sorted Set) Commands and Examples

ZADD leaderboard 100 "player1" 200 "player2"  -- 添加成员及分数
ZREM leaderboard "player1"                    -- 移除成员
ZSCORE leaderboard "player1"                  -- 获取分数
ZCARD leaderboard                             -- 成员数量
ZINCRBY leaderboard 50 "player1"              -- 增加分数

# 按排名查
ZRANGE leaderboard 0 -1 WITHSCORES            -- 按分数从小到大 (加分差)
ZREVRANGE leaderboard 0 -1 WITHSCORES         -- 按分数从大到小 (排行榜)
ZRANK leaderboard "player1"                   -- 获取排名 (从小到大)
ZREVRANK leaderboard "player1"                -- 获取排名 (从大到小)

# 按分数范围查
ZRANGEBYSCORE salary 2000 5000                -- 按分数范围查询
ZREVRANGEBYSCORE salary 5000 2000             -- 按分数范围逆序
ZCOUNT salary 2000 5000                       -- 统计分数区间数
ZREMRANGEBYSCORE salary 0 1000                -- 移除分数区间成员

# 按字典序范围查
ZRANGEBYLEX words [a [z                      -- 字典序范围查询
ZLEXCOUNT words [a [z                        -- 统计字典序区间数
ZREMRANGEBYLEX words [a [z                   -- 移除字典序区间成员

# 集合运算
ZINTERSTORE dest 2 z1 z2                     -- 交集
ZUNIONSTORE dest 2 z1 z2                     -- 并集

ZSCAN leaderboard 0 COUNT 100                -- 渐进迭代

ZSet encoding selection: when the number of members < 128 and all value lengths < 64 bytes use ziplist, otherwise use skiplist + dict。

1.7 Advanced Data Structures

# Bitmap — 位图
SETBIT sign:2024-01 100 1                    -- 用户 100 在 1月签到
GETBIT sign:2024-01 100                      -- 查询签到状态
BITCOUNT sign:2024-01                        -- 统计签到人数
BITOP AND dest sign:01 sign:02               -- 位运算 (AND/OR/NOT/XOR)

# HyperLogLog — 基数统计 (0.81% 误差)
PFADD visits:2024-01 "user1" "user2"         -- 添加元素
PFCOUNT visits:2024-01                       -- 近似去重计数
PFMERGE total visits:01 visits:02            -- 合并

# Geo — 地理位置
GEOADD cities 116.397 39.908 "北京"           -- 添加地标
GEODIST cities "北京" "上海" km               -- 计算两地距离
GEORADIUS cities 116.4 39.9 100 km           -- 查找 100km 内的位置
GEORADIUSBYMEMBER cities "北京" 500 km        -- 以成员为中心查找
GEOHASH cities "北京"                         -- 返回 geohash 字符串
GEOPOS cities "北京"                          -- 返回经纬度

# Stream — 消息流 (Redis 5.0+)
XADD mystream * sensor-id 1234 temp 19.8     -- 追加消息 (自动时间戳ID)
XLEN mystream                                 -- 消息长度
XRANGE mystream - +                           -- 范围查询
XREAD COUNT 10 STREAMS mystream 0            -- 读取消息
XGROUP CREATE mystream mygroup $              -- 创建消费组
XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >  -- 消费
XACK mystream mygroup 1648123456789-0        -- 确认消息

2. Cache Patterns and Architecture

2.1 Cache Read Patterns

Read-Through: 应用 → Redis → 数据库
               命中 ✓ 返回    未命中 ✗ 回源
# Cache-Aside (旁路缓存) — 最常用模式
GET cache_key        → 命中返回
                      → 未命中: 查 DB → SET cache_key value EX 3600 → 返回

# 伪代码
function get_user(user_id):
    user = redis.get("user:" + user_id)
    if user is None:
        user = db.query("SELECT * FROM users WHERE id=?", user_id)
        redis.setex("user:" + user_id, 3600, user)
    return user

2.2 Cache Update Patterns

Pattern Operation Method Concurrency Safety Description
Cache-Aside Update DB → Delete cache ⚠️ Deletion failure causes dirty data Most general, most widely used
Write-Through Update DB → Synchronously update cache ✅ High consistency Increased write latency, suitable for few writes and many reads
Write-Behind Write cache first → Asynchronously write DB ⚠️ Downtime may cause data loss Best performance, suitable for scenarios allowing slight data loss
Refresh-Ahead Automatic refresh before cache expiration ✅ No expiration storm Suitable for hot keys

2.3 The Three Major Cache Problems

缓存穿透 (Cache Penetration)
├── 问题:查询一个不存在的数据,每次穿透到 DB
├── 后果:大量请求直接打到数据库,可能击垮 DB
├── 解决:
│   ├── ① 布隆过滤器 (Bloom Filter): 用 Bitmap 预判 key 是否存在
│   ├── ② 缓存空值: SET key "" EX 60 (短过期时间)
│   └── ③ 参数校验: 非法参数直接拒绝
└── 提示:推荐组合使用 ① + ②

缓存击穿 (Cache Breakdown / Hot Key)
├── 问题:热点 key 过期瞬间,高并发直接打到 DB
├── 后果:瞬间高并发,DB 扛不住
├── 解决:
│   ├── ① 互斥锁 (Mutex Lock): SETNX 争锁,只有一个线程回源
│   ├── ② 永不过期 + 异步刷新: 物理上不设 TTL,后台线程定时更新
│   └── ③ 热点 key 预留: 预估热点,预加载
└── 提示:互斥锁实现最简单,异步刷新性能最优

缓存雪崩 (Cache Avalanche)
├── 问题:大量缓存同时过期/Redis 宕机,流量直击 DB
├── 后果:DB 被击垮,服务雪崩
├── 解决:
│   ├── ① TTL 随机化: SET key EX (300 + random(0,60))
│   ├── ② 互斥锁: 同击穿方案
│   ├── ③ 双缓存: 主缓存 + 备份缓存
│   ├── ④ Redis 高可用: 主从 + Sentinel/Cluster
│   └── ⑤ 本地缓存 + Redis: 二级缓存 (guava/caffeine)
└── 提示:TTL 随机化是最低成本最高收益的预防措施

2.4 Distributed Locks

# 最简单的分布式锁 (单 Redis 实例)
SET lock:resource "uuid-value" NX EX 30       -- 加锁
-- 执行业务逻辑...
DEL lock:resource                                -- 释放锁

# 问题:误删其他线程的锁 → 需要 Lua 脚本保证原子性

# 正确的解锁 (Lua 脚本)
-- EVAL "if redis.call('get',KEYS[1])==ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock:resource uuid-value

# Redlock 算法 (多实例场景)
# 1. 获取当前时间 T1
# 2. 向 N/2+1 个实例尝试加锁 (SET NX PX)
# 3. 用去时间 > 锁有效时间 → 加锁失败,向所有实例发送解锁
# 4. 使用 Redisson / Redlock-py 等客户端实现

# 最佳实践:使用 Redisson 等成熟客户端,不要自行实现

2.5 Common Cache Key Design Specifications

# 命名规范
cache:user:{id}           -- 用户缓存
cache:article:{id}        -- 文章缓存
lock:order:{order_id}     -- 分布式锁
rate:limit:ip:{ip}        -- 限流
session:{session_id}      -- Session

# 过期时间策略
- 通用原则: TTL = 业务可接受的脏数据时间 + random(0, TTL*1/5)
- 静态数据 (配置表): 1-24 小时
- 动态数据 (用户信息): 5-30 分钟
- 热点数据 (首页推荐): 1-5 分钟 + 异步刷新

# Big Key 应对
Big Key 指单个 key 存大量数据 (Hash 上百万字段 / List 千万级元素)
├── 问题:阻塞其他命令、内存不均、慢查询
├── 发现:redis-cli --bigkeys 扫描
├── 拆分:Hash → HASH_KEY:{mod(hash_field, 100)}
└── 替代:List → 改用 Stream + 消费组

3. Persistence

3.1 RDB (Redis Database) — Snapshot Persistence

原理:定时将内存数据生成快照写入磁盘 (dump.rdb)
Configuration Description Trigger Condition
save 900 1 ≥1 write within 900s Automatic
save 300 10 ≥10 writes within 300s Automatic
save 60 10000 ≥10000 writes within 60s Automatic
BGSAVE Background fork child process generates snapshot Manual
SAVE Generated by main process (blocks all requests) Manual
RDB 优势:文件紧凑,恢复快,适合备份和灾难恢复
RDB 劣势:可能丢失最后一次快照后的数据 (最长丢失一个 save 间隔)

3.2 AOF (Append Only File) — Log Persistence

原理:记录每次写操作命令,Redis 重启时回放
appendfsync Option Persistence Strategy Data Safety Performance Impact
always fsync per command Lose at most 1 command Very low (frequent disk writes)
everysec fsync every second Lose at most 1s of data Low (recommended)
no Determined by operating system Unpredictable High

AOF Rewrite:BGREWRITEAOF — Compresses the AOF file (merges multiple commands into a minimal set). Configure auto-aof-rewrite-percentage 100 and auto-aof-rewrite-min-size 64mb to trigger automatically.

AOF 优势:数据安全性高 (最多丢 1 秒数据),文件易读
AOF 劣势:文件体积比 RDB 大,恢复比 RDB 慢

3.3 Best Choice: RDB + AOF Hybrid

Redis 4.0+ supports hybrid persistence (aof-use-rdb-preamble yes):

  • During AOF rewrite, an RDB snapshot is first generated and written to the header of the AOF file
  • Subsequent incremental write commands are appended in AOF format
  • Restart recovery: first load RDB (fast), then replay the AOF increment (to complete)
推荐生产配置:
┌─────────────────────────────────────────────┐
│ appendonly yes                              │
│ appendfsync everysec                        │
│ aof-use-rdb-preamble yes                    │
│ save 900 1                                  │
│ save 300 10                                 │
│ save 60 10000                               │
└─────────────────────────────────────────────┘

3.4 Persistence Comparison Summary

Dimension RDB AOF Hybrid (RDB+AOF)
Data Integrity May lose more Lose at most 1s Lose at most 1s
Recovery Speed Fast Slow Fast
File Size Small Large (rewritable) Medium
Real-time Impact fork overhead (memory doubles) Disk I/O (controllable) Combined overhead

4. High Availability Architecture

4.1 Master-Slave Replication (Replication)

┌─────────┐     复制流      ┌──────────┐
│ Master  │ ──────────────→ │ Replica  │
│ (写+读)  │                 │ (只读)   │
└─────────┘                 └──────────┘

Replication Process:

1. Replica 发送 SLAVEOF master_ip master_port
2. Master BGSAVE 生成 RDB → 发送到 Replica
3. Replica 加载 RDB + 缓存增量命令
4. 后续持续增量复制 (基于环形缓冲区 repl_backlog)
5. 网络断开重连 → 部分重同步 (PSYNC2)

Core Configuration:

# master
replica-read-only no                     # Master 默认不设置只读

# replica
replicaof 192.168.1.100 6379             # 指定主节点
replica-read-only yes                    # 从节点只读
replica-priority 100                     # Sentinel 选主优先级 (越小越高)

Replication Considerations:

  • When the master node forks a child process to perform BGSAVE, it does not block reads/writes (but the COW mechanism doubles memory)
  • Slave nodes are read-only by default and can be used to offload read traffic
  • After the master node crashes, you must manually switch to a slave node or use Sentinel

4.2 Sentinel (Sentinel) — Automatic Failover

          ┌─────────────┐
          │  Sentinel-1  │
          └──────┬──────┘
                 │ 监控 + 协调
    ┌────────────┼────────────┐
    │            │            │
┌───▼───┐  ┌────▼────┐  ┌───▼───┐
│Master  │  │Replica-1│  │Replica-2│
└───────┘  └─────────┘  └─────────┘

Sentinel Core Functions:

1. 监控:PING 主从节点,判断是否下线
2. 通知:管理员或程序通过 API 获取状态变化
3. 故障转移:Master 挂了 → 选举新 Master
4. 配置提供:客户端请求获取当前 Master 地址

Deployment Requirements: Minimum 3 Sentinel instances (odd number, to ensure quorum).

sentinel.conf Configuration:

sentinel monitor mymaster 127.0.0.1 6379 2    # 监控主节点,2 个哨兵同意即判定下线
sentinel down-after-milliseconds mymaster 5000 # 5s 无响应判主观下线
sentinel failover-timeout mymaster 60000       # 故障转移超时
sentinel parallel-syncs mymaster 1             # 新主后同时多少个从同步

4.3 Cluster (Cluster) — Data Sharding + High Availability

        ┌──────────┐
        │  客户端   │
        └────┬─────┘
             │ 请求任意节点,MOVED 重定向
    ┌────────┼──────────────┐
    │        │              │
┌───▼───┐ ┌──▼───┐    ┌───▼───┐
│Node 1  │ │Node 2 │... │Node N  │
│ 0-5460 │ │5461-10922 │  │10923-16383│
│+Slave  │ │+Slave │  │+Slave  │
└────────┘ └───────┘    └───────┘

Core Concepts:

  • 16384 hash slots:CRC16(key) % 16384
  • Each node is responsible for a range of slots
  • Nodes communicate via the gossip protocol (PONG/PING)
  • Automatic master-slave failover: if the master node goes down, the slave node is promoted

Key Points for Cluster Setup:

# 配置文件:(每个节点必须有集群模式)
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 5000

# 创建集群 (Redis 5+)
redis-cli --cluster create 192.168.1.100:6379 192.168.1.101:6379 \
  192.168.1.102:6379 192.168.1.103:6379 192.168.1.104:6379 192.168.1.105:6379 \
  --cluster-replicas 1

# 常用操作
redis-cli --cluster check 192.168.1.100:6379    # 检查集群状态
redis-cli --cluster info 192.168.1.100:6379     # 集群信息
redis-cli --cluster rebalance 192.168.1.100:6379 # 重新平衡 slot
redis-cli --cluster add-node 新节点ip:port 已有节点ip:port --cluster-slave

Cluster Limitations:

  • Only database 0 is supported (SELECT 0)
  • Keys for batch operations (MGET/MSET) must be in the same slot (use hash tag {user:1001})
  • Keys in transactions/Lua scripts must be on the same node (same slot)
  • Multi-key operations across slots are not supported

4.4 Architecture Selection Decision Tree

数据量 < 单个节点内存 (≤16GB) ?
├── YES → 是否需要自动故障转移?
│         ├── NO  → 单机 + RDB/AOF
│         └── YES → 读写分离需要?
│                   ├── NO  → 主从 + Sentinel (3实例)
│                   └── YES → 主从 + Sentinel (读写分离)
│
└── NO → 数据量预期增长?
         ├── NO  → 升级单机内存 (垂直扩展)
         └── YES → Cluster 集群 (水平扩展)
                   ├── 三主三从 (最小生产配置)
                   ├── 六主六从 (中等规模)
                   └── 九主九从 (大规模)

5. Lua Scripts and Transactions

5.1 Transactions

MULTI                              -- 开始事务
SET key1 "value1"                  -- 命令入队 (QUEUED)
SET key2 "value2"                  -- 命令入队
EXEC                               -- 按顺序执行所有命令

# WATCH — 乐观锁 (CAS 模式)
WATCH stock:100                    -- 监视 key
count = GET stock:100              -- 读取当前值
MULTI
SET stock:100 (count - 1)          -- 如果期间 stock:100 被修改 → EXEC 返回 nil
EXEC
UNWATCH                            -- 取消监视

Transaction vs Pipeline Comparison:

Feature MULTI/EXEC (Transaction) Pipeline
Atomicity ✅ All/none executed ❌ Not guaranteed
Blocks waiting for results ✅ Yes ✅ Yes
Intermediate results readable ❌ Queued only, not readable ✅ Readable
Error handling Syntax errors fail all/runtime errors continue others Each command is independent
Use cases Need "all or nothing" Batch operations to improve performance

5.2 Lua Scripts

Redis 2.6+ has a built-in Lua 5.1 interpreter; scripts execute atomically on the server side.

-- 原子扣减库存脚本
-- EVAL script 1 key arg
-- KEYS[1] = stock:100
-- ARGV[1] = 1 (扣减数量)

local stock = redis.call("GET", KEYS[1])
if not stock or tonumber(stock) < tonumber(ARGV[1]) then
    return -1  -- 库存不足
end
redis.call("DECRBY", KEYS[1], ARGV[1])
return redis.call("GET", KEYS[1])

-- 调用方式: EVAL "上述脚本" 1 stock:100 1
-- 缓存: SCRIPT LOAD "script" → SHA → EVALSHA SHA 1 stock:100 1

Lua Script Best Practices:

  • Scripts should be short and concise (keep within 100 lines); long scripts block other requests
  • Use SCRIPT LOAD + EVALSHA to reduce network transmission
  • All keys in the script must be passed in via the KEYS array; hardcoding is not allowed
  • Scripts must not access keys on different nodes (Cluster scenario)
  • Use redis.log(redis.LOG_WARNING, msg) for debugging

5.3 CAS (Optimistic Locking) Pattern

-- 更安全的库存扣减:数值检查 + 原子操作
local key = KEYS[1]
local expected = tonumber(ARGV[1])
local new_value = tonumber(ARGV[2])

local current = redis.call("GET", key)
if tonumber(current) ~= expected then
    return -1  -- 已被其他客户端修改
end
return redis.call("SET", key, new_value)

6. Performance Optimization and Operations

6.1 Memory Eviction Policies (maxmemory-policy)

Policy Description Use cases
noeviction No eviction; write operations return errors Database mode (data loss prohibited)
allkeys-lru Least recently used across all keys General-purpose cache (recommended)
allkeys-lfu Least frequently used across all keys (Redis 4.0+) Caches with large differences in access frequency
volatile-lru LRU on keys with an expiration time Partially persistent, partially cached
volatile-ttl Evict keys with the shortest TTL Specific scenarios
volatile-random Randomly evict keys with an expiration time Not recommended
redis-cli CONFIG SET maxmemory 4gb           # 设置最大内存
redis-cli CONFIG SET maxmemory-policy allkeys-lru  # LRU 淘汰

6.2 Big Key and Hot Key

Big Key Detection:

# 扫描大 key (阻塞,建议低峰期执行)
redis-cli --bigkeys

# 用 MEMORY USAGE 精确查看内存占用
redis-cli MEMORY USAGE mykey

# 用 DEBUG 命令 (Redis 4.0+)
redis-cli MEMORY DOCTOR

Big Key Splitting Strategy:

场景 1: Hash 包含上百万字段
→ 拆分: HASH_KEY:{hash(field) % 100} = {field: value}

场景 2: List 包含千万级元素
→ 替换为 Stream (支持消费组)

场景 3: ZSet 大排行榜
→ 分桶: ranking:2024-01, ranking:2024-02...

场景 4: String 存储大 JSON (>10MB)
→ 压缩存储: Snappy/LZ4 压缩后存,读取解压

Hot Key Handling:

问题:单个 key QPS 极高 (如 10万+/秒)
解决:
├── 本地缓存 (Local Cache): 二级缓存 (如 Caffeine)
├── 读写分离: 从节点分担读流量
├── 热点拆分: hotkey:{random(0,10)} = value (分片读取)
└── 代理层: Redis Proxy (如 Twemproxy/RedisShake)

6.3 Slow Query Log

CONFIG SET slowlog-log-slower-than 10000     # 记录 >10ms 的命令
CONFIG SET slowlog-max-len 128               # 最多保留 128 条记录

SLOWLOG GET 10                               # 获取前 10 条慢查询
SLOWLOG LEN                                  # 慢查询总数
SLOWLOG RESET                                # 清空慢查询

6.4 Performance Benchmarks and Optimization

# 基准测试
redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -q
redis-benchmark -t set,get,incr,lpush -q    # 指定命令测试
redis-benchmark -P 10 -q                     # Pipeline 模式

# 预期性能 (单实例):
# GET/SET: 10万+ QPS (无持久化)
# Pipeline 100条: 100万+ QPS
# Lua 脚本: 5-15万 次/秒

Key Optimization Parameters:

tcp-backlog 511                           # TCP 连接队列
timeout 300                               # 客户端空闲超时
tcp-keepalive 300                         # TCP keepalive
lfu-log-factor 10                         # LFU 计数器对数因子 (Redis 4.0+)
lfu-decay-time 1                          # LFU 衰减时间
hz 10                                     # 后台任务频率 (可增加到 100)

6.5 Security Hardening

# 基础安全
requirepass your_strong_password           # 设置密码
RENAME_COMMAND FLUSHALL ""                 # 禁用危险命令
RENAME_COMMAND FLUSHDB ""
RENAME_COMMAND CONFIG ""
RENAME_COMMAND SHUTDOWN ""

# 网络安全
bind 127.0.0.1 192.168.1.100              # 绑定内网 IP
protected-mode yes                         # 保护模式
port 6379                                  # 修改默认端口

# 连接限制
maxclients 10000                           # 最大连接数
client-output-buffer-limit normal 0 0 0    # 客户端输出缓冲区限制
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60

# ACL (Redis 6.0+)
ACL SETUSER alice on >password ~cached:* +get +set -admin
ACL SETUSER bob on >password ~* +@all -@dangerous

7. Redis Ecosystem and Client Connections

7.1 Client Configuration

// Java (Jedis 连接池)
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(100);           // 最大连接数
config.setMaxIdle(50);             // 最大空闲
config.setMinIdle(10);             // 最小空闲
config.setTestOnBorrow(true);      // 获取时校验
config.setTestOnReturn(true);      // 返回时校验
config.setMaxWaitMillis(3000);     // 获取连接超时

// Java (Lettuce — 推荐,支持异步/响应式)
RedisClient client = RedisClient.create("redis://password@host:6379/0");
StatefulRedisConnection<String, String> conn = client.connect();
RedisCommands<String, String> sync = conn.sync();
RedisAsyncCommands<String, String> async = conn.async();

7.2 Common Redis Monitoring Commands

# 实时监控
redis-cli -a password MONITOR          # 实时打印所有命令 (生产慎用)
redis-cli -a password INFO             # 全面状态信息
redis-cli -a password INFO STATS       # 统计信息
redis-cli -a password INFO MEMORY      # 内存信息
redis-cli -a password INFO CLIENTS     # 客户端信息

# 连接数监控
redis-cli CLIENT LIST | wc -l          # 当前连接数
redis-cli INFO connected_clients       # 连接数

# 内存监控
redis-cli INFO used_memory_human       # 已用内存
redis-cli INFO used_memory_peak_human  # 峰值内存
redis-cli INFO mem_fragmentation_ratio # 内存碎片率 (>1.5 需重启)

7.3 Redis Stack Extensions

Redis Stack (Redis 6.2+) integrates the following modules:

  • RediSearch: Full-text search, indexing, vector search
  • RedisJSON: Native JSON data type support
  • RedisTimeSeries: Time series data type
  • RedisBloom: Bloom filter, Cuckoo Filter

Gotchas — Common Gotchas & Anti-Patterns

# Problem Risk Solution
# Anti-Pattern Problem Correct Approach
1 Using KEYS to search in production O(n) scans the entire database, blocking Redis for several seconds Use SCAN cursor iteration
2 Using SMEMBERS/HGETALL on large collections JSON serialization of millions of elements, memory explosion Use SSCAN/HSCAN in batches or split keys
3 Not using a connection pool Creating a connection per request, exhausting system resources Use JedisPool/Lettuce connection pool
4 All keys without expiration time Unbounded memory growth, reaching maxmemory Set appropriate TTL based on the scenario
5 Using a single instance as a database for permanent storage No high availability, data loss on crash Master-replica + Sentinel or Cluster
6 Using SELECT to split databases Difficult to manage and monitor, not supported in Cluster Use different key prefixes or different Redis instances
7 Generating overly long key names Wastes memory (the key itself is also stored) Keep key names at a reasonable length (32-64 characters)
8 Using Redis to store large files/binary data Blows past the 512MB single key limit, poor performance Store file paths + object storage
9 Unbounded Pipeline batching Client buffer OOM Keep Pipeline batch size between 100-500
10 Not separating business use cases on the same instance Mutual interference, difficult to isolate Split instances by business + limit memory/connections

9. FAQ

Q1: Why is Redis not recommended as a primary database? Redis memory is expensive. Although it supports persistence, at most 1 second of data can be lost, and it does not support SQL queries or complex constraints. A typical architecture is Redis as an acceleration layer in front, with a relational database for persistent storage behind.

Q2: What happens when Redis memory is full? It depends on maxmemory-policy. Recommended: allkeys-lru, which evicts the least recently used keys. If set to noeviction, write operations return an OOM error.

Q3: When RDB and AOF are both enabled, what is the startup loading order? AOF is loaded first (data is more complete); if AOF does not exist, RDB is loaded. With hybrid persistence, the AOF file header is an RDB snapshot, so RDB is loaded first (fast), then the AOF increment is replayed.

Q4: How to choose between Stream and Pub/Sub?

Comparison Pub/Sub Stream
Message persistence ❌ Not persistent, lost when offline ✅ Persisted to memory/disk
Consumption acknowledgment ❌ No ACK ✅ XACK acknowledgment
Consumer group ❌ Not supported ✅ XGROUP consumer group
Message replay ❌ Cannot replay ✅ XRANGE can replay
Use cases Real-time broadcast notifications Reliable message queue/event sourcing

Q5: Can transactions/Lua still be used in Cluster mode? Yes, but the keys of all operations must be on the same node. Use a hash tag {tag} to ensure related keys are routed to the same slot. Transactions across slots cannot be executed.

Q6: Why are Big Keys dangerous? A Big Key causes: Redis to slow down (O(n) commands block), uneven cluster data distribution, longer persistence time, and large master-replica synchronization delay. Use --bigkeys to scan periodically.

Q7: What is Redis's threading model like? Redis processes commands in a single thread (6.0+ handles network I/O with multiple threads). Single-threading avoids lock contention, but one slow query will block all subsequent requests. Therefore, Lua scripts must be short and small, and KEYS must not operate on large collections with O(n) commands.

Q8: In what situations should Redis 6.0+ ACL be used? In multi-tenant scenarios, when a team shares a Redis instance, using ACL to control permissions can prevent accidental operations. For example: business line A can only operate on a:*and cannot use FLUSHALL.

Q9: How can configuration be modified without restarting? CONFIG SET dynamically modifies runtime configuration,CONFIG REWRITE writes to redis.conf to make it take effect after restart.

Q10: How should high Redis memory fragmentation be handled? mem_fragmentation_ratio > 1.5 indicates severe fragmentation. Redis 4.0+ can use the MEMORY PURGE command to trigger defragmentation, or set activedefrag yes for automatic defragmentation.


Keywords

redis, redis-cli, cache, string, hash, list, set, zset, sorted set, bitmap, hyperloglog, stream, geo, lua, transaction, pipeline, persistence, RDB, AOF, hybrid persistence, master-replica replication, sentinel, sentinel, cluster, cluster, cache penetration, cache breakdown, cache avalanche, distributed lock, memory eviction, LRU, LFU, big key, hot key, slow query, performance optimization, security hardening, ACL, Redisson, Jedis, Lettuce, bigkey, hotkey, slot, hash tag, RediSearch, RedisJSON, Redis Stack, data eviction


References