做后台系统时,经常要面对一个实际问题:怎么准确知道当前有多少人在线?尤其是在用户量突然激增的情况下,比如某个促销活动刚上线,流量瞬间翻了十倍,这时候如果统计机制扛不住,轻则数据不准,重则直接拖垮服务。
传统方式的瓶颈
很多人第一反应是用数据库记录每个用户的登录状态,定时轮询统计 active 的用户数。这在小规模系统里没问题,但一旦并发上来,比如每秒几千个请求更新状态,数据库连接池很快就满了。更别说每次统计还得扫一遍 user 表,加锁、慢查询接踵而至。
还有人用 session 统计,但分布式部署下 session 不共享,得依赖 Redis 做集中存储。虽然比数据库快,但如果每个请求都去 set 一次最后活跃时间,Redis 也扛不住高频写入。
用滑动窗口减少写压力
一个实用的做法是引入“心跳上报+滑动过期”机制。前端每隔 30 秒发一次心跳,后端不直接写数据库,而是把用户 ID 写进 Redis,并设置 60 秒过期。这样只要用户持续活跃,这个 key 就会不断被刷新。
统计的时候,不用遍历所有 key,而是用 Redis 的 KEYS 指令不太现实——数据量大时会卡住实例。更好的方式是按业务维度分桶,比如把用户按 UID 取模分成 10 个集合,每个集合用一个 Sorted Set 存储,score 是最后活跃时间戳。
zadd online_users:0 <timestamp> user_12345
expire online_users:0 60
查在线人数时,先 zremrangebyscore 清理过期成员,再 zcard 获取数量。这样每次操作都在可控范围内,不会全量扫描。
结合内网穿透调试真实场景
开发阶段很难模拟高并发,本地服务又没法被外部访问。这时候可以用内网穿透工具把测试服务暴露出去,比如用 frp 把本地的统计接口映射到公网。
[user-stat]
type = http
local_port = 8080
subdomain = stat
然后用压测工具从外网发起请求,模拟几千个设备同时上报。通过内网穿透后的域名访问接口,观察 Redis 内存和响应延迟的变化,及时调整过期时间和分片策略。
有个实际案例:我们给一个社区平台做在线统计,刚开始用单个 key 存所有用户,结果凌晨三点 Redis 内存暴涨,监控报警。后来改成按区域分片,加上前置过滤(只有真正进入主页面才上报),压力立马降下来。
避免精确陷阱
其实大多数业务并不需要绝对精确的实时数字。显示“当前在线 8,432 人”和“约 8,400 人”对用户来说没区别。可以接受一定程度的近似,换取系统稳定性。
比如用 HyperLogLog 来估算基数,Redis 提供的 pfadd 和 pfcount 指令,几万并发下内存占用不到几 MB,性能远超集合操作。虽然有一定误差,但在展示类场景完全够用。
pfadd hll_online user_12345
pfcount hll_online
关键是根据场景选型:运营后台要查明细,就得用精确方案;前台展示热度,用估算更划算。