构建高可用的Redis 集群

构建高可用的Redis 集群-- pd的后端笔记

文章目录

    • [构建高可用的Redis 集群-- pd的后端笔记](#构建高可用的Redis 集群-- pd的后端笔记)
  • [主从复制(Replication)------ 构建高可用 Redis 的基石](#主从复制(Replication)—— 构建高可用 Redis 的基石)
    • [🧠 为什么需要主从复制?](#🧠 为什么需要主从复制?)
    • [🔷 一、主从复制原理(异步复制)](#🔷 一、主从复制原理(异步复制))
    • [🔧 二、如何配置主从复制?](#🔧 二、如何配置主从复制?)
    • [🌰 三、Python 中如何使用主从?](#🌰 三、Python 中如何使用主从?)
    • [🔍 四、监控主从状态](#🔍 四、监控主从状态)
    • [⚠️ 五、主从复制的局限性](#⚠️ 五、主从复制的局限性)
    • [✅ 六、生产环境最佳实践](#✅ 六、生产环境最佳实践)
  • [哨兵模式(Sentinel)------ Redis 的高可用守护者](#哨兵模式(Sentinel)—— Redis 的高可用守护者)
    • [🧠 为什么需要 Sentinel?](#🧠 为什么需要 Sentinel?)
    • [🔷 一、Sentinel 架构与核心组件](#🔷 一、Sentinel 架构与核心组件)
    • [🔍 二、Sentinel 工作原理(四步走)](#🔍 二、Sentinel 工作原理(四步走))
    • [🔧 三、如何配置 Sentinel?](#🔧 三、如何配置 Sentinel?)
    • [🌰 四、Python 客户端如何连接 Sentinel?](#🌰 四、Python 客户端如何连接 Sentinel?)
    • [🔍 五、监控 Sentinel 状态](#🔍 五、监控 Sentinel 状态)
    • [✅ 六、生产环境最佳实践](#✅ 六、生产环境最佳实践)
  • [Redis Cluster ------ 水平扩展 Redis 的官方方案](#Redis Cluster —— 水平扩展 Redis 的官方方案)
    • [🔷 一、核心概念:哈希槽(Hash Slot)](#🔷 一、核心概念:哈希槽(Hash Slot))
    • [🔷 二、集群架构要求](#🔷 二、集群架构要求)
    • [🔧 三、如何搭建 Redis Cluster?](#🔧 三、如何搭建 Redis Cluster?)
    • [🌰 四、Python 客户端如何连接 Cluster?](#🌰 四、Python 客户端如何连接 Cluster?)
    • [🔍 五、集群内部如何工作?](#🔍 五、集群内部如何工作?)
    • 六、重要限制与注意事项
      • [📖 Hash Tag ------ 让多个 key 落在同一个槽的魔法](#📖 Hash Tag —— 让多个 key 落在同一个槽的魔法)
    • [🛠️ 七、运维常用命令](#🛠️ 七、运维常用命令)
    • [✅ 八、生产环境最佳实践](#✅ 八、生产环境最佳实践)
  • [Redis 性能调优 ------ 榨干每一分性能](#Redis 性能调优 —— 榨干每一分性能)
    • [🔷 一、配置调优(redis.conf)](#🔷 一、配置调优(redis.conf))
    • [🔷 二、命令级优化(避坑指南)](#🔷 二、命令级优化(避坑指南))
    • [🔷 三、内存优化(防 OOM + 降成本)](#🔷 三、内存优化(防 OOM + 降成本))
    • [🔷 四、网络与客户端优化](#🔷 四、网络与客户端优化)
    • [🛠️ 五、性能诊断 checklist](#🛠️ 五、性能诊断 checklist)
    • [✅ 六、终极建议:压测 + 监控](#✅ 六、终极建议:压测 + 监控)

主从复制(Replication)------ 构建高可用 Redis 的基石

🧠 为什么需要主从复制?

单机 Redis 有两大致命问题:

  1. 单点故障:Redis 挂了 → 整个服务不可用
  2. 读性能瓶颈:所有请求压在一台机器上

主从复制(Replication) 就是解决方案:

  • 1 个主节点(Master):负责写 + 读
  • N 个从节点(Slave / Replica):只读,数据与主节点最终一致
  • 实现:读写分离 + 故障转移预备

🔑 核心价值:提升可用性 + 扩展读能力

🔷 一、主从复制原理(异步复制)

✅ 工作流程(简化版)

  1. 从节点连接主节点,发送 PSYNC 命令(Redis 2.8+ 使用部分同步)
  2. 主节点 fork 子进程,生成 RDB 快照(全量同步)
  3. 主节点将 RDB 文件传给从节点
  4. 从节点清空旧数据,加载 RDB
  5. 主节点将同步期间的写命令缓存,RDB 传输完后追加发送
  6. 此后,主节点每执行一个写命令,就立即转发给所有从节点(增量同步)

🔄 这个过程叫 "异步复制" ------ 主节点不等从节点确认,继续处理新请求。

📊 全量同步 vs 部分同步

类型 触发条件 说明
全量同步(Full Resync) - 从节点初次连接 - 复制偏移量(offset)不匹配 - 主节点重启 传输整个 RDB,开销大
部分同步(Partial Resync) 网络闪断后快速重连 只传断连期间的命令(靠 复制积压缓冲区 replication backlog)

💡 复制积压缓冲区:主节点维护的一个固定大小环形队列(默认 1MB),用于缓存最近的写命令,供从节点断线重连时"补课"。

🔧 二、如何配置主从复制?

方式 1:动态配置(推荐)

bash 复制代码
# 在从节点执行(临时生效)
127.0.0.1:6380> REPLICAOF 127.0.0.1 6379
OK

方式 2:配置文件(永久生效)

在从节点的 redis.conf 中添加:

conf 复制代码
replicaof 192.168.1.100 6379
# 如果主节点有密码
masterauth your_redis_password
# Redis 6+ 推荐用
replica-serve-stale-data yes  # 主挂了,从是否继续提供旧数据?

方式 3:Docker / Kubernetes

通过环境变量或启动参数指定主节点地址。

🌰 三、Python 中如何使用主从?

场景:读写分离

python 复制代码
import redis

# 主节点(写)
master = redis.Redis(host="192.168.1.100", port=6379)

# 从节点(读)
slave1 = redis.Redis(host="192.168.1.101", port=6380)
slave2 = redis.Redis(host="192.168.1.102", port=6380)

# 写操作 → 主
master.set("user:1001:name", "Alice")

# 读操作 → 从(可轮询或随机)
name = slave1.get("user:1001:name")  # 最终一致,可能有毫秒级延迟

⚠️ 注意一致性:

刚写完主,立刻从从读,可能读不到(因为复制是异步的)!

对强一致性要求高的场景,读也走主。

🔍 四、监控主从状态

查看复制信息(在主或从节点执行):

bash 复制代码
127.0.0.1:6379> INFO REPLICATION

关键字段:

text 复制代码
# 主节点视角
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=12345,lag=0

# 从节点视角
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up      # 是否连接正常
slave_read_only:1          # 从节点是否只读(默认是)

⚠️ 五、主从复制的局限性

问题 说明
不能自动故障转移 主挂了,从不会自动变主 → 需要 哨兵(Sentinel)
写能力无法扩展 所有写仍压在主节点
异步复制有延迟 从节点数据是"最终一致",非实时
从节点只读 不能分担写压力

✅ 所以:主从复制只是高可用的第一步,不是终点!

✅ 六、生产环境最佳实践

建议 说明
至少 1 主 2 从 避免单从节点故障导致无备份
从节点开启 AOF 防止从节点重启后全量同步(可从 AOF 恢复)
限制主节点内存 避免 RDB 太大导致全量同步超时
监控复制延迟 INFO REPLICATIONlag 字段告警
不要在从节点执行耗时命令 KEYS *,会阻塞复制线程

常见问题排查

❌ 问题 1:从节点 master_link_status: down

  • 检查网络连通性(telnet master_ip 6379)
  • 检查主节点是否设置了 requirepass,从节点是否配了 masterauth
  • 检查防火墙/安全组

❌ 问题 2:频繁全量同步

  • 原因:复制积压缓冲区太小(默认 1MB)
  • 解决:增大 repl-backlog-size(如 128mb)

❌ 问题 3:从节点数据不一致

  • 检查是否有人误操作直接写从节点(应设 replica-read-only yes)
  • 检查主从 Redis 版本是否兼容

哨兵模式(Sentinel)------ Redis 的高可用守护者

🧠 为什么需要 Sentinel?

主从复制解决了读扩展和数据备份,但无法自动处理主节点故障:

  • 主节点宕机 → 所有写入失败
  • 运维手动切换 → 慢(几分钟)、易出错
  • 客户端不知道新主是谁 → 服务长时间中断

Sentinel 就是 Redis 官方提供的高可用解决方案:

  • 自动监控主从健康状态
  • 主挂了?→ 自动选一个从节点提升为新主
  • 通知所有从节点:"认新老大!"
  • 通知客户端:"主地址变了!"

🔑 核心价值:无人值守的自动故障转移(Failover)

🔷 一、Sentinel 架构与核心组件

✅ 基本拓扑

text 复制代码
+------------------+      +------------------+
|   Redis Master   |<---->|   Sentinel 1     |
+------------------+      +------------------+
         ^                         ^
         | Replication             | Monitoring
         v                         v
+------------------+      +------------------+
|  Redis Replica 1 |<---->|   Sentinel 2     |
+------------------+      +------------------+
         ^                         ^
         |                         |
         v                         v
+------------------+      +------------------+
|  Redis Replica 2 |<---->|   Sentinel 3     |
+------------------+      +------------------+

📌 关键原则:

  1. Sentinel 本身也要高可用 → 至少部署 3 个 Sentinel 实例
  2. Sentinel 不存储数据 → 只做监控、通知、决策
  3. 客户端通过 Sentinel 获取主节点地址 → 不再硬编码主 IP

🔍 二、Sentinel 工作原理(四步走)

步骤 1️⃣:监控(Monitoring)

  • 每个 Sentinel 每秒向所有 Redis 节点发送 PING
  • 如果主节点 连续 down-after-milliseconds 毫秒无响应 → 标记为 主观下线(SDOWN)

步骤 2️⃣:协商(Agreement)

  • Sentinel 之间互相通信(通过 Redis 发布/订阅)
  • 如果 多数 Sentinel(quorum) 都认为主挂了 → 标记为 客观下线(ODOWN)
    💡 quorum 建议 = (N/2 + 1)
    • 3 个 Sentinel → quorum=2
    • 5 个 Sentinel → quorum=3

步骤 3️⃣:选举(Leader Election)

  • 所有 Sentinel 中选举一个 Leader(使用 Raft-like 算法)
  • Leader 负责执行故障转移

步骤 4️⃣:故障转移(Failover)

  1. Leader 从从节点中选一个最优的(优先级、复制偏移量、runid 字典序)
  2. 向该从节点发送 REPLICAOF NO ONE → 提升为主
  3. 向其他从节点发送 REPLICAOF new_master_ip new_master_port → 认新主
  4. 更新 Sentinel 内部配置,并通知所有客户端

✅ 整个过程通常 10~30 秒完成!

🔧 三、如何配置 Sentinel?

  1. 准备 Redis 主从(假设已配置好)
  • 主:192.168.1.10:6379
  • 从1:192.168.1.11:6379
  • 从2:192.168.1.12:6379

redis-sentinel 是 Redis 官方发行版自带的程序,从 Redis 2.8+ 开始就内置了 Sentinel 功能。

  1. 创建 sentinel.conf(每个 Sentinel 节点一份)
conf 复制代码
# 监控名为 "mymaster" 的主节点
sentinel monitor mymaster 192.168.1.10 6379 2

# 主节点无响应多久算主观下线(毫秒)
sentinel down-after-milliseconds mymaster 5000

# 故障转移超时时间(毫秒)
sentinel failover-timeout mymaster 60000

# 最多同时有几个从节点同步新主(避免网络拥塞)
sentinel parallel-syncs mymaster 1

# 如果主节点有密码
sentinel auth-pass mymaster your_redis_password

⚠️ 关键参数解释:

  • 2 = quorum(至少 2 个 Sentinel 同意才算主挂了)
  • down-after-milliseconds:建议 3~10 秒,太短易误判,太长恢复慢
  1. 启动 Sentinel
bash 复制代码
redis-sentinel /path/to/sentinel.conf
# 或
redis-server /path/to/sentinel.conf --sentinel

✅ 建议:Sentinel 和 Redis 实例部署在不同机器,避免单点故障。

🌰 四、Python 客户端如何连接 Sentinel?

不要直接连 Redis!要通过 Sentinel 获取主节点地址:

python 复制代码
from redis.sentinel import Sentinel

# 连接 Sentinel 集群(提供多个 Sentinel 地址)
sentinel = Sentinel([
    ('192.168.1.20', 26379),
    ('192.168.1.21', 26379),
    ('192.168.1.22', 26379)
], socket_timeout=0.1)

# 获取主节点连接(自动发现当前主)
master = sentinel.master_for('mymaster', socket_timeout=0.1)

# 获取从节点连接(用于读)
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)

# 正常使用
master.set('key', 'value')
print(slave.get('key'))

✅ 优势:

  • 主切换后,下次调用 master_for() 会自动返回新主地址
  • 客户端无需重启!

🔍 五、监控 Sentinel 状态

查看 Sentinel 信息:

bash 复制代码
redis-cli -p 26379

# 列出所有监控 mymaster 的 Sentinel 实例信息(包括自己)。
sentinel sentinels mymaster
# 查看被监控的主节点(master)的详细信息。
# ✅ 用途:快速确认"现在谁是主"、"主是否健康"、"集群规模"。
sentinel master mymaster
# 列出所有从节点(replicas/slaves)的详细信息。
# ✅ 用途:排查"某个从节点是否掉队"、"哪个从最适合升主"。
# slave-priority 故障转移时的优先级(越小越优先)
sentinel replicas mymaster

✅ 六、生产环境最佳实践

建议 说明
至少 3 个 Sentinel 避免脑裂(split-brain)
跨机房部署 防止单机房故障导致 Sentinel 全挂
监控 Sentinel 日志 关注 +switch-master 事件
测试故障转移 定期演练(如 kill -9 redis-master
客户端重试机制 写操作失败时自动重试(应对切换窗口)

Sentinel 的局限性

问题 说明
不能扩展写能力 仍只有一个主节点处理写
切换期间短暂不可用 Failover 过程中写入会失败(通常 < 30 秒)
配置复杂 需维护额外的 Sentinel 进程
不适合超大规模 节点数 > 100 时管理困难

Redis Cluster ------ 水平扩展 Redis 的官方方案

🧠 为什么需要 Redis Cluster?

架构 优点 缺点
单机 简单 内存/QPS 有上限
主从 + Sentinel 高可用、读扩展 写无法扩展,数据仍在单机
Redis Cluster ✅ 自动分片 ✅ 高可用 ✅ 写/读都可扩展 配置稍复杂

🔑 核心思想:

把 16384 个哈希槽(hash slots) 分配给多个主节点,每个 key 属于一个槽,从而实现数据分布 + 并行处理。

🔷 一、核心概念:哈希槽(Hash Slot)

  • Redis Cluster 将整个 key 空间划分为 16384 个槽(0 ~ 16383)
  • 每个主节点负责一部分槽
  • key → slot 的映射公式:
python 复制代码
slot = CRC16(key) % 16384

🌰 举例:

  • user:1001 → CRC16 → 12345 → slot 12345
  • 如果 slot 12345 分配给 Node A,则 user:1001 存在 Node A

✅ 优势:

  • 无需中心路由表:客户端可本地计算 slot
  • 扩容/缩容只需迁移部分槽,不影响其他数据

🔷 二、集群架构要求

最小集群配置(生产推荐):

  • 至少 3 个主节点(负责数据分片)
  • 每个主节点至少 1 个从节点(负责高可用)
  • 总共 ≥ 6 个 Redis 实例

⚠️ 官方要求:不能少于 3 主(否则无法达成故障转移共识)

节点角色:

角色 职责
Master 负责一部分槽的读写
Replica 复制 Master 数据,Master 挂了可接替

🔧 三、如何搭建 Redis Cluster?

步骤 1:准备 6 个 Redis 实例(以端口为例)

bash 复制代码
# redis.conf 模板(每个实例不同 port)
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 15000
appendonly yes

⚠️ 关键配置:

  • cluster-enabled yes → 启用集群模式
  • cluster-config-file → 自动生成的节点信息文件(不要手动改)

步骤 2:启动所有实例

bash 复制代码
redis-server redis-7000.conf
redis-server redis-7001.conf
...
redis-server redis-7005.conf

步骤 3:创建集群(Redis 5.0+ 使用 redis-cli --cluster

bash 复制代码
redis-cli --cluster create \
  127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
  --cluster-replicas 1
  • --cluster-replicas 1 表示 每个主配 1 个从
  • 命令会自动分配槽,并提示确认

✅ 输出示例:

text 复制代码
>>> Assigning 16384 slots to 3 masters.
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:7003 to 127.0.0.1:7000
...

🌰 四、Python 客户端如何连接 Cluster?

不能用普通 Redis()!必须用 RedisCluster。

python 复制代码
pip install redis-py-cluster
# 或 Redis 4.0+ 官方推荐(但功能较新):
# pip install redis[hiredis]  # redis-py 4.0+ 内置 cluster 支持

方式 1:使用 redis-py-cluster(稳定)

python 复制代码
from rediscluster import RedisCluster

startup_nodes = [
    {"host": "127.0.0.1", "port": "7000"},
    {"host": "127.0.0.1", "port": "7001"},
    # ... 提供部分节点即可,客户端会自动发现全部
]

rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)

# 像单机一样使用!
rc.set("user:1001:name", "Alice")
print(rc.get("user:1001:name"))

方式 2:使用 redis-py 4.0+ 内置支持(推荐新项目)

python 复制代码
from redis.cluster import RedisCluster

rc = RedisCluster(
    startup_nodes=[{"host": "127.0.0.1", "port": "7000"}],
    decode_responses=True
)
rc.set("key", "value")

✅ 客户端会自动:

  • 计算 key 所属 slot
  • 缓存 slot → node 映射
  • 遇到 MOVED 重定向时自动跳转

🔍 五、集群内部如何工作?

场景:客户端写入 SET user:1001 Alice

  1. 客户端计算 slot = CRC16("user:1001") % 16384 = 12345
  2. 查本地缓存:slot 12345 → Node B (7001)
  3. 发送命令到 Node B
  4. 如果 Node B 不负责该 slot(如刚扩容),返回:
text 复制代码
MOVED 12345 127.0.0.1:7002
  1. 客户端更新缓存,并重试到新节点

故障转移(类似 Sentinel):

  • 主节点挂了 → 从节点自动提升为主
  • 其他节点更新集群拓扑
  • 客户端收到 MOVED 或 -CLUSTERDOWN,自动重连

六、重要限制与注意事项

限制 说明 解决方案
不支持多 key 跨槽操作 MGET key1 key2 若 key1/key2 在不同槽 → 报错 用 Hash Tag:user:{1001}:nameuser:{1001}:email 会落在同一槽
不支持事务(MULTI/EXEC)跨槽 事务内所有 key 必须在同一槽 同上,用 Hash Tag
不支持 DB 多数据库 只能用 DB 0 设计时避免多 DB
Lua 脚本必须只操作同一槽的 key 否则报错 脚本中所有 key 加相同 {tag}

💡 Hash Tag 规则:

{} 中的内容用于计算 slot,忽略其他字符。

例如:

  • foo{bar} 和 hello{bar}:world → 同一槽
  • foo{bar}1 和 foo{bar}2 → 同一槽

📖 Hash Tag ------ 让多个 key 落在同一个槽的魔法

🔑 核心规则:花括号 {} 内的内容决定槽

✅ Redis 只对 {} 中的第一个闭合部分计算哈希,忽略其他字符。

📌 规则详解:

Key 用于计算 slot 的部分 说明
foo{bar} bar 只取 {} 内的内容
foo{bar}baz bar 忽略 {} 前后
foo{bar}{qux} bar 只取第一个 {}
foo{}bar foo{}bar 没有有效内容 → 整个 key 参与计算
foo{bar foo{bar 无闭合 } → 整个 key 参与计算

但是在python中由于经常使用format,所以使用format时需要将{}中的内容用双重花括号包裹

python 复制代码
user_id = "1001"
# 想生成: user:{1001}:name

# 错误写法(会报 KeyError):
# key = "user:{user_id}:name".format(user_id=user_id)  # ❌ {user_id} 被 format 解析

# 正确写法:用双花括号转义
key = f"user:{{{user_id}}}:name"
print(key)  # user:{1001}:name ✅

# 或命名参数:
key = "user:{{{user_id}}}:name".format(user_id=user_id)

🛠️ 七、运维常用命令

bash 复制代码
# 连接到任意集群节点
redis-cli -c -p 7000

# 查看集群状态
127.0.0.1:7000> CLUSTER INFO

# 查看节点列表
127.0.0.1:7000> CLUSTER NODES

# 手动故障转移(主节点执行)
127.0.0.1:7000> CLUSTER FAILOVER

# 添加新主节点(需先 join,再 reshard)
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000

# 迁移槽(扩容)
redis-cli --cluster reshard 127.0.0.1:7000

✅ 八、生产环境最佳实践

建议 说明
至少 3 主 3 从 满足高可用 + 故障转移
开启 AOF 防止主从同步时全量 RDB 太大
监控 CLUSTER INFO 关注 cluster_state:ok
客户端启用重试 应对短暂 MOVED/ASK
避免大 Key 单个 key 太大会导致迁移卡住

Redis 性能调优 ------ 榨干每一分性能

🔷 一、配置调优(redis.conf)

✅ 1. 内存相关

conf 复制代码
# 设置最大内存(必须!防 OOM)
maxmemory 8gb

# 内存淘汰策略(根据业务选)
maxmemory-policy allkeys-lru    # 通用推荐
# 其他选项:
# - volatile-lru      → 只淘汰带过期时间的 key
# - allkeys-lfu       → 更适合"热点数据"场景(Redis 4.0+)
# - noeviction        → 写满就报错(慎用!)

# 调整 LFU 参数(如果用 allkeys-lfu)
lfu-log-factor 10     # 衰减速度(越大越不敏感)
lfu-decay-time 1      # 分钟级衰减

📌 经验:

  • Web 缓存 → allkeys-lru
  • 热点排行榜 → allkeys-lfu

✅ 2. 持久化优化

conf 复制代码
# RDB 快照(适合备份)
save 900 1      # 15分钟至少1次变更
save 300 10     # 5分钟至少10次变更
# 生产建议:关闭 save "" 或仅保留长周期快照

# AOF(高可靠)
appendonly yes
appendfsync everysec   # 平衡性能与安全(默认)
# appendfsync always   # 最安全但慢(每写同步)
# appendfsync no       # 依赖 OS,可能丢数据

# AOF 重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

⚠️ 关键取舍:

  • 性能优先 → 关 AOF,开 RDB
  • 数据安全优先 → 开 AOF + everysec

✅ 3. 连接与网络

conf 复制代码
# 客户端连接数(默认 10000,按需调整)
maxclients 20000

# TCP backlog(高并发必调)
tcp-backlog 511   # Linux /proc/sys/net/core/somaxconn 要 ≥ 此值

# 关闭 THP(透明大页)→ 防止 fork 延迟飙升
# 在系统层面执行:
# echo never > /sys/kernel/mm/transparent_hugepage/enabled

💡 THP 是 Redis 延迟毛刺的常见元凶!务必关闭。

🔷 二、命令级优化(避坑指南)

🚫 高危命令黑名单(生产禁用!)

命令 风险 替代方案
KEYS * 遍历所有 key,阻塞主线程 SCAN 渐进式迭代
FLUSHDB / FLUSHALL 清空数据 禁用或重命名(rename-command FLUSHDB ""
SMEMBERS big_set 返回巨量数据 SSCAN 分批
HGETALL big_hash 同上 HSCAN 或只取需要字段
python 复制代码
# 用 SCAN 安全遍历
cursor = '0'
while cursor != 0:
    cursor, keys = r.scan(cursor=cursor, match="user:*", count=100)
    for key in keys:
        # 处理 key

✅ 批量操作 > 单条循环

python 复制代码
# ❌ 慢:N 次网络往返
for i in range(1000):
    r.set(f"key:{i}", "value")

# ✅ 快:1 次 pipeline
with r.pipeline() as pipe:
    for i in range(1000):
        pipe.set(f"key:{i}", "value")
    pipe.execute()

✅ 用合适的数据结构

场景 推荐结构 原因
缓存对象 Hash 比多个 string 节省内存
去重集合 Set O(1) 查找
有序排行榜 Sorted Set 支持范围查询
消息队列 List (LPUSH/RPOP) 或 Stream Stream 更可靠

🔷 三、内存优化(防 OOM + 降成本)

🔍 1. 监控内存使用

bash 复制代码
# 查看内存统计
redis-cli info memory

# 关键指标:
# used_memory: 实际使用内存
# mem_fragmentation_ratio: 内存碎片率(理想 1.0~1.5)
# maxmemory: 是否设上限

🔧 2. 降低内存碎片

  • 重启 Redis(最直接)
  • 开启 activedefrag yes(Redis 4.0+ 自动碎片整理)
conf 复制代码
activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10

🔧 3. 优化 key 设计

  • 缩短 key 名:u:1001:n 代替 user:1001:name
  • 用数字 ID 代替字符串:1001 代替 "alice"
  • 避免大 Value:单 value > 1MB?考虑拆分或压缩

🔧 4. 使用 MEMORY USAGE 定位大 Key

bash 复制代码
# 查看单个 key 内存占用
redis-cli MEMORY USAGE user:1001:profile

# 找出 top 大 key(需 redis-cli 4.0+)
redis-cli --bigkeys

🔷 四、网络与客户端优化

✅ 1. 客户端连接池

python 复制代码
# redis-py 默认有连接池,但要合理配置
pool = redis.ConnectionPool(
    host='localhost',
    port=6379,
    max_connections=20,      # 根据并发调
    retry_on_timeout=True
)
r = redis.Redis(connection_pool=pool)

⚠️ 避免每次请求新建连接!

✅ 2. 启用 pipelining / batching

  • 如前所述,批量操作减少 RTT
  • 对于写密集场景,合并命令是性价比最高的优化

✅ 3. 监控慢查询 & 延迟

bash 复制代码
# 开启慢查询(第 8 讲)
slowlog-log-slower-than 5000
slowlog-max-len 1000

# 实时监控延迟(Redis 5.0+)
redis-cli --latency -h your_redis

✅ 4. 集群模式下避免 MOVED 重定向风暴

  • 客户端缓存 slot 映射
  • 使用支持集群的客户端(如 redis-py-cluster)
  • 用 Hash Tag 减少跨槽操作

🛠️ 五、性能诊断 checklist

当你发现 Redis 变慢,按顺序排查:

  1. CPU 是否打满? → top 查看 redis-server CPU%
  2. 内存是否 swap? → free -m 看 swap 使用
  3. 慢查询日志 → SLOWLOG GET 10
  4. 大 Key / 大 Value? → redis-cli --bigkeys
  5. 持久化阻塞? → INFO PERSISTENCE 看 aof_delayed_fsync
  6. 网络延迟? → redis-cli --latency
  7. 客户端连接数过多? → INFO CLIENTS 看 connected_clients

✅ 六、终极建议:压测 + 监控

压测工具:

bash 复制代码
# Redis 自带 benchmark
redis-benchmark -h 127.0.0.1 -p 6379 -c 100 -n 100000 -q

# 更真实的工具:memtier_benchmark
memtier_benchmark -s 127.0.0.1 -p 6379 --ratio=1:1 --pipeline=10
参数 含义 默认值 说明
-h 127.0.0.1 目标 Redis 主机 IP 127.0.0.1 可改为远程地址如 192.168.1.10
-p 6379 目标端口 6379 Sentinel 或 Cluster 节点需指定对应端口
-c 100 并发客户端数量(connections) 50 模拟 100 个客户端同时发请求
-n 100000 总共发送的请求数 100000 执行 10 万次操作后结束
-q quiet mode(安静模式) ❌ 关闭 只输出汇总结果,不打印每条命令详情

📊 默认测试哪些命令?

如果不指定命令,redis-benchmark 会测试一组典型命令:

  • PING_INLINE
  • SET
  • GET
  • INCR
  • LPUSH / LPOP
  • SADD / SPOP
  • ZADD 等

💡 它模拟的是 "简单、高频、单 key" 的操作,适合快速评估基础吞吐能力。

memtier_benchmark 由 Redis Labs(现 Redis Ltd)开发,专为贴近生产场景设计。

参数 含义 默认值 说明
-s 127.0.0.1 服务器地址(server) localhost 注意这里是 -s,不是 -h
-p 6379 端口 6379 同上
--ratio=1:1 读写比例(set:get) 1:10 1:1 = 50% SET + 50% GET
--pipeline=10 每个连接使用 pipeline 批量发送 10 条命令 1 显著提升吞吐(减少 RTT)
(隐含)-c 50 并发客户端数 50 可通过 -c 指定
(隐含)-t 4 线程数 4 可通过 -t 指定

例如:

  • --ratio=1:9 → 10% 写,90% 读(典型缓存场景)
  • --ratio=1:0 → 全写
  • --ratio=0:1 → 全读

✅ 为什么 memtier_benchmark 更真实?

能力 redis-benchmark memtier_benchmark
自定义读写比例
支持 pipeline -P ✅ 原生支持 --pipeline
使用随机 key(避免缓存命中偏差) ❌(固定 key) ✅(默认生成 key:1, key:2...)
支持数据大小控制 --data-size=1024
支持多线程 ❌(单线程) -t 8 开 8 线程
支持 Redis Cluster --cluster-mode

🛠️ 如何安装 memtier_benchmark?

bash 复制代码
sudo apt-get install build-essential autoconf automake libtool pkg-config \
  libssl-dev libevent-dev zlib1g-dev

git clone https://github.com/RedisLabs/memtier_benchmark.git
cd memtier_benchmark
autoreconf -ivf
./configure
make
sudo make install

监控指标(Prometheus + Grafana):

  • QPS / 延迟 P99
  • 内存使用率
  • 拒绝连接数(rejected_connections)
  • 主从复制延迟(slave_repl_offset 差值)

🎯 总结:性能调优四象限

维度 关键动作
配置 maxmemory、选淘汰策略、关 THP、调 tcp-backlog
命令 KEYS *、用 SCAN、批量操作、选对数据结构
内存 缩短 key、拆分大 Value、监控 --bigkeys、开 activedefrag
网络 连接池、Pipeline、集群客户端、监控延迟
相关推荐
_OP_CHEN3 小时前
【MySQL数据库基础】(一)保姆级 MySQL 环境配置教程!CentOS 7+Ubuntu 双系统全覆盖
linux·数据库·sql·mysql·ubuntu·centos·环境配置
Drifter_yh10 小时前
【黑马点评】Redisson 分布式锁核心原理剖析
java·数据库·redis·分布式·spring·缓存
鸽鸽程序猿10 小时前
【Redis】zset 类型介绍
数据库·redis·缓存
z玉无心10 小时前
Redis
数据库·redis·oracle
予枫的编程笔记10 小时前
【Redis核心原理篇2】Redis 单线程模型:为什么单线程还能这么快?
数据库·redis·缓存
希忘auto10 小时前
详解Redis之分布式锁
redis
fengxin_rou10 小时前
一文吃透 Redis 压缩列表、listpack 及哈希表扩容与并发查询
数据库·redis·散列表
一只鹿鹿鹿10 小时前
智慧水利一体化建设方案
大数据·运维·开发语言·数据库·物联网
_codemonster10 小时前
数据库字符集编码问题
android·数据库·oracle