BitMap介绍与应用

news2024/9/20 16:14:24

文章目录

  • BitMap
    • BitMap介绍
      • BitMap 结构
      • RoaringBitmap
    • 常见BitMap
      • Java中的BitSet
      • Redis中的BitMap
      • ClickHouse中的BitMap
    • BitMap应用案例
      • 人群圈选

BitMap

场景一:(大部分开发面试都会遇到的一个问题)

有10亿个用户id (int类型),判断用户是否登录?(限制内存)

🤔 10亿个int类型,10亿 × 4 = 40亿byte = 4 GB,如何既要保证速度,又要保证占用内存呢?

场景二:

短视频平台中有十亿+用户,广告主想要精准投放流量,如何进行人群的圈选呢?

BitMap介绍

bit(位)map(地图)被叫做位图,其实理解其原理之后,可能叫位(bit)映射(map)更贴切一些。

BitMap比较适合通过比较小的内存解决大量数字的去重、排序和判存问题。

BitMap 结构

用场景一的问题举例:BitMap解决思路就是构建一个bit数组,bit每一位只能表示1或者0,其中数组的索引值映射到userId,当前索引上的数字是1的时候代表对应的userId已登录,是0的时候代表userId未登录。所以,逐行读取文件中的userid并给bit数组赋值,最终通过该bit数组就可以实现去重、排序和判存了。
image-20240112115256884.png
(上图中的用户34 就是已登录的)

假设每个位使用1比特(bit)来表示,那么十亿级别的位图将有10^9个位。由于每个字节(byte)包含8个位,我们可以将位数除以8来得到字节数。

总字节数 = 10^9 / 8 = 125,000,000 字节

将字节数转换为更常见的单位,可以得到以下结果:

  • 125,000,000 字节 = 125 兆字节(MB)

位图也不是万能的,还是上述的题目,如果需要统计出每一个userId的出现次数,位图这种通过1和0标识有和无的方式就无法表达这个业务含义了。当位图比较稀疏的时候,也可能浪费空间,比如文件中就1和1000000000这两个userid,那么为为了使用位图而开辟的bit数组大小已经远超过两个int数字所使用的空间了。

RoaringBitmap

RoaringBitmap可以认为是BitMap的另外一种实现,该实现方式中考虑了数据量的多少与存储空间的平衡问题。将32的数字分为高16位和低16位,其中低16位根据数据量的多少,可以通过三种数据结构来实现:ArrayContainerBitMapContainerRunContainer,其目的就是尽量节约存储空间。

  • 数组容器(Array Container) :这种容器使用一个数组来存储整数,适用于稀疏数据的情况,即数据之间的间隔较大。例如,如果一个容器内的整数数量小于4096,就使用数组容器来存储,因为在这种情况下,使用数组容器比使用位图容器更节省空间。
  • 位图容器(Bitmap Container) :位图容器使用一个长度为65536的位数组(bit array)来存储数据,适用于稠密数据的情况,即数据之间的间隔较小。例如,如果一个容器内的整数数量大于4096,就使用位图容器来存储,因为在这种情况下,使用位图容器比使用数组容器更节省空间。
  • 运行长度编码容器(Run Container) :运行长度编码(Run-length Encoding,RLE)是一种简单的数据压缩算法,适用于连续重复数据的情况。在 Roaring Bitmap 中,如果一系列连续的整数都存在于位图中,那么可以使用一个运行长度编码容器来存储这些连续的整数的起始值和长度,从而节省空间。

RoaringBitmap 根据实际的数据情况,动态地选择最适合的容器类型,从而实现了既高效的数据存储,又快速的数据查询。在做位图计算(AND、OR、XOR)时,RoaringBitmap提供了相应的算法来高效地实现在多个容器之间的运算,使得RoaringBitmap无论在存储和计算性能上都表现优秀。

image.png

常见BitMap

Java中的BitSet

BitSet 是 Java 中的一个类,用于表示位集合(bit set)。它提供了一种有效地存储和操作位(布尔值)的方式。

下面是一个示意图,展示了 BitSet 的基本结构:

+---------+---------+---------+---------+---------+---------+---------+---------+
| 位索引:  |    0    |    1    |    2    |    3    |    4    |    5    |    6    |
+---------+---------+---------+---------+---------+---------+---------+---------+
| 位值:    |   true  |   false |   true  |   false |   true  |   false |   false |
+---------+---------+---------+---------+---------+---------+---------+---------+

在上述示意图中,每个方框表示一个位,从左到右依次编号。位索引从 0 开始,表示每个位在 BitSet 中的位置。位值表示每个位的状态,可以是 true(被设置为 1)或 false(被设置为 0)。

BitSet 提供了一系列方法来操作位集合,例如:

  • set(int index):将指定索引位置的位设置为 true
  • clear(int index):将指定索引位置的位设置为 false
  • get(int index):获取指定索引位置的位的值。
  • size():获取位集合的大小(位的数量)。
  • length():获取位集合的长度(最高位的索引加 1)。
  • and(BitSet set):对当前位集合与另一个位集合执行逻辑与操作。
  • or(BitSet set):对当前位集合与另一个位集合执行逻辑或操作。
  • xor(BitSet set):对当前位集合与另一个位集合执行逻辑异或操作。

【例子】判断某个用户是否存在:

public class UserPresenceChecker {
    private BitSet userBitSet;public UserPresenceChecker() {
        userBitSet = new BitSet();
    }public void addUser(long userId) {
        userBitSet.set((int) userId);
    }public boolean isUserPresent(long userId) {
        return userBitSet.get((int) userId);
    }public static boolean checkUserPresence(long userId) {
        UserPresenceChecker presenceChecker = new UserPresenceChecker();// 假设有一亿用户存在
        for (int i = 0; i < 100000000; i++) {
            presenceChecker.addUser(i);
        }//模拟123不存在
        presenceChecker.userBitSet.set(123,false);
        return presenceChecker.isUserPresent(userId);
    }public static void main(String[] args) {
        long userId = 123;
        boolean isPresent = checkUserPresence(userId);if (isPresent) {
            System.out.println("用户 " + userId + " 存在");
        } else {
            System.out.println("用户 " + userId + " 不存在");
        }
    }
}

RoaringBitmap 在Java中也有实现,还提供了更多的逻辑运算方式。

Redis中的BitMap

Redis 的位图是一种特殊的字符串类型,可以用于存储和操作位级别的数据。

Redis 中的位图使用字节数组来表示,每个字节可以存储 8 个位。位图可以非常高效地进行位级别的操作,如设置位、获取位、计数位、位图之间的逻辑运算等。

以下是一些 Redis 中位图相关的命令:

  • SETBIT key offset value:设置指定偏移量上的位为指定值(0 或 1)。
  • GETBIT key offset:获取指定偏移量上的位的值。
  • BITCOUNT key [start end]:计算指定范围内的位为 1 的数量。
  • BITOP operation destkey key [key ...]:对多个位图进行逻辑运算,并将结果保存到目标位图中。
  • BITPOS key bit [start] [end]:查找指定位的第一个出现位置。

以下是一个示例,展示如何在 Redis 中使用位图:

# 设置位图
SETBIT mybitmap 0 1
SETBIT mybitmap 2 1
SETBIT mybitmap 5 1
​
# 获取位图
GETBIT mybitmap 0  # 返回 1
GETBIT mybitmap 1  # 返回 0
​
# 计算位图中位为 1 的数量
BITCOUNT mybitmap  # 返回 3
​
# 逻辑运算
SETBIT mybitmap2 1 1
SETBIT mybitmap2 3 1
BITOP AND mybitmap_and mybitmap mybitmap2  # 对两个位图进行 AND 运算,并将结果保存到 mybitmap_and 中
​
# 查找位为 1 的第一个出现位置
BITPOS mybitmap 1 0  # 返回 0
BITPOS mybitmap 1 1  # 返回 2

通过 Redis 的位图功能,你可以高效地存储和操作位级别的数据,例如记录用户的在线状态、统计用户活跃度、进行布隆过滤器等应用场景。

ClickHouse中的BitMap

😜3,2,1上链接!!!

https://clickhouse.com/docs/zh/sql-reference/functions/bitmap-functions

BitMap应用案例

人群圈选

如果有一张表,存储着每一个userid对应的常驻省和性别,如何从这张表中查出所有北京市男性用户的userId,并存储成一个人群包。

image.png

最简单直接的思路是select userId from table where sex = ‘男’ and province = ‘北京’,但是当满足条件的userId达到千万+级别时,通过SQL语句直接获取userId就不太合适,一是结果集比较大,二是执行比较耗时。

此时可以转换一个角度解决这类问题,如果表结构如下:

image.png
可以将不同标签值对应的userId列表转换成BitMap,再次筛选北京市男性用户的时候,直接将男BitMap与北京BitMap做交集即可,快速高效且节约大量的存储空间。

如图所示:

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1537349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue el-table 合并单元格

一般常见的就是下图这种的单列&#xff0c;上下重复进行合并。 有时候可能也会需要多行多列的合并。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&qu…

【LeetCode】--- 动态规划 集训(一)

目录 一、1137. 第 N 个泰波那契数1.1 题目解析1.2 状态转移方程1.3 解题代码 二、面试题 08.01. 三步问题2.1 题目解析2.2 状态转移方程2.3 解题代码 三、746. 使用最小花费爬楼梯3.1 题目解析3.2 状态转移方程3.3 解题代码 一、1137. 第 N 个泰波那契数 题目地址&#xff1a…

FloodFill算法——岛屿数量

文章目录 题目解析算法解析代码解析 题目解析 岛屿数量 题目依旧是熟悉的配方&#xff0c;熟悉的味道&#xff0c;还是那个0还是那个1还是那个二维矩阵&#xff0c;这时候BFS和DFS闻着味就来了&#xff0c;我们来看一下这个题目&#xff0c;这个题目也很容易理解如下图有一个…

阿里云2核4G服务器租用价格和性能测评

阿里云2核4G服务器租用优惠价格&#xff0c;轻量2核4G服务器165元一年、u1服务器2核4G5M带宽199元一年、云服务器e实例30元3个月&#xff0c;活动链接 aliyunfuwuqi.com/go/aliyun 活动链接如下图&#xff1a; 阿里云2核4G服务器优惠价格 轻量应用服务器2核2G4M带宽、60GB高效…

市场复盘总结 20240322

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 36% 最常用…

力扣题库27题移除元素(c语言)

解法&#xff1a; int removeElement(int* nums, int numsSize, int val) {int src0,dst0;while(src<numsSize){if(nums[src]val){src;}else{nums[dst]nums[src];src;dst;}}return dst; }

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述…

visual studio卸载几种方法

1、控制面板卸载&#xff1b; 2、有时候会发现控制面板卸载会失败&#xff0c;无法卸载&#xff0c;这时候要先把下面目录的关于visual studio的都删除&#xff0c;然后重启电脑后&#xff0c;重新安装vs即可。

C语言预编译#pragma宏的作用

在嵌入式编程中&#xff0c;#pragma 指令具有非常重要的作用&#xff0c;因为它允许开发者在不同的编译器之间传达特定的编译指令。由于嵌入式编程通常与硬件紧密相关&#xff0c;且资源有限&#xff0c;这些指令可以帮助开发者更有效地利用可用资源&#xff0c;优化程序&#…

基于python+vue的stone音乐播放器的设计与实现flask-django-php-nodejs

随着我国经济的高速发展与人们生活水平的日益提高&#xff0c;人们对生活质量的追求也多种多样。尤其在人们生活节奏不断加快的当下&#xff0c;人们更趋向于足不出户解决生活上的问题&#xff0c;stone音乐播放器展现了其蓬勃生命力和广阔的前景。与此同时&#xff0c;为解决用…

docker快速安装达梦数据库

docker快速安装达梦数据库 文章目录 docker快速安装达梦数据库前言环境准备下载镜像运行、配置容器 前言 因为公司需要将自己的底代码平台与客户的需求做适配&#xff0c;客户要求必须满足信创要求&#xff0c;使用达梦数据库。所以需要将原有的MySQL数据库与达梦数据库适配&a…

每日五道java面试题之springboot篇(一)

目录&#xff1a; 第一题. 什么是 Spring Boot&#xff1f;第二题. Spring Boot 有哪些优点&#xff1f;第三题. Spring Boot 的核心注解是哪个&#xff1f;它主要由哪几个注解组成的&#xff1f;第四题. 什么是 JavaConfig&#xff1f;第五题. Spring Boot 自动配置原理是什么…

来了,工业5.0

什么是工业5.0 “工业5.0”一词是由欧盟委员会引入和推广的&#xff0c;用于描述其对欧洲工业的愿景。 工业5.0的强调的不仅是技术&#xff0c;更注重是人性。提倡“以人为本”的思想。工业 5.0 不是专注于创造经济价值&#xff0c;而是激励企业探索如何通过提供更健康的工作…

排序算法记录(冒泡+快排+归并)

文章目录 前言冒泡排序快速排序归并排序 前言 冒泡 快排 归并&#xff0c;这三种排序算法太过经典&#xff0c;但又很容易忘了。虽然一开始接触雀氏这些算法雀氏有些头大&#xff0c;但时间长了也还好。主要是回忆这些算法干了啥很耗时间。 如果在笔试时要写一个o(nlogn)的…

java学习——集合

目录 一、集合框架介绍 1、集合与集合框架说明 2、使用集合框架原因 3、集合框架接口体系 二、Collection接口 1、Collection常用方法 2、AbstractCollection 三、迭代器 1、迭代器说明 2、自定义Collection集合 四、泛型 1、泛型说明 2、使用泛型方法 3、泛型通配…

哲♂学家带你深♂入了♂解结构体及结构体内存大小问题

目录 概要 一、结构体的声明 二、结构体变量的创建和初始化 三、结构体的特殊声明 四、结构体内存对齐 1、对齐原则 2、例一 对齐数 计算方法 3、例二 总结 概要 结构体是我们日常编程中经常要用到的一种自定义类型&#xff0c;使用起来也是十分的方便。接下来就由…

ts js vue 验证文件 MD5 值 spark-md5

ts js vue 验证文件 MD5 值 spark-md5 如何在前端中验证要上传的文件的 md5 值 一、安装 spark-md5 插件 需要用到 spark-md5 这个插件 官方 github&#xff1a;https://github.com/satazor/js-spark-md5/tree/master yarn add spark-md5 // 或 npm i spark-md5使用的时候引…

TCP | TCP协议格式 | 三次握手

1.TCP协议 为什么需要 TCP 协议 &#xff1f;TCP 工作在哪一层&#xff1f; IP网络层是不可靠的&#xff0c;TCP工作在传输层&#xff0c;保证数据传输的可靠性。 TCP全称为 “传输控制协议&#xff08;Transmission Control Protocol”&#xff09;。 TCP 是面向连接的、可靠…

KDB+Q | D1 | 学习资源 基础数据类型

官网会是主要的学习资源&#xff1a;https://code.kx.com/q/ 中文教程可能读起来会快一点&#xff1a; https://kdbcn.gitee.io/ 参考了还不错的学习经验帖&#xff1a;https://www.jianshu.com/p/488764d42627 KDB擅长处理时序数据&#xff0c; KDB数据库是后端数据库&…

MySQL数据库存储引擎MyISAM与InnoDB

前言 MySQL存储引擎是MySQL数据库中负责管理数据存储和检索的组件&#xff0c;不同的存储引擎提供了不同的功能和特性&#xff0c;可以根据实际需求选择合适的存储引擎来优化数据库性能和功能。以下是一些常见的MySQL存储引擎&#xff1a;InnoDB、MyISAM、MEMORY、NDB Cluster…