高并发下在线用户统计的实战思路

做电商系统的时候,遇到过这么个场景:双十一零点刚过,服务器监控显示在线用户数瞬间飙到 50 万。这时候老板问:现在到底有多少人在线?你得马上答得出来,不能卡壳。

传统方式撑不住

很多人第一反应是查数据库登录记录,比如 select count(*) from user_sessions where last_active > now() - interval 5 minute。这在几百人在线时没问题,但到了几十万级别,光是这条 SQL 就能把数据库拖垮。

更别说还有网络延迟、连接池耗尽这些问题。等结果出来,黄花菜都凉了。

用 Redis 做实时计数

我们后来改用 Redis 的 ZSET(有序集合)来存活跃用户。每个用户登录或刷新页面时,更新一次时间戳:

zadd online_users $current_timestamp $user_id

然后定期清理过期用户,比如 5 分钟没动静的:

zremrangebyscore online_users 0 $cutoff_timestamp

查总数就一条命令:

zcard online_users

响应基本在毫秒级,扛住百万级并发也没问题。

分片减轻压力

单个 Redis 实例也有瓶颈。我们按用户 ID 取模,分成 10 个 key,比如 online_users:0 到 online_users:9。每次更新只操作对应分片。

查总数时并行获取各分片数量再相加。虽然多了几次网络请求,但避免了单点过热,整体更稳。

别忘了降级方案

有一次 Redis 集群出问题,我们临时切回数据库异步统计。不是实时查,而是每 30 秒由后台任务更新一次缓存值。虽然有点延迟,但至少页面不会崩。

这种“能用就行”的兜底策略,在大促期间救过好几次场。

上报频率也得控制

前端每隔几秒就发一次心跳?用户开着页面不关,后台瞬间爆炸。我们改成监听页面可见状态,只有用户真正在看页面才上报,切到别的标签页就暂停。

既减少了无效请求,又更贴近真实“在线”定义——毕竟用户不在屏幕前,算啥在线呢。

这套方案上线后,从原来查一次要 8 秒,变成现在 20 毫秒内返回,系统压力下降七成。最关键的是,老板再问“现在多少人在线”,我们能笑着秒回了。