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

2023-12-26 05:59

本文主要是介绍Redis 亿级用户信息存储实践:bitmap 位图存储,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

bitmap简介

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

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

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

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

Redis 提供 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。
– <来自官方文档: 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 值

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 值:

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 的命令
命令

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

布隆过滤器

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


http://www.taodudu.cc/news/show-8402039.html

相关文章:

  • 总线制和多线制示意图_总线制与多线制的区别
  • 节节提高 提升绿色IT三大新境界
  • 数据库select操作
  • 势不可挡!Android手机在英国手机市场上的销量节节攀升
  • 用 Apache Derby 进行开发 —— 取得节节胜利: 使用 Apache Derby 进行数据库开发,第 1 部分
  • 达梦数据库渠道建设取得新突破 数据库销售节节攀高
  • 《玩转Windows 7 》销售节节攀高
  • 中秋国庆_节节快乐
  • 用 Apache Derby 进行开发 —— 取得节节胜利: 用 Apache Derby 进行 Java 数据库开发,第 1 部分
  • 交换次数【第九届蓝桥杯国赛】
  • 职场节节攀升有十招
  • 9届国赛java试题 5: 交换次数
  • 20170117L07-1-2老男孩linux运维实战培训-初级第七节节课课前考试第一题route命令讲解...
  • 职场兵法:十大招数让你薪酬节节攀升
  • 第九届蓝桥杯 2018年国赛真题(Java 大学C组)
  • 在平板市场份额节节攀升的华为将发新平板
  • 用 Apache Derby 进行开发 —— 取得节节胜利: 介绍 Apache Derby
  • 第一盘节节登高的做法
  • Net Applications:IE份额还剩67% 对手节节追赶
  • 戴尔股价节节攀升,但去中国化导致老本行正被中国消费者抛弃
  • AIGC参数量节节攀升,对存储带来的挑战如何解决?
  • 主机与虚拟机都可以上网,但是互相ping不通
  • 算法一般都可以用哪几种控制结构组合而成(dell)
  • 【数据库】所有的视图都可以更新吗?请举例说明。
  • 【转】Tomcat webapps下面的东西都可以删除吗
  • MySQL设置所有IP地址都可以访问数据库
  • 为何 as sysdba的方式登陆oracle数据库,为何随便输入用户名和密码都可以登陆?
  • 给定N个非0的个位数字,用其中任意2个数字都可以组合成1个2位的数字。要求所有可能组合出来的2位数字的和。例如给定2、5、8,则可以组合出:25、28、52、58、82、85,它们的和为330。...
  • 是不是所有的文法都可以化为LL(1)文法?
  • C#如何定义全局变量,不同的子窗体和类都可以访问
  • 这篇关于Redis 亿级用户信息存储实践:bitmap 位图存储的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



    http://www.chinasem.cn/article/538327

    相关文章

    redis中使用lua脚本的原理与基本使用详解

    《redis中使用lua脚本的原理与基本使用详解》在Redis中使用Lua脚本可以实现原子性操作、减少网络开销以及提高执行效率,下面小编就来和大家详细介绍一下在redis中使用lua脚本的原理... 目录Redis 执行 Lua 脚本的原理基本使用方法使用EVAL命令执行 Lua 脚本使用EVALSHA命令

    在 Spring Boot 中实现异常处理最佳实践

    《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

    Redis 热 key 和大 key 问题小结

    《Redis热key和大key问题小结》:本文主要介绍Redis热key和大key问题小结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、什么是 Redis 热 key?热 key(Hot Key)定义: 热 key 常见表现:热 key 的风险:二、

    C#使用StackExchange.Redis实现分布式锁的两种方式介绍

    《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

    Go语言开发实现查询IP信息的MCP服务器

    《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

    关于MongoDB图片URL存储异常问题以及解决

    《关于MongoDB图片URL存储异常问题以及解决》:本文主要介绍关于MongoDB图片URL存储异常问题以及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录MongoDB图片URL存储异常问题项目场景问题描述原因分析解决方案预防措施js总结MongoDB图

    Spring Boot 整合 SSE的高级实践(Server-Sent Events)

    《SpringBoot整合SSE的高级实践(Server-SentEvents)》SSE(Server-SentEvents)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实... 目录1、简述2、Spring Boot 中的SSE实现2.1 添加依赖2.2 实现后端接口2.3 配置超时时

    Redis Pipeline(管道) 详解

    《RedisPipeline(管道)详解》Pipeline管道是Redis提供的一种批量执行命令的机制,通过将多个命令一次性发送到服务器并统一接收响应,减少网络往返次数(RTT),显著提升执行效率... 目录Redis Pipeline 详解1. Pipeline 的核心概念2. 工作原理与性能提升3. 核

    Python使用getopt处理命令行参数示例解析(最佳实践)

    《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

    redis过期key的删除策略介绍

    《redis过期key的删除策略介绍》:本文主要介绍redis过期key的删除策略,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录第一种策略:被动删除第二种策略:定期删除第三种策略:强制删除关于big key的清理UNLINK命令FLUSHALL/FLUSHDB命