卡飞资源网

专业编程技术资源共享平台

使用redis bitmap计算日活跃用户数

Metrics(指标)在允许延迟的情况下,通常通过job任务定时执行(如按小时、每天等频率),而基于 Redis 的Bitmap使我们能够实时完成此类计算,且极其节省空间。以亿级用户计算 “日活跃用户数” 指标这个场景为例,在 MBP 上的计算耗时不到 50 毫秒,仅占用 16MB 内存。

Bitmap

Bitmap(位图)是由 0 和 1 组成的数组,其中每个位可以设置为 0 或 1,数组中的每个位置称为偏移量。Bitmap支持逻辑与、或、异或等按位操作。

Population Count

Population Count算法是指计算一个二进制数中1的个数的算法。具体来说,就是任意给定一个无符号整数N,求N的二进制表示中1的个数,比如N = 5(0101)时,返回2;N = 15(1111)时,返回4。

可使用Redis BITCOUNT高效计算Population Count。甚至在支持SSE4指令集的CPU上,可以使用POPCNT硬件指令来计算二进制数中1的个数。

Redis中的Bitmap

Redis 支持二进制键和二进制值,而Bitmap本质上就是二进制值。SETBIT(key, offset, value)操作可将指定键在偏移量处的位设置为 0 或 1,该操作的时间复杂度为 O (1)。

日活跃用户数

为统计某日登录的唯一用户数,可以创建一个Bitmap,其中每个用户由一个偏移量(用户ID)标识。当用户访问页面或执行需要计数的操作时,将代表该用户 ID 的偏移量处的位设置为 1。位图的键由用户执行的操作名称和时间戳共同决定。

当用户登录时,我们执行redis.setbit(daily_active_users, user_id, 1),这会将daily_active_users Bitmap中对应的偏移量置为 1。对该位图进行人口计数,即可得到今日登录唯一用户数为9。此时键为daily_active_users,值为1011110100100101。

不过,由于日活跃用户每天都会变化,所以需要每天创建一个新的位图。实现方式很简单:将日期附加到位图键后,例如可将键命名为
daily_active_users:yyyy-mm-dd。

redis.setbit(daily_active_users:yyyy-mm-dd, user_id, 1)

某日登录过的唯一用户数,即为
daily_active_users:yyyy-mm-dd键对应位图的Population Count。若要计算周或月指标,只需对一周或一个月内的每日位图取并集,再计算结果位图的Population Count即可。

import redis.clients.jedis.Jedis;
import java.util.BitSet;
...
  Jedis redis = new Jedis("localhost");
...
  public int uniqueCount(String action, String... dates) {
    BitSet all = new BitSet();
    for (String date : dates) {
      String key = action + ":" + date;
      BitSet users = BitSet.valueOf(redis.get(key.getBytes()));
      all.or(users);
    }
    return all.cardinality();
  }

亿级用户下的性能对比

下表对比了 1.28 亿用户在 1 天、7 天和 30 天内的日活跃数计算耗时(7 天和 30 天指标通过合并每日位图计算得出):

周期

耗时(毫秒)

每日

50.2

每周

392.0

每月

1624.8

结论

Redis Bitmap不仅能有效地节省存储空间,还能提高性能。无论是用户行为分析、活动参与度统计,还是其他需要快速计算交集的场景,Bitmap提供了一种极具优势的处理方式。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言