项目介绍

Redis(Remote Dictionary Server)是一个开源的高性能内存数据存储系统,最初由 Salvatore Sanfilippo(antirez)于 2009 年创建。它不仅仅是简单的 Key-Value 存储,还支持 String、List、Hash、Set、Sorted Set、Stream、HyperLogLog、Bitmap 等丰富的数据结构。

Redis 以极致的性能著称 — 纯内存操作、单线程事件循环(避免锁开销)、I/O 多路复用,使其在单节点上即可达到 10 万+ QPS。它被广泛用于缓存、会话管理、排行榜、消息队列、分布式锁、实时分析等场景,是现代互联网架构中不可或缺的基础组件。

项目信息详情
开发团队Redis Ltd(前 Redis Labs)
GitHub Stars68,000+
官方网站redis.io
开源协议BSD-3-Clause(Redis 7.4+ 改为 RSALv2 / SSPLv1 双协议)
核心技术C 语言(ANSI C99)
存储模式纯内存 + 可选持久化(RDB / AOF)
部署方式源码编译 / 包管理器 / Docker / Redis Stack
一句话理解:Redis 就像一个超高速的"瑞士军刀"内存数据库 — 不只存 Key-Value,还能做排行榜、消息队列、分布式锁、地理位置查询、全文搜索甚至向量检索,且读写延迟通常在亚毫秒级别。

Redis vs Memcached vs KeyDB vs Dragonfly

对比维度 Redis Memcached KeyDB Dragonfly
数据结构 String/List/Hash/Set/ZSet/Stream 等 10+ 仅 Key-Value(字符串) 兼容 Redis 全部数据结构 兼容 Redis 全部数据结构
持久化 RDB + AOF + 混合模式 不支持 RDB + AOF RDB 快照
多线程 I/O 多线程(6.0+),命令仍单线程 多线程 多线程执行 多线程无共享架构
集群 Redis Cluster(原生支持) 客户端分片 兼容 Redis Cluster + Active Replication 单节点(集群开发中)
内存效率 中等(jemalloc) slab 分配器 与 Redis 类似 显著更优(Dash 结构)
生态与社区 最成熟,客户端覆盖全语言 成熟但功能停滞 兼容 Redis 客户端 兼容 Redis 客户端,社区较新
Lua 脚本 支持(Lua + Functions) 不支持 支持 支持
适用场景 通用缓存 / 数据结构服务器 纯缓存(简单 KV) 需要多线程的 Redis 兼容场景 需要极致内存效率的 Redis 兼容场景
选择建议:Redis 是默认首选 — 生态最完善、文档最丰富、运维经验最多。如果只需简单缓存且已有 Memcached 基础设施,可以继续使用 Memcached。如果需要多线程性能且保持 Redis 兼容,可以考虑 KeyDB 或 Dragonfly。

安装部署

方式 1 Linux 源码编译安装

从源码编译是获取最新版本的推荐方式:
# 安装依赖
sudo apt update && sudo apt install -y build-essential tcl

# 下载并编译
wget https://download.redis.io/redis-stable.tar.gz
tar xzf redis-stable.tar.gz
cd redis-stable
make
make test   # 可选,运行测试
sudo make install

# 启动 Redis
redis-server

# 验证
redis-cli ping
# 返回 PONG 表示成功
提示:编译安装默认将二进制文件放在 /usr/local/bin/。可以通过 make PREFIX=/your/path install 自定义安装路径。

方式 2 包管理器安装

Ubuntu / Debian:
# 添加官方 APT 仓库
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list

sudo apt update
sudo apt install redis
CentOS / RHEL:
sudo yum install epel-release
sudo yum install redis
sudo systemctl enable --now redis
macOS:
brew install redis
brew services start redis

方式 3 Docker 部署(推荐)

一行命令快速启动:
# 快速启动
docker run -d --name redis \
  -p 6379:6379 \
  -v redis_data:/data \
  redis:7-alpine

# 连接测试
docker exec -it redis redis-cli ping
Docker Compose 部署(生产环境推荐):
# docker-compose.yml version: "3.8" services: redis: image: redis:7-alpine restart: always ports: - "6379:6379" command: redis-server /usr/local/etc/redis/redis.conf volumes: - redis_data:/data - ./redis.conf:/usr/local/etc/redis/redis.conf sysctls: - net.core.somaxconn=1024 volumes: redis_data:
注意:生产环境务必设置 requirepass 密码、禁用 CONFIG 命令、绑定内网 IP,避免 Redis 未授权访问漏洞。

方式 4 Redis Stack(推荐开发体验)

Redis Stack 在核心 Redis 基础上集成了多个模块:RediSearch(全文搜索)、RedisJSON(JSON 文档)、RedisTimeSeries(时序数据)、RedisBloom(布隆过滤器)和 RedisInsight(可视化管理界面)。
# Docker 安装 Redis Stack
docker run -d --name redis-stack \
  -p 6379:6379 \
  -p 8001:8001 \
  redis/redis-stack:latest

# 6379 → Redis 服务端口
# 8001 → RedisInsight Web UI
推荐:如果你需要 JSON 文档存储、全文搜索或向量检索功能,直接使用 Redis Stack 比手动加载模块更方便。

数据结构

String 字符串

最基础的数据类型,可以存储字符串、整数或浮点数。最大 512 MB。
# 基本操作
SET user:1:name "张三"
GET user:1:name           # "张三"

# 原子计数器
SET page:views 0
INCR page:views           # 1
INCRBY page:views 100     # 101

# 设置过期时间
SET session:abc123 "user_data" EX 3600   # 1 小时后过期

# 分布式锁
SET lock:order:123 "owner_id" NX EX 30   # 仅当不存在时设置,30 秒过期
底层编码:当值为整数且在 long 范围内时使用 int 编码;短字符串使用 embstr(一次内存分配);长字符串使用 raw(SDS)。

List 列表

有序的字符串列表,支持从两端推入和弹出,适合实现队列和栈。
# 从左侧推入
LPUSH queue:tasks "task1" "task2" "task3"

# 从右侧弹出(队列 FIFO)
RPOP queue:tasks          # "task1"

# 阻塞弹出(消费者等待)
BRPOP queue:tasks 30      # 等待最多 30 秒

# 范围查询
LRANGE queue:tasks 0 -1   # 获取所有元素

# 获取长度
LLEN queue:tasks
底层编码:Redis 7+ 统一使用 listpack(小列表)或 quicklist(大列表,由多个 listpack 组成的双向链表)。

Hash 哈希

键值对的集合,适合存储对象。比将 JSON 字符串存在 String 中更省内存,也支持对单个字段操作。
# 设置多个字段
HSET user:1 name "张三" age 28 email "zhangsan@example.com"

# 获取单个字段
HGET user:1 name          # "张三"

# 获取所有字段
HGETALL user:1

# 字段原子计数
HINCRBY user:1 age 1      # 29

# 仅当字段不存在时设置
HSETNX user:1 created_at "2024-01-01"
底层编码:元素少且值短时使用 listpack(ziplist 的替代);否则使用 hashtable。阈值由 hash-max-listpack-entries(默认 128)和 hash-max-listpack-value(默认 64 字节)控制。

Set 集合

无序且不重复的字符串集合,支持交集、并集、差集等集合运算。
# 添加成员
SADD tags:article:1 "redis" "database" "cache"
SADD tags:article:2 "redis" "nosql" "performance"

# 判断成员是否存在
SISMEMBER tags:article:1 "redis"   # 1

# 交集 — 两篇文章共同的标签
SINTER tags:article:1 tags:article:2   # "redis"

# 随机返回成员(抽奖场景)
SRANDMEMBER lottery:pool 3

# 弹出随机成员
SPOP lottery:pool

Sorted Set 有序集合

每个成员关联一个 score(分数),自动按 score 排序。排行榜、优先级队列的首选数据结构。
# 添加成员及分数
ZADD leaderboard 1500 "player:alice"
ZADD leaderboard 2300 "player:bob"
ZADD leaderboard 1800 "player:charlie"

# 排行榜 Top 3(从高到低)
ZREVRANGE leaderboard 0 2 WITHSCORES

# 查询排名
ZREVRANK leaderboard "player:bob"   # 0(第一名)

# 增加分数
ZINCRBY leaderboard 500 "player:alice"

# 范围查询
ZRANGEBYSCORE leaderboard 1000 2000 WITHSCORES
底层编码:使用 skiplist(跳跃表)+ hashtable 的组合实现,支持 O(log N) 的插入、删除和范围查询。

Stream

Redis 5.0 引入的日志型数据结构,类似 Kafka,支持消费者组。适合事件溯源和消息队列场景。
# 追加消息
XADD orders * user_id 1001 product "iPhone" amount 1

# 读取消息
XRANGE orders - + COUNT 10

# 创建消费者组
XGROUP CREATE orders group1 0

# 消费者读取(阻塞模式)
XREADGROUP GROUP group1 consumer1 COUNT 1 BLOCK 5000 STREAMS orders >

# 确认消息已处理
XACK orders group1 "1234567890-0"

HyperLogLog 基数估算

使用极少内存(每个 key 最多 12 KB)估算集合中不重复元素的数量,误差率约 0.81%。适合 UV 统计。
# 添加元素
PFADD page:uv:2024-01-01 "user:1" "user:2" "user:3" "user:1"

# 估算基数
PFCOUNT page:uv:2024-01-01   # 3

# 合并多天的 UV
PFMERGE page:uv:week page:uv:2024-01-01 page:uv:2024-01-02

Bitmap 位图

在 String 类型上提供按位操作,可以用极少内存表示大量布尔值。适合签到、在线状态等场景。
# 用户签到(第 0 天 = 1 月 1 日)
SETBIT sign:user:1:2024 0 1   # 1月1日签到
SETBIT sign:user:1:2024 4 1   # 1月5日签到

# 查询某天是否签到
GETBIT sign:user:1:2024 0     # 1

# 统计本月签到天数
BITCOUNT sign:user:1:2024 0 3 # 前 4 个字节中 1 的个数

# 查找第一个签到日
BITPOS sign:user:1:2024 1     # 0
内存优势:用 Bitmap 存储 1 亿用户的每日签到状态,仅需 12.5 MB 内存。

实战场景

场景 1 会话缓存(Session Cache)

将用户会话存储在 Redis 中,实现分布式会话共享,支持多个应用服务器无状态水平扩展。
# 存储会话(Hash 结构)
HSET session:abc123 user_id 1001 username "张三" role "admin" login_time "2024-01-01T10:00:00"
EXPIRE session:abc123 1800   # 30 分钟过期

# 读取会话
HGETALL session:abc123

# 续期(每次请求时)
EXPIRE session:abc123 1800

# 销毁会话(退出登录)
DEL session:abc123
最佳实践:会话 key 使用随机 token(如 UUID),不要使用用户 ID;设置合理的过期时间;关键操作后刷新过期时间。

场景 2 接口限流(Rate Limiting)

使用 Redis 实现 API 请求速率限制,保护后端服务免受流量冲击。
# 滑动窗口限流(Lua 脚本 — 原子操作)
# 限制每个 IP 每分钟最多 100 次请求
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local window = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
if count < limit then
    redis.call('ZADD', key, now, now .. math.random())
    redis.call('EXPIRE', key, window / 1000)
    return 1   -- 允许
else
    return 0   -- 拒绝
end
注意:简单计数器方案(INCR + EXPIRE)存在临界点问题 — 在时间窗口交界处可能允许双倍请求。滑动窗口方案更精确。

场景 3 排行榜(Leaderboard)

Sorted Set 天然适合排行榜场景,插入和查询都是 O(log N)。
# 更新玩家分数
ZADD game:leaderboard 15000 "player:alice"
ZADD game:leaderboard 23000 "player:bob"
ZADD game:leaderboard 18500 "player:charlie"

# Top 10 排行榜
ZREVRANGE game:leaderboard 0 9 WITHSCORES

# 查询某玩家排名(0-based)
ZREVRANK game:leaderboard "player:alice"   # 2

# 查询某玩家分数
ZSCORE game:leaderboard "player:alice"     # 15000

# 查询某分数段的玩家
ZREVRANGEBYSCORE game:leaderboard 20000 10000 WITHSCORES LIMIT 0 10

# 增加分数(游戏得分累加)
ZINCRBY game:leaderboard 2000 "player:alice"

场景 4 发布/订阅消息(Pub/Sub)

Redis Pub/Sub 提供轻量级的消息发布与订阅机制,适用于实时通知、聊天室等场景。
# 终端 1 — 订阅频道
SUBSCRIBE chat:room:1

# 终端 2 — 发布消息
PUBLISH chat:room:1 "Hello, everyone!"

# 模式订阅(通配符)
PSUBSCRIBE chat:room:*

# 查看活跃频道
PUBSUB CHANNELS chat:*
局限性:Pub/Sub 不持久化消息 — 订阅者离线期间发布的消息会丢失。如果需要可靠消息传递,请使用 Redis Stream 或专业消息队列(如 RabbitMQ / Kafka)。

场景 5 分布式锁(Distributed Lock)

使用 Redis 实现跨进程/跨服务器的互斥锁,防止并发冲突。
# 加锁(SET NX + 过期时间 — 原子操作)
SET lock:order:pay:123 "owner:server1:thread42" NX EX 30

# 释放锁(Lua 脚本确保只释放自己的锁)
-- KEYS[1] = lock key, ARGV[1] = owner id
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end
Redlock 算法:单节点 Redis 锁在主从切换时可能失效。对于强一致性要求的场景,应使用 Redlock 算法(在 N 个独立 Redis 节点上加锁,过半成功才视为获取锁)或使用 ZooKeeper / etcd。

场景 6 消息队列(Queue)

使用 Redis List 或 Stream 实现轻量级消息队列。
# === 方案 A:List 实现简单队列 ===
# 生产者
LPUSH queue:emails '{"to":"a@b.com","subject":"Welcome"}'

# 消费者(阻塞式)
BRPOP queue:emails 0

# === 方案 B:Stream 实现可靠队列(推荐) ===
# 创建消费者组
XGROUP CREATE queue:orders $ MKSTREAM

# 生产者
XADD queue:orders * action "create" order_id 12345

# 消费者(消费者组模式)
XREADGROUP GROUP workers worker1 COUNT 10 BLOCK 5000 STREAMS queue:orders >

# 确认处理完成
XACK queue:orders workers "1234567890-0"

# 查看待确认消息(处理失败的)
XPENDING queue:orders workers
Stream vs List:List 是先进先出的简单队列;Stream 支持消费者组、消息确认(ACK)、消息回溯和持久化,更适合生产环境。

场景 7 实时分析(Real-time Analytics)

利用 Redis 的原子操作和丰富数据结构,实现实时统计分析。
# PV 统计(String 计数器)
INCR stats:page:/home:pv:2024-01-01

# UV 统计(HyperLogLog)
PFADD stats:page:/home:uv:2024-01-01 "user:1001"
PFCOUNT stats:page:/home:uv:2024-01-01

# 实时在线用户数(Bitmap)
SETBIT online:users 1001 1    # 用户 1001 上线
SETBIT online:users 1001 0    # 用户 1001 下线
BITCOUNT online:users          # 在线用户总数

# 热点商品排行(Sorted Set)
ZINCRBY hot:products:hourly 1 "product:iphone15"
ZREVRANGE hot:products:hourly 0 9 WITHSCORES

场景 8 地理位置查询(Geo Queries)

Redis GEO 命令基于 Sorted Set 实现,支持经纬度存储和距离计算。
# 添加位置
GEOADD restaurants 116.403963 39.915119 "全聚德"
GEOADD restaurants 116.407526 39.904030 "东来顺"
GEOADD restaurants 121.473701 31.230416 "南翔馒头店"

# 查询两点距离
GEODIST restaurants "全聚德" "东来顺" km   # ≈1.2 km

# 附近搜索(5 km 范围内的餐厅)
GEOSEARCH restaurants FROMLONLAT 116.405 39.910 BYRADIUS 5 km ASC COUNT 10 WITHCOORD WITHDIST

# 获取位置坐标
GEOPOS restaurants "全聚德"

场景 9 全文搜索(RediSearch)

Redis Stack 内置的 RediSearch 模块提供全文搜索、二级索引和聚合能力。
# 创建索引(需要 Redis Stack)
FT.CREATE idx:products ON HASH PREFIX 1 product:
  SCHEMA
    name TEXT WEIGHT 5.0
    description TEXT
    price NUMERIC SORTABLE
    category TAG

# 添加数据
HSET product:1 name "iPhone 15 Pro" description "Apple 最新旗舰手机" price 8999 category "手机"
HSET product:2 name "MacBook Pro M3" description "Apple 高性能笔记本" price 14999 category "笔记本"

# 全文搜索
FT.SEARCH idx:products "Apple 手机"

# 带过滤和排序
FT.SEARCH idx:products "@category:{手机}" SORTBY price ASC LIMIT 0 10

# 聚合查询
FT.AGGREGATE idx:products "*" GROUPBY 1 @category REDUCE COUNT 0 AS count

场景 10 AI 向量搜索(Vector Search)

Redis Stack 支持向量相似度搜索(VSS),可用于 RAG(检索增强生成)、推荐系统等 AI 场景。
# 创建向量索引
FT.CREATE idx:docs ON HASH PREFIX 1 doc:
  SCHEMA
    content TEXT
    embedding VECTOR FLAT 6
      TYPE FLOAT32
      DIM 768
      DISTANCE_METRIC COSINE

# 存储文档和向量(向量为二进制格式)
HSET doc:1 content "Redis 是高性能内存数据库" embedding "\x00\x00..."

# KNN 向量搜索(查找最相似的 5 个文档)
FT.SEARCH idx:docs "*=>[KNN 5 @embedding $query_vec AS score]"
  PARAMS 2 query_vec "\x00\x00..."
  SORTBY score ASC
  RETURN 2 content score
  DIALECT 2
应用场景:将文档通过 Embedding 模型转为向量存入 Redis,查询时将问题也转为向量进行相似度搜索,再将结果送入 LLM 生成答案 — 这就是 RAG 的核心流程。

持久化

RDB 快照持久化

在指定时间间隔内将内存数据快照保存到磁盘(dump.rdb 文件)。通过 fork 子进程完成,不阻塞主进程。
# redis.conf — RDB 配置 save 3600 1 # 3600 秒内至少 1 次写操作则触发快照 save 300 100 # 300 秒内至少 100 次写操作 save 60 10000 # 60 秒内至少 10000 次写操作 dbfilename dump.rdb dir /data rdbcompression yes rdbchecksum yes
优点:文件紧凑,适合备份和灾难恢复;加载速度快。
缺点:两次快照之间的数据可能丢失;fork 大内存实例时可能短暂阻塞。

AOF 追加日志持久化

将每个写操作追加到日志文件(appendonly.aof),重启时重放日志恢复数据。
# redis.conf — AOF 配置 appendonly yes appendfilename "appendonly.aof" appenddirname "appendonlydir" # 同步策略(三选一) appendfsync everysec # 推荐:每秒同步,最多丢失 1 秒数据 # appendfsync always # 每次写操作都同步,最安全但最慢 # appendfsync no # 由操作系统决定,性能最好但可能丢失较多数据 # AOF 重写阈值 auto-aof-rewrite-percentage 100 # AOF 文件比上次重写后增长 100% 时触发重写 auto-aof-rewrite-min-size 64mb # AOF 文件最小达到 64MB 才触发重写
优点:数据安全性更高(最多丢失 1 秒数据);可读的日志格式方便调试。
缺点:文件通常比 RDB 大;恢复速度比 RDB 慢。

混合模式 RDB + AOF(推荐)

Redis 4.0+ 支持混合持久化:AOF 重写时在文件开头写入 RDB 格式的全量数据,之后追加增量 AOF 命令。兼顾了 RDB 的快速加载和 AOF 的数据安全。
# redis.conf — 混合持久化(推荐生产环境使用) appendonly yes aof-use-rdb-preamble yes # 开启混合持久化(Redis 7 默认开启)
生产建议:开启混合持久化 + appendfsync everysec,在大多数场景下是性能与数据安全的最佳平衡点。同时定期将 RDB 快照备份到远程存储(S3 / OSS)。

集群与高可用

Sentinel 哨兵模式

Redis Sentinel 提供主从架构的自动故障转移和监控。适用于数据量不超过单机内存、但需要高可用的场景。
┌──────────────────────────────────────────────┐ │ Sentinel × 3(奇数个) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │Sentinel 1│ │Sentinel 2│ │Sentinel 3│ │ │ └─────┬────┘ └─────┬────┘ └─────┬────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ 监控 / 选举 / 故障转移 / 通知客户端 │ └──────────────────────────────────────────────┘ │ ▼ ┌──────────┐ 复制 ┌──────────┐ │ Master │ ──────────▶ │ Replica 1│ │ (读写) │ │ (只读) │ └──────────┘ └──────────┘ │ 复制 └──────────────────▶ ┌──────────┐ │ Replica 2│ │ (只读) │ └──────────┘
# sentinel.conf sentinel monitor mymaster 127.0.0.1 6379 2 # 2 = 需要 2 个 Sentinel 同意才触发故障转移 sentinel down-after-milliseconds mymaster 5000 # 5 秒无响应判定为主观下线 sentinel failover-timeout mymaster 60000 # 故障转移超时时间 sentinel auth-pass mymaster your-password

Cluster 集群模式

Redis Cluster 将数据分布到多个节点(分片),每个分片有主从副本。适用于数据量超过单机内存或需要极高写吞吐的场景。

核心概念:
- 16384 个哈希槽(Hash Slot),均匀分配到各主节点
- 每个 key 通过 CRC16(key) % 16384 映射到对应的槽
- 支持在线扩缩容(添加/移除节点并迁移槽)
- 每个主节点至少配置一个从节点保障高可用
# 创建 6 节点集群(3 主 3 从)
redis-cli --cluster create \
  192.168.1.1:6379 192.168.1.2:6379 192.168.1.3:6379 \
  192.168.1.4:6379 192.168.1.5:6379 192.168.1.6:6379 \
  --cluster-replicas 1

# 查看集群状态
redis-cli -c cluster info
redis-cli -c cluster nodes

# 添加新节点
redis-cli --cluster add-node 192.168.1.7:6379 192.168.1.1:6379

# 迁移槽(重新分片)
redis-cli --cluster reshard 192.168.1.1:6379
注意:Redis Cluster 不支持跨槽的多 key 操作(如 MGET 跨节点的 key)。可以使用 Hash Tag(如 {user:1}.name)强制相关 key 落在同一个槽。

主从复制 Replication

Redis 原生的主从复制是 Sentinel 和 Cluster 的基础。从节点异步复制主节点数据,提供读扩展和数据冗余。
# 从节点配置(redis.conf) replicaof 192.168.1.1 6379 # 指定主节点地址 masterauth your-master-password # 主节点密码 replica-read-only yes # 从节点只读(推荐) replica-serve-stale-data yes # 复制中断时是否继续响应读请求
复制流程:
1. 从节点发送 PSYNC 命令
2. 首次全量复制:主节点 BGSAVE 生成 RDB → 传输给从节点 → 从节点加载
3. 增量复制:主节点将后续写命令实时发送给从节点
4. 断线重连后优先尝试部分重同步(基于 repl-backlog)
注意:主从复制是异步的,主节点宕机时从节点可能丢失最新的几条写操作。对数据一致性要求极高的场景,需要配合 WAIT 命令或使用 Redis Raft 等强一致方案。

常见问题(50 个)

一、安装与启动 (10 个)

#1 编译报错缺少依赖

make[3]: *** [Makefile:404: adlist.o] Error 1 /bin/sh: cc: command not found
系统缺少 C 编译器或构建工具链。
# Ubuntu / Debian
sudo apt update && sudo apt install -y build-essential

# CentOS / RHEL
sudo yum groupinstall "Development Tools"

# 清理后重新编译
cd redis-stable
make distclean && make

#2 端口被占用无法启动

Could not create server TCP listening socket *:6379: bind: Address already in use
6379 端口已被其他进程占用:
# 查找占用进程
lsof -i :6379
# 或
ss -tlnp | grep 6379

# 终止占用进程
kill -9 <PID>

# 或者修改 Redis 端口
redis-server --port 6380

#3 Docker 容器启动后立即退出

docker: Error response from daemon: Conflict. The container name "/redis" is already in use.
容器名称冲突,先移除旧容器:
# 移除旧容器
docker rm -f redis

# 重新启动
docker run -d --name redis -p 6379:6379 redis:7-alpine

# 如果是其他原因退出,查看日志
docker logs redis

#4 WARNING: overcommit_memory

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition.
Linux 内核内存超额分配策略需要调整,否则 BGSAVE/BGREWRITEAOF 在内存紧张时可能失败:
# 临时生效
echo 1 > /proc/sys/vm/overcommit_memory

# 永久生效
echo "vm.overcommit_memory = 1" >> /etc/sysctl.conf
sysctl -p

#5 WARNING: Transparent Huge Pages

WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis.
THP 会导致 Redis fork 时延迟增大和内存碎片:
# 临时禁用
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

# 永久禁用(添加到 rc.local 或 systemd service)
# /etc/rc.local
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

#6 WARNING: somaxconn 限制

WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
TCP 连接队列长度不足,影响高并发连接:
# 临时调整
echo 1024 > /proc/sys/net/core/somaxconn

# 永久生效
echo "net.core.somaxconn = 1024" >> /etc/sysctl.conf
sysctl -p

#7 配置文件路径错误

Fatal error, can't open config file '/etc/redis/redis.conf': No such file or directory
Redis 找不到指定的配置文件。解决方法:
1. 确认配置文件路径:find / -name "redis.conf" 2>/dev/null
2. 如果使用源码编译安装,配置文件在源码目录中:redis-stable/redis.conf
3. 复制到标准位置:sudo cp redis.conf /etc/redis/redis.conf
4. 指定正确路径启动:redis-server /path/to/redis.conf

#8 Redis 以 root 用户运行警告

WARNING: Redis is running in a root user! It is recommended to run Redis as a non-root user.
以 root 运行有安全风险。创建专用用户:
# 创建 redis 用户
sudo useradd -r -s /sbin/nologin redis

# 设置目录权限
sudo chown -R redis:redis /var/lib/redis /var/log/redis /etc/redis

# 使用 systemd 服务管理(已内置用户切换)
sudo systemctl start redis

#9 jemalloc 编译失败

zmalloc.h:50:10: fatal error: jemalloc/jemalloc.h: No such file or directory
jemalloc 依赖缺失或编译残留导致冲突:
# 方案 1:清理后重新编译
make distclean
make

# 方案 2:使用 libc 分配器(不推荐生产环境)
make MALLOC=libc

#10 systemd 服务启动失败

redis.service: Failed with result 'exit-code'. redis-server: Can't open the log file: Permission denied
日志文件或数据目录权限问题:
# 检查并修复权限
sudo mkdir -p /var/log/redis /var/lib/redis
sudo chown redis:redis /var/log/redis /var/lib/redis

# 查看详细错误
sudo journalctl -u redis -n 50

# 确认 redis.conf 中的路径配置正确
# logfile "/var/log/redis/redis-server.log"
# dir "/var/lib/redis"

二、连接与认证 (10 个)

#11 远程连接被拒绝

Could not connect to Redis at 192.168.1.100:6379: Connection refused
Redis 默认只绑定 127.0.0.1,不接受远程连接:
# 修改 redis.conf
bind 0.0.0.0    # 或指定内网 IP:bind 192.168.1.100

# 同时关闭保护模式(需配合密码使用)
protected-mode no

# 重启 Redis
sudo systemctl restart redis
安全警告:绑定 0.0.0.0 后必须设置密码(requirepass),否则任何人都能访问你的 Redis!历史上大量 Redis 被黑事件都是因为未设置密码+绑定公网 IP。

#12 需要密码认证

NOAUTH Authentication required.
Redis 设置了密码但客户端未提供:
# 命令行连接时指定密码
redis-cli -a your-password

# 或连接后认证
redis-cli
AUTH your-password

# 使用 URL 格式
redis-cli -u redis://:your-password@127.0.0.1:6379

#13 密码错误

WRONGPASS invalid username-password pair or user is disabled.
密码不匹配。Redis 6+ 使用 ACL 系统:
1. 检查 redis.conf 中的 requirepass 设置
2. Redis 6+ 检查 ACL 用户配置:ACL LIST
3. 如果忘记密码,修改配置文件后重启 Redis
4. 使用用户名+密码认证:AUTH username password

#14 最大连接数已满

ERR max number of clients reached
连接数超出限制(默认 10000):
# 查看当前连接数
redis-cli info clients

# 查看当前连接详情
redis-cli client list

# 增大最大连接数(redis.conf)
maxclients 50000

# 动态修改(无需重启)
redis-cli CONFIG SET maxclients 50000

# 排查连接泄漏(未关闭的空闲连接)
redis-cli client list | grep "idle=\|age="
注意:同时需要确保操作系统的 ulimit -n(文件描述符限制)大于 maxclients + 32。

#15 连接超时

Error: Connection timed out redis.exceptions.TimeoutError: Timeout reading from socket
排查思路:
1. 检查网络连通性:telnet redis-host 6379
2. 检查防火墙规则:iptables -L -n | grep 6379
3. 检查 Redis 是否过载:redis-cli info stats 查看 total_connections_received
4. 检查客户端超时配置是否合理
5. 检查是否存在慢查询阻塞:redis-cli SLOWLOG GET 10

#16 protected-mode 拒绝连接

DENIED Redis is running in protected mode because protected mode is enabled and no password is set... If you need to connect from external hosts, you should either set a password or disable protected-mode.
Redis 3.2+ 的保护模式在没有密码且绑定非 localhost 时拒绝外部连接:
# 推荐方案:设置密码
requirepass your-strong-password

# 或者同时做两件事(redis.conf)
bind 192.168.1.100          # 绑定内网 IP
requirepass your-password    # 设置密码
protected-mode yes           # 保持保护模式开启

#17 SSL/TLS 连接失败

Error: Connection reset by peer (when using TLS) SSL_connect: certificate verify failed
TLS 配置不正确:
# redis.conf 中配置 TLS
tls-port 6380
tls-cert-file /path/to/redis.crt
tls-key-file /path/to/redis.key
tls-ca-cert-file /path/to/ca.crt

# 客户端连接
redis-cli --tls --cert /path/to/client.crt --key /path/to/client.key --cacert /path/to/ca.crt -p 6380

#18 ACL 权限不足

NOPERM this user has no permissions to run the 'config' command
Redis 6+ ACL 系统限制了用户可执行的命令:
# 查看当前用户权限
ACL WHOAMI
ACL GETUSER <username>

# 授予权限(管理员操作)
ACL SETUSER myuser +config on
ACL SAVE

# 或修改 ACL 配置文件
# aclfile /etc/redis/users.acl

#19 客户端连接池耗尽

redis.clients.jedis.exceptions.JedisExhaustedPoolException: Could not get a resource from the pool Cannot get Jedis connection: pool is exhausted
客户端连接池配置不当或存在连接泄漏:
# Java (Jedis) 连接池配置建议
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50);          // 最大连接数
config.setMaxIdle(20);           // 最大空闲连接
config.setMinIdle(5);            // 最小空闲连接
config.setMaxWaitMillis(3000);   // 等待超时
config.setTestOnBorrow(true);    // 借用时测试连接
排查方向:确保每次使用后归还连接(try-with-resources);适当增大连接池大小;检查是否有慢查询占用连接。

#20 跨数据中心延迟高

Redis 响应延迟 > 100ms(正常应 < 1ms)
远距离网络导致高延迟:
1. 使用 Pipeline 批量发送命令减少 RTT:一次发送多条命令
2. 部署读副本到就近数据中心
3. 使用 Redis Cluster 的就近读:READONLY 命令
4. 考虑在各数据中心部署独立 Redis 实例 + 应用层同步
5. 检查是否存在慢查询:SLOWLOG GET 10

三、内存管理 (10 个)

#21 内存超出限制(OOM)

OOM command not allowed when used memory > 'maxmemory'.
Redis 使用内存超过 maxmemory 限制,且淘汰策略为 noeviction(默认):
# 查看内存使用情况
redis-cli info memory

# 设置最大内存和淘汰策略
maxmemory 4gb
maxmemory-policy allkeys-lru   # 在所有 key 中 LRU 淘汰

# 常用淘汰策略:
# volatile-lru   — 仅对设置了过期时间的 key 进行 LRU 淘汰
# allkeys-lru    — 对所有 key 进行 LRU 淘汰(推荐缓存场景)
# volatile-ttl   — 淘汰 TTL 最小的 key
# allkeys-lfu    — 对所有 key 进行 LFU 淘汰(Redis 4.0+,推荐)
# noeviction     — 不淘汰,内存满时拒绝写入(默认)

#22 内存碎片率过高

mem_fragmentation_ratio: 2.5(正常应在 1.0 ~ 1.5)
内存碎片率 = used_memory_rss / used_memory。大于 1.5 说明碎片严重。
# 查看碎片率
redis-cli info memory | grep mem_fragmentation_ratio

# Redis 4.0+ 开启自动碎片整理
CONFIG SET activedefrag yes

# 调整碎片整理参数
CONFIG SET active-defrag-threshold-lower 10    # 碎片率 > 10% 时开始整理
CONFIG SET active-defrag-threshold-upper 100   # 碎片率 > 100% 时全力整理
CONFIG SET active-defrag-cycle-min 1           # 最小 CPU 占比 1%
CONFIG SET active-defrag-cycle-max 25          # 最大 CPU 占比 25%

#23 大 Key 占用过多内存

某个 Hash/List/Set 占用数百 MB 甚至 GB 内存
大 Key 会导致内存不均匀、阻塞主线程、影响集群迁移:
# 扫描大 Key
redis-cli --bigkeys

# 更精确的分析(Redis 4.0+)
redis-cli --memkeys

# 查看指定 key 的内存使用
redis-cli MEMORY USAGE mykey

# 解决方案:拆分大 Key
# 例:将一个 100 万元素的 Hash 拆分为 100 个子 Hash
# user:data → user:data:0, user:data:1, ..., user:data:99
# 分桶算法:bucket = CRC32(field) % 100
禁忌:不要对大 Key 直接使用 DEL 命令!删除数百万元素的 Key 会阻塞 Redis 数秒。请使用 UNLINK(异步删除,Redis 4.0+)。

#24 BGSAVE 导致内存翻倍

Can't save in background: fork: Cannot allocate memory
Redis fork 子进程时使用 Copy-On-Write(COW),写操作频繁时物理内存可能翻倍:
# 确保 overcommit_memory = 1
echo 1 > /proc/sys/vm/overcommit_memory

# 预留足够内存(建议 maxmemory 不超过物理内存的 50-60%)
# 例:8GB 服务器,maxmemory 设为 4gb

# 减少 fork 期间的写放大
# 禁用 THP
echo never > /sys/kernel/mm/transparent_hugepage/enabled

# 监控 COW 内存
redis-cli info persistence | grep rdb_last_cow_size

#25 过期 Key 内存未释放

已设置 TTL 的 Key 过期后,内存使用未下降
Redis 使用惰性删除 + 定期删除策略,可能导致已过期但未被访问的 Key 暂时占用内存:
1. 惰性删除:访问 key 时检查是否过期
2. 定期删除:每 100ms 随机检查一批 key
3. 如果大量 key 同时过期,删除可能不及时
# 手动触发扫描(生产环境慎用)
redis-cli SCAN 0 COUNT 10000

# 调优定期删除频率(redis.conf)
hz 25   # 默认 10,增大可加快过期 key 回收(但增加 CPU)

# 避免大量 key 同时过期 — 给 TTL 加随机偏移
# TTL = base_ttl + random(0, 300)

#26 内存使用远超预期

存储的数据只有 1GB,但 used_memory 显示 3GB
Redis 的每个 key-value 有额外的内存开销(对象头、SDS 头、dictEntry 等):
1. 使用 MEMORY USAGE key 查看单个 key 的实际内存
2. 使用 OBJECT ENCODING key 查看编码类型
3. 优化策略:
  - 短 key 名称(u:1:nuser:1:name 省内存)
  - 使用 Hash 代替多个 String(共享 key 开销)
  - 调整 listpack/ziplist 阈值让更多数据使用紧凑编码
  - 使用 OBJECT HELP 了解编码切换条件

#27 Swap 使用导致性能暴跌

Redis 响应延迟从 < 1ms 跃升到 100ms+
Redis 数据被交换到 Swap 分区,磁盘 I/O 导致性能严重下降:
# 检查 Redis 进程是否使用 Swap
cat /proc/$(pidof redis-server)/smaps | grep Swap

# 检查系统 Swap 使用
free -h
swapon --show

# 解决方案
# 1. 增加物理内存或降低 maxmemory
# 2. 降低 swappiness
echo 1 > /proc/sys/vm/swappiness

# 3. 生产环境建议完全禁用 Swap(确保内存充足)
swapoff -a
原则:Redis 是内存数据库,绝对不应该使用 Swap。如果数据量超过物理内存,应该使用 Redis Cluster 分片或淘汰策略,而不是依赖 Swap。

#28 Redis 进程被 OOM Killer 杀死

redis-server 进程突然消失,dmesg 中显示: Out of memory: Kill process (redis-server) score xxx
Linux OOM Killer 在系统内存不足时杀死内存占用最大的进程:
# 查看是否被 OOM Kill
dmesg | grep -i "oom\|kill"

# 调低 Redis 进程的 OOM 分数(降低被杀优先级)
echo -17 > /proc/$(pidof redis-server)/oom_adj

# 根本解决方案
# 1. 设置合理的 maxmemory(留出操作系统和 fork 所需内存)
# 2. 增加服务器内存
# 3. 使用淘汰策略而非 noeviction

#29 内存回收不及时(lazy-free)

DEL 大 Key 时 Redis 阻塞数秒
Redis 4.0+ 支持异步删除(lazy-free),避免大 Key 删除阻塞主线程:
# 使用 UNLINK 代替 DEL(异步删除)
UNLINK large_key

# 在 redis.conf 中开启 lazy-free
lazyfree-lazy-eviction yes       # 淘汰时异步释放
lazyfree-lazy-expire yes         # 过期时异步释放
lazyfree-lazy-server-del yes     # 内部删除时异步释放
lazyfree-lazy-user-del yes       # DEL 命令也异步(Redis 6.0+)
lazyfree-lazy-user-flush yes     # FLUSHDB/FLUSHALL 异步(Redis 6.2+)

#30 MEMORY DOCTOR 报告异常

redis-cli MEMORY DOCTOR 返回 "Peak memory is significantly higher than current memory"
内存峰值远高于当前使用量,说明曾经有大量临时数据或内存碎片:
# 查看内存诊断信息
redis-cli MEMORY DOCTOR

# 查看内存统计
redis-cli MEMORY STATS

# 重置峰值统计
redis-cli CONFIG RESETSTAT

# 如果碎片率高,参考 #22 开启碎片整理
# 如果是临时大数据操作导致,评估是否需要优化业务逻辑

四、持久化问题 (10 个)

#31 RDB 快照失败

MISCONF Redis is configured to save RDB snapshots, but it is currently not able to persist on disk.
RDB 持久化失败后 Redis 拒绝写入:
# 临时允许写入(不推荐长期使用)
CONFIG SET stop-writes-on-bgsave-error no

# 排查 RDB 失败原因
# 1. 磁盘空间不足
df -h

# 2. 权限问题
ls -la /data/dump.rdb
chown redis:redis /data

# 3. 内存不足无法 fork
# 参考 #24 解决 fork 内存问题

# 手动触发 RDB 快照测试
redis-cli BGSAVE

#32 AOF 文件损坏

Bad file format reading the append only file: make a backup, then use ./redis-check-aof --fix
AOF 文件因断电等原因损坏:
# 1. 先备份
cp appendonly.aof appendonly.aof.bak

# 2. 使用修复工具
redis-check-aof --fix appendonly.aof

# 3. 对于 Redis 7+ 的 Multi-Part AOF
redis-check-aof --fix appendonlydir/appendonly.aof.1.incr.aof

# 4. 确认修复结果后重启 Redis
redis-server /etc/redis/redis.conf
注意:修复工具会截断损坏部分之后的数据。建议同时保留 RDB 备份作为兜底。

#33 AOF 重写阻塞

Background AOF rewrite terminated with error fork: Cannot allocate memory (AOF rewrite)
AOF 重写同样需要 fork 子进程,内存不足时会失败:
1. 参考 #24 确保 overcommit_memory = 1
2. 避免 RDB 和 AOF 重写同时进行:no-appendfsync-on-rewrite yes
3. 调高重写阈值减少频率:auto-aof-rewrite-percentage 200
4. 在低峰期手动触发:BGREWRITEAOF

#34 AOF 文件持续增长

AOF 文件已达数十 GB,磁盘空间告急
AOF 重写未正常工作或阈值设置不合理:
# 手动触发 AOF 重写
redis-cli BGREWRITEAOF

# 检查 AOF 重写状态
redis-cli info persistence | grep aof

# 调整自动重写阈值
CONFIG SET auto-aof-rewrite-percentage 100
CONFIG SET auto-aof-rewrite-min-size 64mb

# 检查是否有大量写操作导致快速增长
redis-cli info stats | grep instantaneous_ops_per_sec

#35 RDB 和 AOF 同时存在,加载哪个?

同时存在 dump.rdb 和 appendonly.aof,Redis 启动时数据不符合预期
Redis 的加载优先级:
1. 如果开启了 AOF(appendonly yes),优先加载 AOF
2. 只有 AOF 关闭时才加载 RDB
3. 如果两者都关闭,不加载任何数据

如果想从 RDB 恢复数据(忽略 AOF):
# 1. 临时关闭 AOF
CONFIG SET appendonly no

# 2. 重启 Redis(此时会加载 RDB)
redis-cli SHUTDOWN
redis-server /etc/redis/redis.conf

# 3. 重新开启 AOF
CONFIG SET appendonly yes
BGREWRITEAOF

#36 RDB 文件损坏

Short read or OOM loading DB. Unrecoverable error, aborting now. RDB CRC error
RDB 文件损坏导致加载失败:
# 检查 RDB 文件完整性
redis-check-rdb /data/dump.rdb

# 如果有备份,使用备份恢复
cp /backup/dump.rdb /data/dump.rdb

# 如果没有备份但有 AOF,切换到 AOF 加载
# 修改 redis.conf:appendonly yes
# 重启 Redis

# 预防措施:开启 RDB 校验
rdbchecksum yes

#37 磁盘 I/O 延迟导致 AOF 卡顿

Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.
磁盘写入速度跟不上 AOF 追加速度:
# 排查磁盘 I/O
iostat -x 1

# 解决方案
# 1. 使用 SSD 替换 HDD
# 2. AOF 重写期间跳过 fsync(牺牲少量安全性)
no-appendfsync-on-rewrite yes

# 3. 降低 fsync 频率(如果可以接受更多数据丢失风险)
appendfsync everysec   # 不要用 always

# 4. 将 AOF 目录放在高速磁盘上

#38 BGSAVE 子进程被杀

Background saving error rdb_last_bgsave_status: err
BGSAVE 子进程因 OOM 或其他原因被终止:
# 查看详细原因
redis-cli info persistence | grep rdb_last_bgsave
dmesg | tail -20

# 常见原因和解决
# 1. OOM Killer 杀掉子进程 → 增加内存或降低 maxmemory
# 2. 磁盘空间不足 → 清理磁盘
# 3. /data 目录无写权限 → chown redis:redis /data

# 重新触发
redis-cli BGSAVE

#39 从 RDB 恢复数据很慢

Redis 启动加载 RDB 文件耗时数分钟
大 RDB 文件加载时间与数据量和 key 数量相关:
1. 使用 SSD 加速磁盘读取
2. 优化数据结构减少 key 数量(使用 Hash 聚合小 key)
3. 查看加载进度:Redis 7+ 在日志中输出加载进度百分比
4. 考虑使用 Redis Cluster 分片,每个节点数据量更小,加载更快
5. 如果是开发环境,可以清空不需要的数据再生成 RDB

#40 混合持久化配置不生效

开启 aof-use-rdb-preamble 后 AOF 文件仍然全是文本命令
混合持久化仅在 AOF 重写时生效,不影响已有的 AOF 文件:
# 确认配置
CONFIG GET aof-use-rdb-preamble   # 应返回 "yes"

# 手动触发 AOF 重写使配置生效
BGREWRITEAOF

# 验证:重写后的 AOF 文件开头是二进制 RDB 格式
# 可以用 file 命令检查
file appendonlydir/appendonly.aof.1.base.rdb

五、性能与运维 (10 个)

#41 KEYS 命令导致阻塞

执行 KEYS * 后 Redis 无响应数秒
KEYS 命令遍历所有 key,时间复杂度 O(N),key 数量多时会阻塞 Redis:
# 禁止在生产环境使用 KEYS!
# 使用 SCAN 替代(游标式迭代,不阻塞)
SCAN 0 MATCH "user:*" COUNT 100

# 通过 ACL 禁用 KEYS 命令
ACL SETUSER default -keys

# 或通过 rename-command 禁用
rename-command KEYS ""
生产环境禁令:永远不要在生产环境执行 KEYS *!同理,FLUSHALLFLUSHDBDEBUG 等危险命令也应禁用或重命名。

#42 慢查询排查

部分请求响应时间异常,延迟 spike
使用 Redis 内置的慢查询日志排查:
# 配置慢查询阈值(微秒)
CONFIG SET slowlog-log-slower-than 10000   # 10ms
CONFIG SET slowlog-max-len 128

# 查看慢查询日志
SLOWLOG GET 10

# 查看慢查询数量
SLOWLOG LEN

# 重置
SLOWLOG RESET

# 常见慢查询原因:
# 1. O(N) 命令操作大集合(HGETALL、SMEMBERS、LRANGE 0 -1)
# 2. 大 Key 的 DEL 操作
# 3. Lua 脚本执行时间过长
# 4. SORT 命令排序大量数据

#43 延迟诊断(Latency)

Redis 整体延迟偏高,但慢查询日志中没有明显异常
慢查询只记录命令执行时间,不包括排队等待时间。使用 Redis 延迟监控工具:
# 延迟基准测试
redis-cli --latency
redis-cli --latency-history
redis-cli --latency-dist

# Redis 内置延迟监控(Redis 2.8.13+)
CONFIG SET latency-monitor-threshold 5   # 记录 > 5ms 的事件
LATENCY LATEST
LATENCY HISTORY event-name

# 检查事件循环延迟
redis-cli DEBUG SLEEP 0   # 触发一次延迟检测

# 常见非命令延迟来源:
# 1. fork(BGSAVE/BGREWRITEAOF)
# 2. AOF fsync
# 3. 操作系统 Swap
# 4. 网络问题
# 5. 过期 key 的集中删除

#44 Pipeline 使用不当

批量操作效率低,每秒只能处理几千次请求
逐条发送命令会受 RTT(网络往返时间)限制。使用 Pipeline 批量发送:
# redis-cli 管道模式
cat commands.txt | redis-cli --pipe

# Python 示例
import redis
r = redis.Redis()
pipe = r.pipeline()
for i in range(10000):
    pipe.set(f"key:{i}", f"value:{i}")
pipe.execute()   # 一次性发送所有命令

# 注意事项:
# 1. Pipeline 不是原子操作(如需原子性用 MULTI/EXEC 或 Lua)
# 2. 单次 Pipeline 不要太大(建议 1000~10000 条)
# 3. Pipeline 中的命令结果在 execute() 后统一返回

#45 热点 Key(Hot Key)问题

集群中某个节点 CPU 100%,其他节点空闲
某个 Key 被频繁访问,导致对应节点过载:
# 发现热点 Key
redis-cli --hotkeys   # 需要开启 LFU 淘汰策略
redis-cli MONITOR     # 实时监控(短时间使用,影响性能)

# 解决方案
# 1. 本地缓存:在应用层增加 L1 缓存(如 Caffeine/Guava Cache)
# 2. 读写分离:将读请求分发到多个从节点
# 3. Key 拆分:将热点 Key 拆分为多个子 Key(如 hotkey:0 ~ hotkey:9)
#    读取时随机选择一个子 Key
# 4. 使用 Redis Cluster 的 READONLY 命令让从节点分担读请求

#46 缓存穿透

大量请求查询不存在的数据,缓存未命中,压力直接打到数据库
恶意请求或业务逻辑导致查询不存在的 key:
# 方案 1:缓存空值
# 查询数据库为空时,在 Redis 中缓存空值
SET user:999999 "" EX 300   # 缓存空值 5 分钟

# 方案 2:布隆过滤器(Redis Stack / RedisBloom)
# 将所有合法 ID 加入布隆过滤器
BF.ADD valid_users "user:1"
BF.ADD valid_users "user:2"

# 请求前先检查
BF.EXISTS valid_users "user:999999"   # 0 → 不存在,直接拒绝

# 方案 3:请求参数校验
# 在接入层拦截非法请求(如负数 ID、超长字符串等)

#47 缓存雪崩

大量缓存同时过期,请求涌入数据库导致数据库崩溃
大量 key 设置了相同的 TTL,同一时刻集中过期:
1. 随机 TTL:给每个 key 的 TTL 加随机偏移
# 基础 TTL + 随机偏移(0~300 秒)
import random
ttl = 3600 + random.randint(0, 300)
r.setex(f"cache:{key}", ttl, value)
2. 永不过期 + 异步更新:缓存不设 TTL,后台线程定期刷新
3. 多级缓存:L1(本地缓存)→ L2(Redis)→ DB
4. 熔断降级:数据库压力过大时返回降级数据或排队等待

#48 缓存击穿

某个热点 Key 过期瞬间,大量并发请求同时打到数据库
单个热点 key 失效瞬间的并发问题:
# 方案 1:互斥锁(分布式锁)
# 缓存未命中时,先获取锁,再查数据库,再写缓存
SET lock:cache:hotkey "1" NX EX 10
# 获取锁成功 → 查询 DB → 写入缓存 → 释放锁
# 获取锁失败 → sleep 后重试读缓存

# 方案 2:逻辑过期
# 缓存中存储过期时间字段,实际不设 Redis TTL
HSET cache:hotkey data "..." expire_at 1704067200
# 读取时判断 expire_at,过期则异步更新,返回旧数据

# 方案 3:预热
# 热点 key 过期前主动刷新

#49 集群模式 MOVED / ASK 重定向

MOVED 12345 192.168.1.2:6379 ASK 12345 192.168.1.3:6379
Redis Cluster 中 key 不在当前节点的槽上:
MOVED:槽已永久迁移到其他节点,客户端应更新槽映射缓存
ASK:槽正在迁移中,临时重定向
# 使用集群模式连接(自动处理重定向)
redis-cli -c -h 192.168.1.1 -p 6379

# 客户端库需启用集群模式
# Python: redis.RedisCluster(...)
# Java: JedisCluster / Lettuce cluster mode

# 查看槽分配
redis-cli -c cluster slots

# 如果频繁出现,检查集群是否正在 rebalance
redis-cli -c cluster info

#50 Redis 版本升级注意事项

从 Redis 6.x 升级到 7.x 后出现兼容性问题
Redis 大版本升级需要注意的关键变更:
Redis 7.0 重要变更:
- Multi-Part AOF:AOF 文件拆分为多个文件存放在 appendonlydir 目录
- Listpack 全面替代 Ziplist
- redis.conf 中部分配置项名称变更
- Lua 脚本引擎升级,部分行为变化

升级建议:
1. 先在测试环境验证兼容性
2. 备份所有数据(RDB + AOF + 配置文件)
3. 先升级从节点,再手动故障转移后升级原主节点
4. 阅读官方 Release Notes
5. 检查客户端库是否兼容新版本
注意:Redis 7.4+ 更改了开源协议(RSALv2 / SSPLv1 双协议),不再是 BSD。如果你的使用场景受协议影响,可以考虑社区分支 Valkey。