美文网首页技术分享
redis精进 - 玩归玩、闹归闹,别拿Bitmap开玩笑

redis精进 - 玩归玩、闹归闹,别拿Bitmap开玩笑

作者: java菲菲 | 来源:发表于2019-12-30 21:20 被阅读0次

推荐阅读:

1. 为何面试“字节”屡次惨败?都是“算法”在搞鬼,你却不知道!
2. 终极手撕之架构大全:分布式+框架+微服务+性能优化,够不够?

01 BitMap 是什么

8 个 bit 组成一个 Byte,所以 bitmap 极大的节省储存空间
你可以把它理解为一个特殊处理过的 字符串
key代表业务属性、标签。一个 bit 位来表示某个元素对应的值或者状态。
举个例子:登记每天活跃用户,key 代表 登录时间, 1、2、3...代表 用户id


image.png

那么根据上面位图可以得出:
用户id:1 在 20191231 登录过
用户id:2 在 20191230,20200101 登录过
用户id:4 在 20191231 登录过
其余没有登录

02 Redis 中的 BitMap

已经是源于 2.2.0 版本的 "新技术"。我觉得你会看到那个 双引号 不会当真的
新增了setbit,getbit,bitcount等几个 bitmap 相关命令。但其实 setbit 等命令只不过是在 set 上的扩展而已。

2.1 setbit 命令介绍

指令 SETBIT key offset value
复杂度 O(1)
设置或者清空 key 的 value(字符串)在 offset 处的 bit 值(只能只 0 或者 1)。

2.2 空间占用、以及第一次分配空间需要的时间

在一台 2010MacBook Pro 上

offset 为 2^32-1(分配 512MB)需要~ 300ms
offset 为 2^30-1(分配 128MB)需要~ 80ms
offset 为 2^28-1(分配 32MB)需要~ 30ms
offset 为 2^26-1(分配 8MB)需要 8ms。
-- <来自官方文档>
大概的空间占用计算公式是:($offset/8/1024/1024)MB

03 使用场景

3.1 统计活跃用户

使用时间作为 cacheKey,然后用户 ID 为 offset,如果当日活跃过就设置为 1
那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令
命令 BITOP operation destkey key [key ...]
说明:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
说明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数

//日期对应的活跃用户
$data = array(
    '2020-01-10' => array(1,2,3,4,5,6,7,8,9,10),
    '2020-01-11' => array(1,2,3,4,5,6,7,8),
    '2020-01-12' => array(1,2,3,4,5,6),
    '2020-01-13' => array(1,2,3,4),
    '2020-01-14' => array(1,2)
);
//批量设置活跃状态
foreach($data as $date=>$uids) {
    $cacheKey = sprintf("stat_%s", $date);
    foreach($uids as $uid) {
        $redis->setBit($cacheKey, $uid, 1);
    }
}

$redis->bitOp('AND', 'stat', 'stat_2020-01-10', 'stat_2020-01-11', 'stat_2020-01-12');

//总活跃用户:6
echo "总活跃用户:" . $redis->bitCount('stat') . PHP_EOL;

$redis->bitOp('AND', 'stat1', 'stat_2020-01-10', 'stat_2020-01-11', 'stat_2020-01-14') . PHP_EOL;

//总活跃用户:2
echo "总活跃用户:" . $redis->bitCount('stat1') . PHP_EOL;

$redis->bitOp('AND', 'stat2', 'stat_2020-01-10', 'stat_2020-01-11') . PHP_EOL;

//总活跃用户:8
echo "总活跃用户:" . $redis->bitCount('stat2') . PHP_EOL;

假设当前站点有 5000W 用户,那么一天的数据大约为 50000000/8/1024/1024=6MB

3.2 用户签到、用户在线状态

这些都大同小异我就不,慢性谋杀你们的时间了。Peace&Love

04 有哪些需要注意

4.1 需要压缩运算

我觉得 人对事物的认知,得经过懵懂但美好憧憬 -> 被欺骗(坑)感情 -> 再次审视自己和事物 才能建立起一个 客观正确 的认知 -- 越欣赏越懂欣赏

Redis Bitmap 的好在于 ta 压缩存储空间。在日常用法中,这种压缩的代价是要经过 CPU 运算的。
大量数据的 setBit 会造成大量的网络请求。所以一般是 程序中 把 id 数组 pack() 设进位图变成一个 String。再一次性 set 进 Redis。
这就意味着 取出来的时候需要 unpack() 把 String 解压成 id 数组。不过得益于算法,这一步并不算太复杂

4.2 存储数据有限

另外ta 存储的数据相当有限,举个例子:

// 正常情况的 用户 id:1、3 登录数组:
'login20191230' => array(
    1 => array(
        'user_id' => 1,
        'login_ip' => 'x.x.x.x',
        'usage_agent' => 'xxx'
    ),
    3 => array(
        'user_id' => 3,
        'login_ip' => 'x.x.x.x',
        'usage_agent' => 'xxx'
    ),
)

// bitmap 的 登录数组
'login20191230' => array(
    0 => 0,
    1 => 1,   
    2 => 0,
    3 => 1,
)

可以用的的地方只有 'login20191230' 和 数组里的 key
你会问 数组里的 value 不是看起来也能改么
我给你个表情自己领会😑。硬要折腾是可以的,只是收支不平衡就是了

作者:锐玩道
原文链接:https://juejin.im/post/5e09496df265da33b071914a

相关文章

  • redis精进 - 玩归玩、闹归闹,别拿Bitmap开玩笑

    推荐阅读: 1. 为何面试“字节”屡次惨败?都是“算法”在搞鬼,你却不知道!2. 终极手撕之架构大全:分布式+框架...

  • 2020-12-19

    玩归玩,闹归闹,别拿生命开玩笑!i

  • 玩归玩,闹归闹,别拿朋友开玩笑

    星星,若是来自地狱,那么没有一丝光亮也是正常的吧!此生不悔入地狱,来世再为天上星,我是来自地狱的星星,愿我的文字可...

  • 玩归玩,闹归闹,别拿名著开玩笑!

    醒木一响,飞雨开讲!上文书我们说到小滕来找范闲,结果因为范二公子两次闯入闹了一些尴尬的误会。其实小滕这次来找范闲的...

  • 南理新记:第二十一篇

    玩归玩,闹归闹,别拿生活开玩笑。 耍归耍,皮归皮,五好青年打嘴炮。 今天呢,去了那个新闻机构,在此之前还是有些犹豫...

  • 玩归玩,闹归闹

    昨晚玩的太尽兴,直接忘记了更文。 一条短信,弄的我到处找,我是哪里开通了什么,为什么要扣费呢? 去微信...

  • 论“柴犬”

    玩归玩,闹归闹,别拿生命开玩笑,人在飞,魂在追,一不小心一堆灰,曲一响,布一盖,亲戚朋友等上菜 ,走的走抬的抬,后...

  • 这说的什么呢?

    玩归玩,闹归闹,别拿菜刀开玩笑,刀在飞,魂在追,一不小心一堆灰,曲一响,布一盖,亲戚朋友等上菜 ,走的走抬的抬,后...

  • 冷静

    玩归玩,闹归闹 底线不能降 节奏不能乱

  • 年轻人,玩归玩,闹归闹,最后还是要回家

    文/酸辣土豆丝 年轻人,玩归玩,闹归闹,最后还是要回家。 放下一个人就是一瞬间的事,但是这个一瞬间之前,你要经历无...

网友评论

    本文标题:redis精进 - 玩归玩、闹归闹,别拿Bitmap开玩笑

    本文链接:https://www.haomeiwen.com/subject/oaqvoctx.html