问题1
两个分别有60亿字节(Byte)的文件存储了空格隔开的IP地址,现提供4核CPU 以及 12G 内存机器,目标是找出两个文件共有的IP地址并存储到文件C中。
解答
1 容量规划:
60亿字节 = (60 * 10 ^ 8) / 1024 / 1024 = 5.5GB
2 暴力法:
读取其中A文件到内存中(Set去重存储),循环分片读取B文件,如在A 中存在则将其加入到C文件中,并从缓存中删除避免重复加入C中。
多线程加速:(如果对响应速度要求很高的情况下才使用,否则只会引入不必要的技术复杂度)
对B 文件 按照线程数目进行分片, 写A缓存和C文件时会存在多线程安全问题,考虑 1 对C 文件进行二次去重操作 考虑2 C 文件按照线程分片成多个小文件,最终去重并合并成一个C文件
科普布隆过滤器
海量数据优先想到布隆过滤器。但是布隆过滤器能解决 1 哪些key是肯定不存在的 2 判定存在时具有一定概率性(如hash冲突,极其优秀的hash函数也无法保证无hash冲突问题 出现hash冲突后布隆过滤器判断这个key可能存在 具体是否存在需要二次校验)。 综上所述,布隆过滤器适用于处理Redis缓存穿透问题,如被恶意拿一个不存在的id调接口查数据 导致把MySQL查崩掉。可以用布隆过滤器处理上述问题。但在本文题中并不适用。
问题2
问题1中每个文件增长10倍怎么处理?
解答1
1 容量规划:
600亿字节 = (600 * 10 ^ 8) / 1024 / 1024 = 55GB
内存还是12GB 此时采用问题1中解法由于内存限制无法解决此问题。
2 分治算法
step1:遍历文件a,对每个key求取hash(key)%10,然后根据所取得的值将url分别存储到10个小文件(记为a0,a1,...,a9,每个小文件约5.5GB),为什么是10?主要根据内存大小和要分治的文件大小来计算,我们就大致可以把55G大小分为10份,每份大约5.5GB(当然,到底能不能分布尽量均匀,得看hash函数的设计)
step2:遍历文件b,采取和a相同的方式将key分别存储到10个小文件(记为b0,b1,...,b9)(为什么要这样做? 文件a的hash映射和文件b的hash映射函数要保持一致,这样的话相同的url就会保存在对应的小文件中,比如,如果a中有一个key记录data1被hash到了a9文件中,那么如果b中也有相同url,则一定被hash到了b9中)
所以现在问题转换成了:找出10对小文件中每一对相同的key(不对应的小文件不可能有相同的key)
step3:因为每个小文件大约5.5GB,所以我们再可以采用 解法1中的解法处理后续问题。
解答2
在解法1 的基础上
如果ip满足规律性 如192.168.xxx.xxx 那么可以在缓存时使用字典树数据结构可以节省掉n * 8个字符空间
但是每次查询是否存在的时间复杂度会由 Set的O(1) 增长到 字段树的 O(logN)
空间和时间的权衡 依据业务场景以及硬件条件进行权衡