V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sakudie
V2EX  ›  Redis

redis 统计在线人数的疑问

  •  
  •   sakudie · 2018-06-22 16:04:33 +08:00 · 6444 次点击
    这是一个创建于 2106 天前的主题,其中的信息可能已经有所发展或是发生改变。

    系统 session 设置为 2 小时, 并且我的 session 信息是直接存储在 redis 里面的

    现在利用 rediszset 统计人数,每当用户登陆的时候, zadd 添加当前用户 id,score 为登陆时间戳

    这个比较适合统计 一天 或者 一周 的上线人数, 直接 zrange 结果也比较准确

    如果我想同时利用 zset 统计 当前在线人数, 必须得 每当用户访问页面时, 更新用户的 score 为新的时间戳, 是这样吗?

    那么我直接在 redis 中维护一个 cur_online_user_numkey, 每当 session 创建的时候, 值++ , 每当 session 失效的时候 值-- , 这样是不是更好点, 避免了 频繁更新 score 的问题?

    12 条回复    2018-06-23 11:57:34 +08:00
    joaner
        1
    joaner  
       2018-06-22 17:09:15 +08:00   ❤️ 1
    是的,需要每次访问页面都用 ZADD 更新时间,redis 会按用户 ID 自动去重。
    查询 2 小时内的在线人数时:`ZCOUNT views ($now_timestamp-7200) 9999999999`

    你后面的方案简单点,但 session 失效时的钩子不好设置。
    hiboshi
        2
    hiboshi  
       2018-06-22 17:38:53 +08:00
    如果用户很长时间不刷新呢?不也在线 比如看一篇比较难懂的文章,建议加个心跳接口。每间隔一段时间 t 去做刷新 last_time.
    zzf2017
        3
    zzf2017  
       2018-06-22 17:42:18 +08:00
    大佬有文章或者方法吗?我最近也在弄这些东西,但是处理起来不太好
    holyghost
        4
    holyghost  
       2018-06-22 18:02:25 +08:00   ❤️ 1
    zzf2017
        5
    zzf2017  
       2018-06-22 18:07:51 +08:00
    @holyghost 你的这种太高端了,还是全英文的,看不懂啊
    a7a2
        6
    a7a2  
       2018-06-22 18:10:07 +08:00
    避免频繁更新 redis 不是这样设计

    而是“每当用户访问页面时”记录在代码 map 中,开一个专门处理这个 map 数据的任务,每 1 分钟处理一次,把 map 记录的页面访问时间对比 redis 时间才更新。

    就是 redis 前自己缓存一下的意思,网站 pv 量很高的时候也不至于把 redis 搞得太忙
    @sakudie
    sakudie
        7
    sakudie  
    OP
       2018-06-22 18:20:40 +08:00
    @hiboshi 这个 session 在服务端有定时检测的,过期的会被主动清理掉,session 有效时间设置为 2 小时够长了。
    sakudie
        8
    sakudie  
    OP
       2018-06-22 18:22:28 +08:00
    @a7a2 如果放到 map 里面,到时候就算定期比较、更新不是要遍历 zset 吗
    owenliang
        9
    owenliang  
       2018-06-22 18:25:30 +08:00   ❤️ 1
    分享一个我的实现。

    假设 2 分钟以内访问过网站,算作在线。

    那么,当一个访问请求过来的时候,假设此时是 1970 年以来的第 N 分钟,那么你应该执行这样一个 Redis 命令:

    pipeline
    zrem online_${N-1} uid
    zadd online_${N} 0 uid
    expire online_${N} 1800
    exec

    那么你想知道在线用户数量,只需要 zcard online_${N-1} + zcard online_${N},就是 2 分钟内的用户总数量了。

    考虑实际工程问题,用户量大的情况下肯定不能都写一个 redis 集合,可以按 uid 取模到不同的 Redis 实例中,最后统计时做加和。
    a7a2
        10
    a7a2  
       2018-06-22 18:31:32 +08:00   ❤️ 1
    @sakudie 不需要比较了 直接 map 里面有最后的浏览记录直接写 redis 写完要清空 map 即可免于比较。遍历 map 是必须的,速度也肯定很快,redis 操作这里使用 hmset 效率比单个 set 要高(猜测)
    a7a2
        11
    a7a2  
       2018-06-22 18:45:51 +08:00
    @sakudie
    记得 遍历+清空 map 前要加锁,写最后浏览数据到 map 建议通过 mq 完成,遍历 map 数据写 redis 也 mq。
    这样保障单服务器每秒 pv 上万的时候不至于在 每当遍历 map 时候卡住一下,因为遍历+清空 map 前有一个锁 这样才能保证每一个数据不丢失
    julyclyde
        12
    julyclyde  
       2018-06-23 11:57:34 +08:00
    zrange 速度很慢的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4115 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 10:16 · PVG 18:16 · LAX 03:16 · JFK 06:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.