先赞后看,养成习惯❤️
BitMap 是什么
- 8 个 bit 组成一个 Byte,所以 bitmap 极大的节省储存空间
你可以把它理解为一个特殊处理过的 字符串 - key代表业务属性、标签。一个 bit 位来表示某个元素对应的值或者状态。
举个例子:登记每天活跃用户,key 代表 登录时间, 1、2、3...代表 用户id
key 0 1 2 3 4 5 6 7 login20191230 0 0 1 0 0 0 0 0 login20191231 0 1 0 0 1 0 0 0 login20200101 0 0 1 0 0 0 0 0
那么根据上面位图可以得出:
用户id:1 在 20191231 登录过
用户id:2 在 20191230,20200101 登录过
用户id:4 在 20191231 登录过
其余没有登录
Redis 中的 BitMap
已经是源于 2.2.0 版本的 "新技术"。我觉得你会看到那个 双引号 不会当真的
新增了setbit,getbit,bitcount等几个 bitmap 相关命令。但其实 setbit 等命令只不过是在 set 上的扩展而已。
setbit 命令介绍
指令 SETBIT key offset value
复杂度 O(1)
设置或者清空 key 的 value(字符串)在 offset 处的 bit 值(只能只 0 或者 1)。
空间占用、以及第一次分配空间需要的时间
在一台 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
使用场景
统计活跃用户
使用时间作为 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');//总活跃用户:6echo "总活跃用户:" . $redis->bitCount('stat') . PHP_EOL;$redis->bitOp('AND', 'stat1', 'stat_2020-01-10', 'stat_2020-01-11', 'stat_2020-01-14') . PHP_EOL;//总活跃用户:2echo "总活跃用户:" . $redis->bitCount('stat1') . PHP_EOL;$redis->bitOp('AND', 'stat2', 'stat_2020-01-10', 'stat_2020-01-11') . PHP_EOL;//总活跃用户:8echo "总活跃用户:" . $redis->bitCount('stat2') . PHP_EOL;复制代码
假设当前站点有 5000W 用户,那么一天的数据大约为 50000000/8/1024/1024=6MB
用户签到、用户在线状态
这些都大同小异我就不,慢性谋杀你们的时间了。Peace&Love
有哪些需要注意
需要压缩运算
我觉得 人对事物的认知,得经过懵懂但美好憧憬 -> 被欺骗(坑)感情 -> 再次审视自己和事物 才能建立起一个 客观正确 的认知 -- 越欣赏越懂欣赏
Redis Bitmap 的好在于 ta 压缩存储空间。在日常用法中,这种压缩的代价是要经过 CPU 运算的。
大量数据的 setBit 会造成大量的网络请求。所以一般是 程序中 把 id 数组 pack() 设进位图变成一个 String。再一次性 set 进 Redis。
这就意味着 取出来的时候需要 unpack() 把 String 解压成 id 数组。不过得益于算法,这一步并不算太复杂
存储数据有限
另外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 不是看起来也能改么
我给你个表情自己领会。硬要折腾是可以的,只是收支不平衡就是了