hyperloglog 不适合实时在线人数统计,只适用于日/周/月 uv 去重计数;因其只增不删、无过期机制、无法反查成员且误差大,pfcount 返回的并非真实在线人数。

HyperLogLog 不适合做「实时在线人数」统计,只适合做「日/周/月 UV」去重计数。如果你在 ThinkPHP 里用 PFADD + PFCOUNT 统计“当前在线多少人”,结果一定是错的——它根本不知道谁下线了。
下面说清楚怎么用、为什么不能乱用、以及真正该用什么。
为什么 PFCOUNT 返回的不是在线人数?
HyperLogLog 是个“只增不删”的概率计数器:它只记录“这个人来过”,从不关心“他走了没”“他还在线吗”。你调一次 PFADD uv:20260507 user123,哪怕 user123 两秒后就关掉网页,PFCOUNT uv:20260507 永远会把它算进去。
- 没有过期机制:不会自动剔除长时间无活动的用户
- 无法反查成员:
PFCOUNT只返回一个数字,你没法知道哪些 user_id 还“活着” - 误差不可控:当单日 UV 小于 100 时,误差可能达 ±10,而真实在线人数常在几十到几百之间
ThinkPHP 中正确接入 HyperLogLog 的姿势
它唯一靠谱的用途是:按天粒度统计独立访客(UV),比如后台看板显示“今日 UV:124,892”。
- Key 必须带日期后缀,例如
uv:20260507,绝不能复用uv:all这种长期 key - 用户标识用稳定字段:
user_id(登录态)或device_id(未登录),别用每次刷新都变的session_id - 写入时机放在请求入口(如中间件),但需加判断:同个用户在同一天只
PFADD一次,避免重复计入 - 用
$redis->pfAdd('uv:'.date('Ymd'), $uid),ThinkPHP 自带 Redis 封装可直接调 - 记得设过期:
$redis->expire('uv:'.date('Ymd'), 86400 * 30),防止 key 积压
那 ThinkPHP 真正该用什么统计“当前在线人数”?
要用支持“活跃时间窗口”和“自动清理”的结构:ZSET(有序集合)最实用。
- 每次用户有动作(页面访问、AJAX 请求),执行:
ZADD online_users 1619999999 user123,score 设为当前时间戳 - 定时或每次查询前先清理:
ZREMRANGEBYSCORE online_users 0 (1619999999-300)(踢掉 5 分钟前无操作的) - 统计就是:
ZCARD online_users,毫秒级响应,精确到个位 - ThinkPHP 中可封装成一个
OnlineUser::touch($uid)和OnlineUser::count()方法
如果非要用 HyperLogLog,只允许它出现在“UV 报表”这类场景;一旦你发现需求里出现“实时”“当前”“在线”“还活着”这些词,立刻放弃 PFADD,换 ZSET 或 Bitmap(后者要求 ID 是连续整数)。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











