Redis持久化、内存管理、慢查询与发布订阅

Redis持久化、内存管理、慢查询与发布订阅 -- pd的后端笔记

文章目录

    • [Redis持久化、内存管理、慢查询与发布订阅 -- pd的后端笔记](#Redis持久化、内存管理、慢查询与发布订阅 -- pd的后端笔记)
  • [持久化机制(RDB vs AOF)------ 如何防止 Redis 数据丢失?](#持久化机制(RDB vs AOF)—— 如何防止 Redis 数据丢失?)
  • [内存管理与淘汰策略(Eviction Policy) ------ Redis 满了怎么办?](#内存管理与淘汰策略(Eviction Policy) —— Redis 满了怎么办?)
    • [当内存达到上限:淘汰策略(Eviction Policy)](#当内存达到上限:淘汰策略(Eviction Policy))
      • [🗑️ 类型 1:针对"设置了过期时间"的 key(volatile)](#🗑️ 类型 1:针对“设置了过期时间”的 key(volatile))
      • [🗑️ 类型 2:针对"所有 key"(allkeys)](#🗑️ 类型 2:针对“所有 key”(allkeys))
      • [🚫 类型 3:不淘汰(危险!)](#🚫 类型 3:不淘汰(危险!))
    • [🛠️ 生产环境推荐配置](#🛠️ 生产环境推荐配置)
  • [Redis 事务(MULTI/EXEC)------ 原子执行一组命令](#Redis 事务(MULTI/EXEC)—— 原子执行一组命令)
    • [🌰 实战:安全转账(模拟)](#🌰 实战:安全转账(模拟))
    • [🔍 WATCH:实现乐观锁(Optimistic Locking)](#🔍 WATCH:实现乐观锁(Optimistic Locking))
    • [🆚 事务 vs Lua 脚本 vs Pipeline](#🆚 事务 vs Lua 脚本 vs Pipeline)
  • [📖 Redis 发布/订阅 ------ 轻量级消息广播系统](#📖 Redis 发布/订阅 —— 轻量级消息广播系统)
      • [🌰 场景 1:实时通知(如订单状态更新)](#🌰 场景 1:实时通知(如订单状态更新))
      • [🌰 场景 2:多服务解耦(WebSocket 集群广播)](#🌰 场景 2:多服务解耦(WebSocket 集群广播))
    • [🎯 总结](#🎯 总结)
  • [📖 Redis 慢查询 ------ 找出拖慢系统的"罪魁祸首"](#📖 Redis 慢查询 —— 找出拖慢系统的“罪魁祸首”)
    • [🌰 常见"慢命令"黑名单(务必警惕!)](#🌰 常见“慢命令”黑名单(务必警惕!))

持久化机制(RDB vs AOF)------ 如何防止 Redis 数据丢失?

Redis 是内存数据库,默认情况下:

  • 所有数据都在 RAM 中
  • 如果 Redis 进程崩溃或服务器断电 → 所有数据丢失!

持久化(Persistence) 就是把内存中的数据保存到磁盘,以便重启后恢复。

Redis 提供两种主流持久化方式:

  • RDB(Redis Database):快照(Snapshot)
  • AOF(Append-Only File):日志(Log)

也可以同时开启两者(推荐用于关键业务)。

RDB:定时快照(全量备份)

✅ 原理

  • 在指定时间点,将整个内存数据集写入一个二进制文件(默认 dump.rdb)
  • 类似"拍照":某一刻的完整状态

⚙️ 触发方式

方式 说明
自动触发 通过 save 配置(如 save 900 1 表示 900 秒内至少 1 次修改就保存)
手动触发 SAVE(阻塞主线程)或 BGSAVE(后台 fork 子进程,推荐)
主从同步 从节点全量同步时,主节点会自动生成 RDB

📄 默认配置(redis.conf)

conf 复制代码
save 900 1      # 15分钟内至少1次修改
save 300 10     # 5分钟内至少10次修改
save 60 10000   # 1分钟内至少10000次修改

✅ 优点

  • 文件紧凑:单个二进制文件,适合备份、灾难恢复
  • 恢复速度快:直接加载 RDB 文件到内存
  • 性能影响小:BGSAVE 在子进程中执行,主进程继续处理请求

❌ 缺点

  • 可能丢数据:如果 Redis 在两次快照之间崩溃,最近一次快照之后的数据全部丢失
  • 例如:配置 save 60 10000,但只写了 5000 次就宕机 → 全丢!
  • 大数据集 fork 耗时:如果内存很大(几十 GB),fork() 子进程可能阻塞主线程几百毫秒

AOF:操作日志(增量备份)

✅ 原理

  • 记录每一个写命令到日志文件(如 appendonly.aof)
  • 重启时,重放(replay)所有命令来重建数据集
  • 类似"录像":记录每一步操作

⚙️ 同步策略(fsync)

配置 说明 安全性 性能
appendfsync always 每个写命令都同步到磁盘 ✅ 最安全(最多丢 1 条) ❌ 最慢(受磁盘 I/O 限制)
appendfsync everysec 每秒同步一次(默认) ⚠️ 最多丢 1 秒数据 ✅ 平衡
appendfsync no 由操作系统决定何时刷盘 ❌ 可能丢几秒~几分钟 ✅ 最快

🛠️ AOF 重写(Rewrite)

  • 问题:AOF 文件会不断增长(即使 SET key 1 执行 100 次,只需最后 1 次)
  • 解决:BGREWRITEAOF 命令生成精简版 AOF(只保留最终状态)
  • 自动触发:可通过 auto-aof-rewrite-percentage 配置

✅ 优点

  • 数据更安全:配合 everysec,最多丢 1 秒数据
  • 可读性强:AOF 是文本格式,可用 redis-check-aof 修复

❌ 缺点

  • 文件大:比 RDB 大很多(尤其高频写入)
  • 恢复慢:需重放所有命令(数据量大时启动慢)
  • I/O 压力:频繁 fsync 可能成为瓶颈

🔍 RDB vs AOF 对比总结

特性 RDB AOF
数据完整性 可能丢多次快照间的数据 最多丢 1 秒(everysec
文件大小 小(二进制压缩) 大(命令日志)
恢复速度 慢(需重放)
备份便利性 ✅ 单文件,易拷贝 ❌ 文件大,需重写
对性能影响 低(BGSAVE 中(everysec 可接受)
适用场景 灾备、冷数据、分析 实时性要求高的业务

配置设置

🛠️ 生产环境推荐配置

✅ 场景 1:既要速度又要安全(推荐大多数业务)

conf 复制代码
# 同时开启 RDB + AOF
save 900 1
save 300 10
save 60 10000

appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

# AOF 重写
; 当 AOF 文件比上次重写后的大小增长了 100%(即翻倍),并且当前 AOF 文件至少有 64MB 时,自动触发 AOF 重写。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

💡 重启时,Redis 优先使用 AOF 恢复(因为更完整)!

✅ 场景 2:纯缓存,允许丢数据: 适合临时 session、排行榜等可重建数据。

conf 复制代码
save ""        # 禁用 RDB
appendonly no  # 禁用 AOF

✅ 场景 3:极致性能,容忍少量丢失

conf 复制代码
save 60 10000
appendonly yes
appendfsync everysec

🔧 Python 中如何验证持久化是否生效?

python 复制代码
r = redis.Redis()
print(r.config_get("save"))        # 查看 RDB 规则
print(r.config_get("appendonly"))  # 查看 AOF 是否开启

# 手动触发保存:
r.bgsave()          # 触发 RDB
r.bgrewriteaof()    # 触发 AOF 重写

模拟重启:

  • 停止 Redis:redis-cli shutdown
  • 启动 Redis:redis-server

⚠️ 注意:SHUTDOWN 命令会强制保存(即使没到 RDB 条件),所以测试时要用 kill -9 模拟崩溃。

🎯 总结

  • RDB = 快照,适合备份、恢复快
  • AOF = 日志,适合数据安全、可读性强
  • 生产环境建议两者都开,AOF 为主,RDB 为辅
  • 没有 100% 不丢数据的方案,但合理配置可将风险降到极低

内存管理与淘汰策略(Eviction Policy) ------ Redis 满了怎么办?

🧠 为什么需要内存限制?

Redis 默认不限制内存使用------它会一直吃内存,直到:

  • 系统内存耗尽 → 触发 Linux OOM Killer → Redis 进程被强制杀死!
  • 或者系统开始大量 swap(交换到磁盘)→ 性能暴跌 100 倍!

所以,生产环境必须设置 maxmemory!

一、设置最大内存:maxmemory

在 redis.conf 中配置:

conf 复制代码
maxmemory 2gb

或者运行时动态设置:

shell 复制代码
CONFIG SET maxmemory 2147483648  # 2GB in bytes
# 1gb = 1,073,741,824 字节

当内存达到上限:淘汰策略(Eviction Policy)

光设 maxmemory 不够!你还得告诉 Redis:

  • "内存满了,该删哪些 key?"

这就是 maxmemory-policy 的作用。

Redis 提供 8 种策略,分为三类:

🗑️ 类型 1:针对"设置了过期时间"的 key(volatile)

策略 行为 适用场景
volatile-lru 从有 TTL 的 key中,淘汰最近最少使用的 ✅ 缓存场景(如 session、临时 token)
volatile-lfu 从有 TTL 的 key中,淘汰最不经常使用的 高频访问 + 低频垃圾共存
volatile-random 从有 TTL 的 key中,随机淘汰 简单场景,不关心访问模式
volatile-ttl 从有 TTL 的 key中,淘汰剩余时间最短的 "快过期的先删"

🗑️ 类型 2:针对"所有 key"(allkeys)

策略 行为 适用场景
allkeys-lru 从所有 key中,淘汰最近最少使用的 ✅ 最常用!通用缓存(如页面缓存)
allkeys-lfu 从所有 key中,淘汰最不经常使用的 访问模式稳定(如热门商品缓存)
allkeys-random 从所有 key中,随机淘汰 测试或完全无状态缓存

✅ 优势:不管有没有 TTL,都能淘汰,不会写满报错。

🚫 类型 3:不淘汰(危险!)

策略 行为 风险
noeviction 拒绝所有写入命令(返回 error),只读操作允许 ❌ 默认策略!内存满后服务不可写

⚠️ Redis 默认策略是 noeviction!

这意味着:如果你不配置,内存一满,所有 SET/INCR/HSET 等写操作都会失败!

🔍 LRU vs LFU:到底选哪个?

算法 全称 特点 适合场景
LRU Least Recently Used "最近没用的删" 访问模式随时间变化(如新闻热点)
LFU Least Frequently Used "总用得少的删" 访问模式稳定(如首页 banner)

🛠️ 生产环境推荐配置

✅ 场景 1:纯缓存(如 API 响应缓存)

conf 复制代码
maxmemory 4gb
maxmemory-policy allkeys-lru

✅ 场景 2:混合存储(部分 key 永久,部分带 TTL)

conf 复制代码
maxmemory 4gb
maxmemory-policy volatile-lru

✅ 场景 3:严格禁止丢数据(如计数器、排行榜)

复制代码
maxmemory 16gb
maxmemory-policy noeviction

🔧 Python 中如何查看和设置?

python 复制代码
import redis

r = redis.Redis()

# 查看当前内存和策略
info = r.info("memory")
print("Used memory:", info["used_memory_human"])
print("Max memory:", info["maxmemory_human"])
print("Policy:", info["maxmemory_policy"])

# 动态设置(谨慎!)
r.config_set("maxmemory", "2147483648")  # 2GB
r.config_set("maxmemory-policy", "allkeys-lru")

⚠️ 注意:动态设置 maxmemory 后,如果当前内存已超限,Redis 会立即触发一次淘汰!

误区 正确做法
"不设 maxmemory,让系统管" ❌ 必须设!否则 OOM 崩溃
"用了 AOF 就不怕丢数据,随便淘汰" ❌ 淘汰 = 永久删除,AOF 也救不回
"LFU 一定比 LRU 好" ❌ 热点突变场景 LRU 更优(如突发新闻)
"设置了 policy 就万事大吉" ❌ 必须配合内存监控(如 Prometheus + Grafana)

✅ 最佳实践清单:

  1. 永远设置 maxmemory(建议物理内存的 50%~80%)
  2. 根据业务选策略:缓存用 allkeys-lru,临时数据用 volatile-lru
  3. 监控内存使用率:超过 80% 就告警
  4. 避免大 Key:单个 key 几百 MB 会导致淘汰时卡顿
  5. 定期分析热点:用 redis-cli --hotkeys(需开启 maxmemory-samples)

Redis 事务(MULTI/EXEC)------ 原子执行一组命令

🧠 Redis 事务是什么?

Redis 的事务通过 MULTI / EXEC 实现:

  • 将多个命令打包,然后一次性、按顺序、不被其他客户端打断地执行
  • 但它 ≠ ACID 事务!没有回滚(Rollback)机制

🔑 核心特点:

  • ✅ 原子性(Atomicity):事务中的命令要么全部执行,要么一个都不执行(在 EXEC 阶段)
  • ❌ 不支持回滚:如果某个命令出错,之前的命令不会撤销(这是设计选择,非 bug!)
  • ✅ 隔离性(Isolation):事务执行期间,其他客户端看不到中间状态
  • ❌ 不保证持久性:除非配合 AOF + appendfsync always

类似于mysql的 start transaction; 写入、删除、更新逻辑 commit;

python 复制代码
# 方式 1:使用 pipeline(推荐!)
pipe = r.pipeline()
pipe.incr("user:1001:posts")
pipe.incr("stats:total_posts")
results = pipe.execute()  # 触发 MULTI/EXEC
print(results)  # [1, 1]

# 方式 2:显式事务(较少用)
with r.pipeline() as pipe:
    pipe.multi()      # 显式开启事务(其实默认就是)
    pipe.incr("a")
    pipe.incr("b")
    res = pipe.execute()

⚠️ 关键限制:Redis 事务不支持回滚!

场景:命令语法错误 vs 运行时错误

❌ 情况 1:语法错误(在 EXEC 前检测到)

bash 复制代码
MULTI
INCR user:1001:posts
INCRBY stats:total_posts abc  # ← 错误!abc 不是数字
EXEC

→ 整个事务被拒绝,两个命令都不执行。✅ 安全!

⚠️ 情况 2:运行时错误(EXEC 后才暴露)

bash 复制代码
MULTI
INCR user:1001:posts        # 正常
HSET user:1001 name Alice   # 但 user:1001 是 String,不是 Hash!
EXEC

→ 结果:

  • NCR 成功执行(值 +1)
  • SET 失败(返回 error)
  • 但 INCR 不会回滚!

📌 Redis 的哲学:

"错误通常是程序 bug,应该在开发阶段发现。生产环境不应依赖回滚。"

------ 所以它选择简单高效,而非复杂回滚。

💡 最佳实践:

  • 简单原子操作 → 用 事务(pipeline)
  • 复杂逻辑(带条件判断、回滚需求)→ 用 Lua 脚本

🌰 实战:安全转账(模拟)

假设我们要从用户 A 转 100 元给用户 B:

python 复制代码
def transfer_money(pipe, from_user, to_user, amount):
    # 注意:这里不能直接用 r.get(),必须在 pipe 中读!
    balance_a = int(pipe.get(f"balance:{from_user}") or 0)
    if balance_a < amount:
        raise ValueError("余额不足")
    pipe.multi()  # 确保后续命令在事务中
    pipe.decr(f"balance:{from_user}", amount)
    pipe.incr(f"balance:{to_user}", amount)

# 使用 watch 监控 key(防并发修改)
def safe_transfer(from_user, to_user, amount):
    with r.pipeline() as pipe:
        while True:
            try:
                # 监听双方余额,防止中间被修改
                pipe.watch(f"balance:{from_user}", f"balance:{to_user}")
                transfer_money(pipe, from_user, to_user, amount)
                break  # 成功则退出
            except redis.WatchError:
                continue  # 重试

pipe.execute() 是在 safe_transfer() 函数的上下文(即 with r.pipeline() as pipe: 块结束时)自动调用的!

🔍 WATCH:实现乐观锁(Optimistic Locking)

  • WATCH key:监控 key,如果在 EXEC 前被其他客户端修改,则事务失败(返回 None)
  • 适用于:先读 → 判断 → 写 的场景(如上面的转账)
python 复制代码
pipe = r.pipeline()
pipe.watch("stock:iphone")
current = int(pipe.get("stock:iphone"))
if current > 0:
    pipe.multi()
    pipe.decr("stock:iphone")
    result = pipe.execute()
    if result is None:
        print("库存被别人抢了!")
    else:
        print("购买成功!")
else:
    print("库存不足")

✅ 这比"先查后改"更安全,但仍不如 Lua 脚本原子(因为 read 和 multi 之间有 gap)。

🆚 事务 vs Lua 脚本 vs Pipeline

特性 事务(MULTI/EXEC) Lua 脚本 Pipeline(非事务)
原子性 ✅(命令序列) ✅(整个脚本)
支持条件逻辑 ✅(if/for)
支持回滚 ❌(但可自己写逻辑)
性能 极高(服务端执行) 最高(仅批处理)
适用场景 简单原子操作 复杂业务逻辑 批量写入/读取

💡 黄金法则:

  • 能用 单个命令 解决?→ 优先用(如 HINCRBY)
  • 需要 条件判断 + 原子性?→ 用 Lua 脚本
  • 只是 批量操作?→ 用 Pipeline

📖 Redis 发布/订阅 ------ 轻量级消息广播系统

Redis 的 发布/订阅(Pub/Sub) 是一个轻量级、高性能的消息通信机制,非常适合用于实时通知、事件广播、解耦系统模块等场景。

虽然它不像 RabbitMQ 或 Kafka 那样功能全面(比如不支持持久化、ACK、重试),但在"简单、快、够用"的场景下,它是极佳的选择!

🧠 核心概念:发布者 vs 订阅者

  • 发布者(Publisher):往某个 频道(channel) 发送消息
  • 订阅者(Subscriber):订阅一个或多个频道,接收消息
  • 消息只发给"当前在线"的订阅者 → 不持久化!离线就丢!

🔍 类比:

就像微信群------你发一条消息,所有群成员(订阅者)都能看到。

但如果某人退出了群(断开连接),他再进来就看不到历史消息了。

模式 命令 说明
频道订阅 SUBSCRIBE channel1 精确匹配频道名
模式订阅 PSUBSCRIBE news.* 通配符匹配(*?

🛠️ 核心命令速览(CLI vs Python)

功能 Redis CLI Python (redis-py)
订阅频道 SUBSCRIBE news r.pubsub().subscribe("news")
订阅模式 PSUBSCRIBE news.* r.pubsub().psubscribe("news.*")
发布消息 PUBLISH news "hello" r.publish("news", "hello")
查看订阅数 PUBSUB NUMSUB news r.pubsub_numsub("news")

🌰 场景 1:实时通知(如订单状态更新)

发布者(订单服务)

py 复制代码
# 订单支付成功
order_id = "order_123"
r.publish(f"order:{order_id}:status", "paid")

订阅者(通知服务)

py 复制代码
pubsub = r.pubsub()

# 正确做法:用模式订阅
pubsub.psubscribe("order:*:status")

for message in pubsub.listen():
    if message["type"] == "pmessage":  # 模式订阅的消息类型是 pmessage
        channel = message["channel"].decode()
        data = message["data"].decode()
        print(f"收到通知: {channel} → {data}")
        # 输出: 收到通知: order:order_123:status → paid

注意for message in pubsub.listen():是一个同步阻塞(synchronous blocking)的

它会阻塞当前线程,直到有新消息到达。pubsub.listen() 的本质

  • 它是一个 Python 生成器(generator)
  • 内部调用 socket.recv() 等待 Redis 服务器推送消息
  • 没有消息时,线程会卡在 recv()上 → 同步 I/O 阻塞

可以用 threading 把 listen() 放到后台线程:

python 复制代码
# 启动后台线程
thread = threading.Thread(target=listen_to_pubsub, daemon=True)
thread.start()

# 主线程继续做其他事
while True:
    print("主线程在工作...")
    time.sleep(2)

⚠️ 重要区别:

  • subscribe() → 消息类型是 "message"
  • psubscribe() → 消息类型是 "pmessage"

🌰 场景 2:多服务解耦(WebSocket 集群广播)

在 WebSocket 集群中,每个节点只维护部分连接。要广播消息给所有用户,可以用 Redis Pub/Sub 中转:

python 复制代码
# 任意节点收到消息
def on_websocket_message(user_id, msg):
    # 先处理本地逻辑
    process_locally(user_id, msg)
    # 再广播给其他节点
    r.publish("ws:broadcast", json.dumps({"from": user_id, "msg": msg}))

# 所有节点都订阅这个频道
pubsub = r.pubsub()
pubsub.subscribe("ws:broadcast")

for msg in pubsub.listen():
    if msg["type"] == "message":
        payload = json.loads(msg["data"])
        # 把消息推送给本机的所有 WebSocket 客户端
        broadcast_to_local_clients(payload)

✅ 这就是很多开源项目(如 Django Channels)的底层实现原理!

🔍 底层原理:字典 + 链表

Redis 服务器内部维护两个关键结构:

  1. pubsub_channels:字典
    • key = 频道名(如 "news")
    • value = 订阅该频道的客户端列表(链表)
  2. pubsub_patterns:列表
    • 存储所有模式订阅(如 "news.*")
    • 每个元素包含 pattern + 客户端

当执行 PUBLISH news "hello":

  1. 查 pubsub_channels["news"] → 获取所有精确订阅者
  2. 遍历 pubsub_patterns → 匹配 "news" 是否符合某个 pattern
  3. 把消息推送给所有匹配的客户端

📌 所以:模式订阅比频道订阅稍慢(需要遍历匹配)。

问题 说明
消息不持久化 订阅者离线期间的消息全部丢失
无 ACK 机制 无法确认消息是否被成功处理
无消息堆积 如果订阅者处理慢,Redis 会缓冲消息,但可能 OOM
不支持历史回溯 不能像 Kafka 那样"从头消费"
连接必须保持 一旦 TCP 断开,自动取消订阅

✅ 最佳实践

  • 命名规范:用冒号分隔,如 event:user:login、cache:invalidate:product
  • 避免长连接中断:在客户端实现自动重连 + 重新订阅
  • 监控订阅者数量:用 PUBSUB NUMSUB channel 防止"假广播"(没人订阅)
  • 不要传大消息:单条消息建议 < 1MB,避免阻塞 Redis

🎯 总结

特性 Pub/Sub
消息模型 广播(一对多)
持久化 ❌ 不支持
可靠性 ❌ 最多一次(at-most-once)
性能 ✅ 极高(纯内存,无磁盘 IO)
适用场景 实时通知、事件驱动、解耦

💡 记住:Pub/Sub 是"火警广播"------响一声就没了,听到算你幸运,没听到也不补。

📖 Redis 慢查询 ------ 找出拖慢系统的"罪魁祸首"

很多线上 Redis 性能问题,其实都是因为一条 KEYS * 或一个巨大的 SMEMBERS 引起的。而慢查询日志,就是你的"黑匣子记录仪"。

🧠 什么是慢查询?

Redis 会自动记录执行时间超过阈值的命令到一个内存中的 FIFO 队列(先进先出),这就是 慢查询日志(Slow Log)。

  • ✅ 只记录命令本身(不含网络传输时间)
  • ✅ 包含执行耗时、发生时间、客户端信息
  • ❌ 不记录读取的数据内容(保护隐私)

🔍 类比:

就像汽车的行车记录仪------只记"什么时候、发生了什么操作、花了多久",不记车内对话。

⚙️ 核心配置(redis.conf)

Redis 通过两个参数控制慢查询:

conf 复制代码
# 1. 超时阈值(单位:微秒,1 秒 = 1,000,000 微秒)
slowlog-log-slower-than 10000   # 默认 10ms

# 2. 日志队列最大长度(FIFO,超出则丢弃最旧的)
slowlog-max-len 128             # 默认保留最近 128 条

💡 建议生产环境配置:

conf 复制代码
slowlog-log-slower-than 5000    # 记录 >5ms 的命令(更敏感)
slowlog-max-len 1000            # 保留更多历史

🔧 如何查看慢查询日志?

python 复制代码
# 如果使用redis终端
# 127.0.0.1:6379> SLOWLOG GET 10

import redis

r = redis.Redis()

# 获取所有慢日志(最多返回 slowlog-max-len 条)
slowlogs = r.slowlog_get(10)

for log in slowlogs:
    print(f"ID: {log['id']}")
    print(f"Time: {log['time']}")
    print(f"Duration: {log['duration'] / 1000:.2f} ms")
    # log['command'] 是 bytes,需要 decode
    print(f"Command: {' '.join(log['command'].decode().split())}")
    print("---")

🌰 常见"慢命令"黑名单(务必警惕!)

命令 为什么慢? 替代方案
KEYS * 遍历所有 key,O(N) SCAN 渐进式迭代
SMEMBERS big_set 返回整个集合(可能百万级) SSCAN 分批取
HGETALL big_hash 返回整个 Hash HSCAN 或只取需要的字段
LRANGE list 0 -1 返回整个 List LRANGE list 0 99 分页
FLUSHDB / FLUSHALL 清空数据库(阻塞) 避免在生产使用
大 Value 的 GET/SET 网络 + 内存拷贝开销大 拆分 Value,或用压缩

Redis 计算耗时的方式:

  • ✅ 包含:命令解析、数据查找、修改、返回构建
  • ❌ 不包含:网络 I/O、排队等待时间

所以:

  • 如果客户端网络慢,不会被记入慢查询
  • 如果 Redis 正在 BGSAVE,命令排队很久,排队时间不算,只算实际执行时间

📌 这意味着:慢查询反映的是 Redis 自身的 CPU/内存瓶颈,不是网络问题。

✅ 推荐监控策略

  1. 设置合理阈值:slowlog-log-slower-than 5000(5ms)
  2. 定期巡检:每天跑脚本检查是否有新慢日志
  3. 告警集成:如果慢日志数量突增,触发企业微信/钉钉告警
  4. 压测时开启:性能测试期间密切观察慢日志
相关推荐
查无此人byebye2 小时前
实战DDPM扩散模型:MNIST手写数字生成+FID分数计算(完整可运行版)
人工智能·pytorch·python·深度学习·音视频
好家伙VCC2 小时前
# 光计算驱动的编程范式革新:用Python实现光子神经网络模拟器在传统电子计算架构逼近物理极限的今天,**光计算**正
java·开发语言·python·神经网络
Dxy12393102162 小时前
Python使用正则提取字符串中的数字
python
花果山总钻风2 小时前
SQLAlchemy各种排序示例
后端·python·中间件
tod1132 小时前
Redis C++ 客户端开发全流程指南
数据库·c++·redis·缓存
大黄说说2 小时前
Python 实战指南:一键批量旋转 PDF 页面方向
开发语言·python·pdf
没有bug.的程序员3 小时前
分布式缓存深潜:Redis Cluster 物理内核、数据分片算法博弈与高并发实战指南
redis·分布式·缓存·高并发·cluster·数据分片
shangyingying_13 小时前
图像质量评价(IQA)
人工智能·python·神经网络
OPEN-Source3 小时前
大模型 Agent 实战:多 Agent 太贵太慢?一套系统性的性能与成本优化方案
人工智能·python·agent·rag·deepseek