bigywater.tech
Redis · 50

Pub/Sub 与 Stream 对比

上一篇看到 Pub/Sub 会丢消息;Stream 则会保留。这篇不再重复「丢不丢」,而是把两者放在同一台实例上,量四个维度:离线补读、内存占用、发布方的代价、分发语义。选哪个,取决于这些数字回答的问题对你重不重要。

0 vs 100
消费者离线期间发布 100 条,上线后读到的条数:Pub/Sub vs Stream
17.7 ms
向 5000 个订阅者 PUBLISH 一条消息的往返耗时(0 个订阅者 27 µs)
114 B/条
Stream 保留一条 100 字节消息的内存;Pub/Sub 保留 0

离线之后:补不补得回来

实测 · 本机执行输出
Pub/Sub:3 个订阅者上线后 0.2 秒内,0 个收到了东西(离线期间的 100 条不会补发)
Stream:消费组上线后 XREADGROUP 一次读到 100 条(全部积压)

同样的 100 条,同时用 PUBLISH 和 XADD 各发一份。三个 Pub/Sub 订阅者上线后没有任何一个收到东西;Stream 的消费组一次 XREADGROUP 就拿回 100 条。

内存:Pub/Sub 不存,Stream 存到你删为止

实测 · 本机执行输出
PUBLISH 10 万条(无订阅者):used_memory 变化 +28592 B
XADD 10 万条:used_memory 变化 +11447584 B  (114.5 B/条)

发布 10 万条(每条 100 字节)无订阅者的 PUBLISH,used_memory 只变了 +28592 字节(测量噪声);XADD 同样数量增加了 11447584 字节。Stream 必须自己控制长度(MAXLEN / XTRIM,见第 47 篇)。

发布方的代价:PUBLISH 是 O(订阅者数)

同步往返,单连接,每种 N 测 1000 次取中位
实测 · 本机执行输出
    订阅者数 N    PUBLISH 中位      XADD 中位(对照)
         0       27.1 µs          29.1 µs   (PUBSUB NUMSUB = 0)
        10       58.1 µs          29.3 µs   (PUBSUB NUMSUB = 10)
       100      375.3 µs          30.0 µs   (PUBSUB NUMSUB = 100)
      1000     3539.0 µs          29.1 µs   (PUBSUB NUMSUB = 1000)
      5000    17668.7 µs          29.3 µs   (PUBSUB NUMSUB = 5000)

订阅者放在另一个进程里持续排空,避免测量进程被拖慢。0 个订阅者时 PUBLISH 和 XADD 一样快(27 µs vs 29 µs);之后 PUBLISH 随订阅者数线性增加,约每个订阅者 3.5 µs,5000 个订阅者时一条 PUBLISH 要 17.7 ms;XADD 恒定在 29 µs 左右。PUBLISH 是在主线程里同步地向所有订阅者的输出缓冲区写数据——这点由耗时的线性增长间接印证,我没有做更细的剖析。

语义:广播 vs 分担

实测 · 本机执行输出
Pub/Sub 3 个订阅者: ['m1', 'm1', 'm1']
Stream 同组 3 个消费者: ['m1', None, None]

Pub/Sub 的一条消息被广播给所有订阅者;Stream 在同一个消费组里只交给一个消费者,不同的组才各得一份(第 41 篇)。所以「多个服务都要收到」用多个消费组,「多个实例分担」用同一个组。

分步演示:四个维度

Pub/Sub 与 Stream
本步的数据

    本文用到的工具与边界

    • 本机 macOS 27 / Redis 8.10.2 独立实例(本机回环端口,save 关闭),redis-cli 风格输出由自写的 RESP 客户端打印;计时用 time.perf_counter,数字每次运行会有小幅抖动。订阅者数很大的实验用单独进程排空订阅连接,PUBLISH 用同步往返计时
    • 没有验证:Pub/Sub 与 Stream 在集群下的差别(包括分片 Pub/Sub)、大消息时的吞吐对比、订阅者存在慢消费时对 PUBLISH 耗时的影响
    京ICP备2026067502号-1京公网安备11010502063487号