Redis Pub/Sub 高并发卡顿主因是客户端连接池配置不当、消费逻辑阻塞或系统TCP参数不合理,而非Redis服务端性能不足;需独占连接、调大PoolSize、设net.core.somaxconn、用异步消费、禁用通配符订阅,并正视其无重试/不保序/不落盘缺陷。

Redis Pub/Sub 在高并发下卡顿,基本不是 Redis 服务端扛不住,而是客户端连接池、消费逻辑或系统资源没配对——调参前先确认是不是在优化错误的对象。
Pub/Sub 连接必须独占,PoolSize 要按订阅者数量预估
go-redis 或 lettuce 的连接池默认配置(如 PoolSize=10)完全不适用于 Pub/Sub。因为每个 SUBSCRIBE 操作会独占一个连接,直到 UNSUBSCRIBE 或连接关闭;它不会像普通命令那样用完即还。
- 50 个并发订阅 goroutine,
PoolSize至少设为 60~80,留出缓冲余量 -
MinIdleConns设为 0 是合理的,空闲连接对订阅无意义 -
PoolTimeout建议显式设为500ms,避免阻塞太久导致 goroutine 积压 - Java 中用 Lettuce 时,
StatefulRedisPubSubConnection必须绑定独立的ClientResources,否则多个订阅共享 event loop 会互相干扰
别乱调 net.core.rmem_max,真正该改的是 net.core.somaxconn
当服务启停频繁、或客户端未复用连接导致每秒新建数百个订阅连接时,内核 TCP 队列可能溢出,报错类似 accept() failed (24: Too many open files) 或日志里出现 SYN queue overflow。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
net.core.somaxconn改成65535(默认常为 128),防止握手队列丢包 -
fs.file-max≥200000,确保系统级文件句柄够用 -
net.ipv4.tcp_fin_timeout从 60 降到 30,仅在大量短连场景下有效 -
net.core.rmem_max和wmem_max对 Pub/Sub 几乎无改善——buffer 堆积说明消费者已堵死,调大只是掩盖问题
消费延迟高?必须把 listen() 从主线程摘出来
Python 的 redis-py 默认 pubsub.listen() 是阻塞式同步调用,一旦处理慢,消息就堆在 socket buffer 里,后续连接也卡住。
- 用
redis-py+asyncio封装pubsub.get_message(block=False),轮询+sleep 控制节奏 - Java 推荐用 Lettuce 的异步 API:
connection.addListener()注册回调,不要用BlockingCommand - 所有消费逻辑必须加超时:比如单条消息处理超过
200ms就丢弃或发告警,避免雪崩 - 批处理可选但非必需:若业务允许 100ms 级延迟,用
time.AfterFunc或scheduledExecutorService聚合多条再处理
频道设计不当会让优化全白费
用 psubscribe order.* 听一堆子频道,比 subscribe order:paid + order:shipped 多消耗 3~5 倍 CPU,且无法水平分片。
- 禁止通配符泛订阅,尤其不能在高频频道上用
*或? - 频道名带业务前缀,如
inventory:update:sku123,方便按前缀做集群分片 - 同一业务模块的多个频道,尽量合并发布:用 JSON 字段区分事件类型,而不是开多个频道
- 上线前用
redis-cli --stat观察pubsub_channels和pubsub_patterns数量,超过 500 就要警惕
最常被忽略的一点:Pub/Sub 没有重试、不保序、不落盘。哪怕参数全调对了,只要下游处理崩溃一次,消息就永远丢失——真要可靠性,得切到 Stream,别硬撑。

















