redis pub/sub 不能扛住高并发投票且不可作为主存储,因其纯内存、无持久化、无ack、不保证消息仅消费一次;需配合incr/hincrby等持久化命令更新票数,并由单一服务监听通知后广播,确保原子性与一致性。

Redis Pub/Sub 能不能扛住高并发投票?
不能直接当主存储用。Pub/Sub 是纯内存消息通道,断电就丢、不持久、无 ACK 机制,SUBSCRIBE 客户端掉线后收不到离线消息。它只适合做「通知广播」——比如某用户刚投完票,立刻推一条 "vote_updated:123" 到频道,让所有在线的管理后台或大屏页面实时刷新。真正的票数必须写进 INCR 或 HINCRBY 这类持久化命令里。
怎么避免多个服务实例重复处理同一条投票通知?
Pub/Sub 本身不保证消息只被消费一次,多个订阅者会各自收到同一份消息。如果每个服务都去查数据库+渲染页面,会造成冗余计算甚至并发更新冲突。解决办法是:只让一个角色负责「响应通知」,比如固定由网关层或某个带权重的 Node.js 实例监听 vote_channel,其他服务退订该频道,改用 HTTP 长轮询或 WebSocket 接收网关推送的轻量事件。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 不要在每个微服务里都
SUBSCRIBE vote_channel - 用
CLIENT SETNAME给关键消费者命名,方便运维识别 - 监听端加超时重连逻辑,
redisClient.on('error', () => reconnect())
为什么用 PUBLISH 后前端收不到消息?
常见原因有三个:客户端没真正进入订阅状态、频道名大小写/空格不一致、Redis 配置禁用了 Pub/Sub。先确认是否执行了 SUBSCRIBE vote_channel(不是 PSUBSCRIBE),再检查 Redis 的 notify-keyspace-events 配置——这个只影响键事件,不影响普通 Pub/Sub,但很多人误以为有关。真正要查的是:redis-cli ping 是否通,redis-cli pubsub numsub vote_channel 返回值是否 ≥1。
- 浏览器不能直连 Redis,必须通过后端中转(如 Express +
redis.createClient()) - Node.js 用
ioredis时,subscribe()是异步操作,得等ready事件后再PUBLISH - 测试时别用两个
redis-cli窗口:一个SUBSCRIBE,另一个PUBLISH,中间不能 Ctrl+C 断开订阅端
投票计数和实时通知怎么协同更新?
原子性是核心。先用 INCRBY vote:option_a 1 更新计数,成功后再 PUBLISH vote_channel '{"option":"a","total":127}'。千万别反过来——否则可能计数已写入,但通知失败,导致前端显示滞后。如果业务要求强一致,考虑用 Lua 脚本封装这两步,但要注意脚本执行时间不能超过 lua-time-limit(默认 5 秒),否则会被中断且不回滚。
EVAL "return {redis.call('INCRBY',KEYS[1],ARGV[1]), redis.call('PUBLISH',KEYS[2],ARGV[2])}" 2 vote:option_a vote_channel "{'option':'a'}"- 生产环境建议拆成两步,加日志和补偿任务:记录 PUBLISH 失败的 vote_id,定时扫描重发
- 前端收到通知后,应主动调用
GET vote:option_a再校验一次,防消息乱序










