Redis之Bitmap
Bitmap是什么:
Redis 的位图(bitmap)是由多个二进制位组成的数组,只有两种状态,0和1, 数组中的每个二进制位都有与之对应的偏移量(从 0 开始),通过这些偏移量可以对位图中指定的一个或多个二进制位进行操作。
Bitmap能解决什么问题:
Bitmap是用一个bit 位来存放某种状态,适用于大规模数据,但是前提是统计的这个大数据量每个的状态只能有两种,因为每一个bit位只能表示两种状态,在大量数据的快速排序、查找、去重bitmap很有优势。
Bitmap带来了什么问题:
1.数据碰撞。比如将字符串映射到 BitMap 的时候会有碰撞的问题,那就可以考虑用 Bloom Filter 来解决,Bloom Filter 使用多个 Hash 函数来减少冲突的概率。
2.数据稀疏。又比如要存入(10,8887983,93452134)这三个数据,我们需要建立一个 99999999 长度的 BitMap ,但是实际上只存了3个数据,这时候就有很大的空间浪费,碰到这种问题的话,可以通过引入 Roaring BitMap 来解决。
Bitmap原理:
BitMap 的基本原理就是用一个 bit 来标记某个元素对应的 Value,而 Key 即是该元素。由于采用一 个bit 来存储一个数据,因此可以大大的节省空间。
计算机分配给内存的最小单元是bit,1Byte=8bit, 1个整数类型为4Byte=32bit。
Bitmap场景:快速查找 去重 排序 签到 打卡
在20亿个随机整数中找出某个数m是否存在其中,并假设32位操作系统,4G内存。
计算机分配给内存的最小单元是bit,在Java中,int占4字节,1字节=8位(1 byte = 8 bit)。
如果每个数字用int存储,那就是20亿个int,因而占用的空间约为 (2000000000*4/1024/1024/1024)≈7.45G
如果按位存储就不一样了,20亿个数就是20亿位,占用空间约为 (2000000000/8/1024/1024/1024)≈0.23G
表示{1,2,4,6}这几个数,每一位表示一个数,0表示不存在,1表示存在,这正符合二进制:
这样我们可以很容易表示{1,2,4,6}这几个数:
那如果要表示{12,13,15}怎么办呢?当然是在另一个8位上表示了:
变成一个二维数组了,1个int占32位,那么我们只需要申请一个int数组长度为 int tmp[1+N/32] 即可存储,其中N表示要存储的这些数中的最大值,于是乎:
tmp[0]:可以表示0~31
tmp[1]:可以表示32~63
tmp[2]:可以表示64~95
总结:
给定任意整数M,那么M/32就得到下标,M%32就知道它在此下标的哪个位置。
添加
这里有个问题,我们怎么把一个数放进去呢?例如,想把5这个数字放进去,怎么做呢?
首先,5/32=0,5%32=5,也是说它应该在tmp[0]的第5个位置,那我们只需要把第5位数字设置位1即可,删除同理将第五位数字设置位0接口。
字符串映射
BitMap 也可以用来表述字符串类型的数据,但是需要有一层Hash映射,如下图,通过一层映射关系,可以表述字符串是否存在。
当然这种方式会有数据碰撞的问题,但可以通过 Bloom Filter 做一些优化。
BitMap如何使用:
网上很多使用教程。
问题:
去重:
https://www.cnblogs.com/chenfx/p/15710156.html
签到:
https://blog.csdn.net/qq_31905135/article/details/124032880
用户量统计:
https://blog.csdn.net/qq_37200262/article/details/122051753
总结
- bigmap 基于最小的单位bit进行存储,最大优势是非常省空间;
- 设置时候时间复杂度O(1)、读取时候时间复杂度O(n),操作是非常快的;
- 二进制数据的存储,进行相关计算的时候非常快,也能方便扩容;
- 不要给一个很短的 bigmap 设置很长位的偏移量的值,这样有可能堵塞。