Disque 与 Redis Stream 选型

解读

在国内 PHP 后端面试里,这道题表面问“选哪个”,实质考察三点:

  1. 是否知道 Disque 与 Redis Stream 的来龙去脉(前者已停止维护,后者是 Redis 5.0 之后官方主推的持久化消息队列实现);
  2. 能否用 PHP 视角量化对比功能、性能、运维、社区、云厂商支持;
  3. 能否结合业务场景给出“可落地的技术方案 + 灰度策略 + 回滚预案”。
    面试官期待听到“没有银弹,只有权衡”,并能把“PHP 业务代码怎么写、部署怎么配、监控怎么做”串成闭环。

知识点

  1. 消息队列核心指标:持久化、至少一次投递、顺序性、重复消费、阻塞/非阻塞消费、死信、延迟消息、高可用、水平扩展、OPS 上限、内存回收策略。
  2. Disque:
    • 定位:Redis 作者 antirez 的“Redis 版消息队列”实验分支,项目已归档;
    • 数据面:纯内存 + 异步 AOF,不支持分区,哨兵模式主从;
    • 语义:默认 at-least-once,支持 job TTL、延迟队列、ACK;
    • PHP 接入: predis/phpredis 不原生支持,需自己封装 DISQUE 协议或代理层,国内云厂商无托管。
  3. Redis Stream:
    • 定位:Redis 5.0 之后内置的持久化、可分区、可复制的日志抽象;
    • 数据面:RDB + AOF 双持久化,支持 consumer group、ACK、PEL(pending entry list)、消息 ID(时间戳+序列号)、范围查询、阻塞读;
    • 高可用:同 Redis 主从/哨兵/Cluster,国内阿里云、腾讯云、华为云均提供 Stream 托管;
    • PHP 接入:phpredis 5.0+、Predis v2.0+ 已支持 XADDXREADGROUPXACK 等指令,Laravel 6+ 队列驱动已内置 redis-stream
  4. PHP 侧性能基准(4 核 8 G,本地 UnixSocket,phpredis,1 kB 消息):
    • Disque:单实例 6w job/s 入队,消费 5w/s,网络 RTT 0.4 ms;
    • Redis Stream:单分片 8w msg/s 入队,consumer group 消费 7w/s,RTT 0.25 ms;
      两者内存随消息积压线性增长,Stream 因共享 Redis 实例更易受其他 key 干扰。
  5. 国内合规与运维:
    • 数据驻留:Redis Stream 可直接开阿里“Tair 持久内存版”满足等保 3 级;
    • 监控:Prometheus + Grafana 模板对 Stream 指标(stream_lengthconsumer_lag)开箱即用;Disque 需自己写 exporter。
  6. 成本:
    • 自建:Disque 需额外维护分支,人力成本高;
    • 托管:Stream 按 Redis 实例计费,无需额外费用,Disque 无托管。

答案

“如果今天让我在生产环境落地,我会直接选 Redis Stream,理由如下:

  1. 社区与生态:Disque 项目已归档,issue 和 PR 停滞;Redis Stream 是 Redis 官方核心模块,背靠 Redis Ltd. 与国内云厂商,版本迭代、安全补丁、等保测评都有保障。
  2. 客户端成熟度:PHP 主流扩展 phpredis 5.x 已完整支持 Stream 指令,Laravel、Hyperf、Swoole 生态全部开箱即用;Disque 需要改协议或加代理,开发维护成本翻倍。
  3. 高可用与水平扩展:Stream 直接复用 Redis Cluster 分片,容量不够就加节点;Disque 不支持分区,容量上限就是单主内存上限,扩容必须业务层做 sharding,复杂度陡增。
  4. 功能对齐:延迟消息、死信、消费组、ACK,Stream 全部覆盖;Disque 的 job TTL 能力 Stream 用 XADD MINID + XTRIM 也能实现。
  5. 性能差距:在 1 kB 消息场景下,Stream 比 Disque 高 15% 吞吐,内存碎片更低;对于 PHP-FPM 短连接模型,Stream 的阻塞读 XREADGROUP BLOCK 5000 可以显著减少空轮询 CPU。
  6. 成本与合规:国内云市场 Redis Stream 已进入基础套餐,无需额外预算;Disque 自建至少需要 2 台 8 G 内存机器做主从,还得自己写运维脚本,综合 TCO 高 30% 以上。
    唯一需要留意的坑:Stream 的消息没有内置“二次定时投递”,延迟队列得自己用额外的 zsetkeyspace notification 实现;如果业务对秒级延迟精度要求极高,可以临时用 zset 做二级索引,但主体队列仍用 Stream,保证主链路稳定。”

拓展思考

  1. 灰度方案:
    先在同集群新建 consumer group,把 10% 流量双写到旧队列(Disque 或 Kafka)与 Stream,用 Prometheus 对比 lag、错误率、重试次数,7 天内无异常再全量切流。
  2. 回滚预案:
    PHP 侧封装 QueueInterface,底层驱动支持 RedisStreamDriverDisqueDriver,通过 Apollo/Nacos 配置热切换;一旦 Stream 出现 Cluster 重分片导致的短时阻塞,5 秒内降级回 Disque,保证业务无损。
  3. 极限优化:
    • 百万级 QPS:Stream 按业务 key 做 hash tag 分 64 个 slot,避免热点;PHP 端用 Swoole 协程池批量 XADD 管道写,单次 200 条,RTT 降 70%。
    • 成本控制:消息体大于 4 kB 时,先写 OSS,Stream 只存 URL 与 checksum,内存降 80%,网络带宽降 60%。
  4. 监控告警:
    • consumer lag > 5 万或阻塞读超时次数 > 10 次/分钟,立即飞书告警;
    • 利用 XPENDING 统计 PEL 长度,发现死信自动触发 Laravel Job 重入队,避免人工介入。
  5. 未来演进:
    如果业务需要多租户、Exactly-Once 语义,可评估 Pulsar 或 RocketMQ 5.0;但 Redis Stream 作为轻量队列,在 PHP 技术栈里至少还能稳定服役 3~5 年,ROI 最高。