前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 亿级用户信息存储实践:bitmap 位图存储

Redis 亿级用户信息存储实践:bitmap 位图存储

作者头像
一个会写诗的程序员
发布2021-06-21 20:17:23
2.1K0
发布2021-06-21 20:17:23
举报

bitmap简介

8 个 bit 组成一个 Byte,所以 bitmap 极大的节省储存空间

你可以把它理解为一个特殊处理过的 字符串 key代表业务属性、标签。一个 bit 位来表示某个元素对应的值或者状态。

bitmap 并不是一种数据结构,实际上它就是字符串,但是可以对字符串的位进行操作。

bitmap有自己的一套命令。可以把bitmap想象成一个以bit为单位的数组,数组的每个单元存储0和1,数组的下标叫做偏移量。

Redis 提供 setbit,getbit,bitcount等几个 bitmap 相关命令。但其实 setbit 等命令只不过是在 set 上的扩展而已。

setbit 命令介绍

指令

代码语言:javascript
复制
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。 – <来自官方文档: https://redis.io/commands/setbit> 大概的空间占用计算公式是:($offset/8/1024/1024)MB

Available since 2.2.0.

Time complexity: O(1)

Sets or clears the bit at offset in the string value stored at key.

The bit is either set or cleared depending on value, which can be either 0 or 1.

When key does not exist, a new string value is created. The string is grown to make sure it can hold a bit at offset. The offset argument is required to be greater than or equal to 0, and smaller than 232 (this limits bitmaps to 512MB). When the string at key is grown, added bits are set to 0.

Warning: When setting the last possible bit (offset equal to 232 -1) and the string value stored at key does not yet hold a string value, or holds a small string value, Redis needs to allocate all intermediate memory which can block the server for some time. On a 2010 MacBook Pro, setting bit number 232 -1 (512MB allocation) takes ~300ms, setting bit number 230 -1 (128MB allocation) takes ~80ms, setting bit number 228 -1 (32MB allocation) takes ~30ms and setting bit number 226 -1 (8MB allocation) takes ~8ms. Note that once this first allocation is done, subsequent calls to SETBIT for the same key will not have the allocation overhead.

Redis bitmap 的命令

bitmap的命令

常用命令 作用 1、getbit key offset 用于获取Redis中指定key对应的值,中对应offset的bit 2、setbit key key offset value 用于修改指定key对应的值,中对应offset的bit 3、 bitcount key [start end] 用于统计字符串被设置为1的bit数 4、bitop and/or/xor/not destkey key [key …] 用于对多个key求逻辑与/逻辑或/逻辑异或/逻辑非

设置 offset 位的 01 值

代码语言:javascript
复制
127.0.0.1:6379> setbit user10001 0 1
(integer) 0
127.0.0.1:6379> setbit user10001 3 1
(integer) 0
127.0.0.1:6379> getbit user10001 0
(integer) 1
127.0.0.1:6379> getbit user10001 1
(integer) 0
127.0.0.1:6379> getbit user10001 2
(integer) 0
127.0.0.1:6379> getbit user10001 3
(integer) 1
127.0.0.1:6379> getbit user10001 7
(integer) 1

批量设置 offset 位的 01 值

使用 u1 类型批量设置 offset 位的 01 值:

代码语言:javascript
复制
127.0.0.1:6379> bitfield user10001 set u1 2 1 set u1 9 1 set u1 10 1
1) (integer) 0
2) (integer) 0
3) (integer) 0
127.0.0.1:6379> getbit user10001 10
(integer) 1
127.0.0.1:6379> getbit user10001 9
(integer) 1
127.0.0.1:6379> getbit user10001 8
(integer) 0
127.0.0.1:6379> getbit user10001 7
(integer) 1

bitmap的应用场景

使用经验: type = string,BitMap 是 sting 类型,最大 512 MB。 注意 setbit 时的偏移量,可能有较大耗时 位图不是绝对好。

通过 bitcount可以很快速的统计,比传统的关系型数据库效率高很多

1、比如统计年活跃用户数量

用户的ID作为offset,当用户在一年内访问过网站,就将对应offset的bit值设置为“1”;

通过bitcount 来统计一年内访问过网站的用户数量

2、比如统计三天内活跃用户数量

时间字符串作为key,比如 “190108:active“ “190109:active”“190110:active” ;

用户的ID就可以作为offset,当用户访问过网站,就将对应offset的bit值设置为“1”;

统计三天的活跃用户,通过bitop or 获取一周内访问过的用户数量

3、连续三天访问的用户数量 bitop and

4、三天内没有访问的用户数量 bitop not

5、统计在线人数 设置在线key:“online:active”,当用户登录时,通过setbit设置

bitmap的优势,以统计活跃用户为例

每个用户id占用空间为1bit,消耗内存非常少,存储1亿用户量只需要12.5M

使用场景: 统计活跃用户

使用时间作为 cacheKey,然后用户 ID 为 offset,如果当日活跃过就设置为 1 那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令 命令

代码语言:javascript
复制
BITOP operation destkey key [key ...]

说明:对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。 说明:BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数

代码语言:javascript
复制
//日期对应的活跃用户
$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

布隆过滤器

bitmap - Redis布隆过滤器 (应对缓存穿透问题)

举例:比如爬虫服务器在爬取电商网站的商品信息时,首先经过缓存,如果缓存查不到,再去数据库获取信息,因为爬虫的效率很高,且sku很有可能是不存在或者已下架的,就会造成缓存穿透,大量请求被发送到数据库,导致服务器受到影响。

此时,可以在缓存层之前,添加一个布隆过滤器,布隆过滤器看作是一个bitmap,sku作为offset值,如果商品真实存在,bit值设为1。首先将商品数据初始化,当有请求时,通过getbit判断sku是否有效。如果布隆过滤器认为商品不存在,就拒绝访问,这样就可以保护存储层。

Data structures are nothing different. They are like the bookshelves of your application where you can organize your data. Different data structures will give you different facility and benefits. To properly use the power and accessibility of the data structures you need to know the trade-offs of using one.

大意是不同的数据结构有不同的适用场景和优缺点,你需要仔细权衡自己的需求之后妥善适用它们,布隆过滤器就是践行这句话的代表。

参考资料

https://blog.csdn.net/weixin_42383575/article/details/86684283

https://zhuanlan.zhihu.com/p/136337229

https://www.imooc.com/article/298707

https://www.jianshu.com/p/4c8e119f35db

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • bitmap简介
    • setbit 命令介绍
    • Redis bitmap 的命令
      • 设置 offset 位的 01 值
        • 批量设置 offset 位的 01 值
        • bitmap的应用场景
          • 使用场景: 统计活跃用户
          • 布隆过滤器
          • 参考资料
          相关产品与服务
          云数据库 Redis
          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档