类似的问题:
20 亿个 URL,限制 1G 内存,如何去重?
10 亿个订单号,限制 1G 内存,如何去重?
对于 Java 来说,可以使用 int 类型表示 QQ 号(Java 并未设计无符号整型,只有几个无符号整型的静态方法)。
40 亿个 QQ 号如果直接存储的话,大约需要内存:4*4000000000/1024/1024/1024 约等于 15G。
实际开发过程中,所需的内存肯定会更多。
很显然,这种方式是不现实的。
对于这种大数据量去重的场景,我们可以考虑使用位图(Bitmap)。位图可以在不占用太多内存的前提下,解决海量数据的存在性问题,进而实现去重。
什么是 Bitmap? Bitmap 是一种用于存储二进制数据的数据结构。简单来说,Bitmap 就是使用二进制位来表示某个元素是否存在的数组。每一位只有两种状态,可以方便地用 0 和 1 来表示存在与不存在。
Bitmap 的常见应用场景如下:
- 去重:如果需要对一个大的数据集进行去重操作,可以使用 Bloom Filter 来记录每个元素是否出现过。比如爬给定网址的时候对已经爬取过的 URL 去重、对巨量的 QQ 号/单号去重。
- 数据统计:Bitmap 可以用来记录某些特定事件发生的情况,例如某个用户是否登录、某个用户是否点赞过某个视频等。
- 布隆过滤器:布隆过滤器是一种基于 Bitmap 的数据结构,主要用于判断一个元素是否存在于一个大的集合中。相遇 Bitmap,占用的空间更少,但其结果不一定是完全准确的。
我们知道 QQ 号是 4 字节无符号整数,共 32bit,也就是说,QQ 号的取值范围是 [0,2^32 - 1]。 2^32 - 1 的值是 4294967295,是一个 10 位的整数,大约是 43 亿。
这样的话,大约需要 512MB 内存就可以表示所有的 QQ 号了,计算过程:4294967295 / 8/ 1024 / 1024 约等于 512MB.
假设我们要把 QQ 号 1384593330 放入 Bitmap,我们只需要将 1384593330 位置的数组元素设置为 1 即可。当我们要判断对应的 QQ 号是否已经存在于 Bitmap 中时,只需要查看对应位置的数组元素是否为 1 即可。
> SETBIT mykey 12312311113 1
0
> GETBIT mykey 12312311113 1
1
> SETBIT mykey 123123133313 1
0
> GETBIT mykey 123123133313 1
1如果我们想要进一步节省空间,并且容许较小的误差的话,还可以使用布隆过滤器(Bloom Filter)进一步优化。布隆过滤器就是基于 Bitmap 实现的,只是多加了哈希函数映射这一步。