投票与限量抽签

返回 高并发

综艺投票、年报评选、限量名额抽签(演唱会候补、内测资格)介于「点赞可近似」与「秒杀强一致」之间:通常要求 一人一票 / 一人一次,名额 不能超发,但读压力往往小于写。


典型问题

问题后果
重复点击投票票数虚高、规则纠纷
最后一百名额并发超发资格
实时榜刷新热 key、DB 压力
刷票脚本业务公平性崩塌

一致性要求

类型要求
普通投票(可隔天再投)周期内唯一:user + activity + day
单次投票全局唯一:user + activity
限量抽签强一致:剩余名额 ≥ 1 才能成功

推荐架构

普通投票(最终一致可接受)

投票 API
  ▼
Redis SADD vote:{activity}:{option} {userId}  → 0 表示已投过
  ▼
INCR vote:count:{activity}:{option}
  ▼
MQ → 异步写 DB(审计、防作弊分析)
  ▼
读榜:Redis ZSET 或 HASH,定时与 DB 对账

点赞与播放计数 类似,但 SADD 返回值 必须作为业务成功依据,不能只看 INCR。

限量抽签(强一致)

抽签 API
  ▼
频控 / 风控
  ▼
Redis Lua:
  if GET remain > 0 then DECR remain; SADD winners userId; return OK
  else return FAIL
  ▼
异步写 DB:winners 表 UNIQUE(user_id, activity_id)
  ▼
失败则 DB 拒绝,回滚 Redis(或以对账为准)

DB 唯一约束是最后防线,防止 Redis 与 DB 短暂不一致导致超发。

UNIQUE KEY uk_user_activity (user_id, activity_id)
-- INSERT 失败 → 已中签或重复,向用户返回明确文案

防刷与公平

手段说明
登录态 + 实名一票一人
设备指纹 / 行为分拦截机器号
投票间隔SET vote:cd:{user} NX EX
结果公示延迟截止后 MQ 汇总再发榜,削峰读

与相邻场景对照

场景重复提交数量准确性
点赞可合并可近似
投票必须拒绝票数要准
限量抽签必须拒绝名额 不能超
秒杀幂等订单库存不能超

读优化

  • 榜单位:ZREVRANGE + 本地缓存 1~3s
  • 活动结束后切静态结果页(CDN),避免长期热 key

检查清单

  • 是否 SADD / 唯一键保证一人一票?
  • 限量是否 Lua 或 DB 原子扣减 + 唯一约束?
  • 榜是否读 Redis、写是否异步?
  • 截止后是否降读压力(静态页)?
  • 是否有防刷与审计日志?

相关