问题:有10亿个url,每个url对应一个非常大的网页,如何检测网页是否重复。
解答:
网页大,数量多,要把它们载入内存是不现实的。 因此我们需要一个更简短的方式来表示这些网页。而hash表正是干这事的。 我们将网页内容做哈希,而不是url,这里不同url可能对应相同的网页内容。
将每个网页转换为一个哈希值后,我们就得到了10亿个哈希值, 很明显,两两对比也是非常耗时的O(n2 )。因此我们需要使用其它高效的方法。
根据以上分析,我们可以推出满足条件的以下算法:
遍历网页,并计算每个网页的哈希值。
检查哈希值是否已经在哈希表中,如果是,说明其网页内容是重复的,输出其url。 否则保存url,并将哈希值插入哈希表。
通过这种方法我们可以得到一组url,其对应的网页内容都是唯一的。但有一个问题, 一台计算机可以完成以上任务吗?
一个网页我们要花费多少存储空间?
每个网页转换成一个4字节的哈希值
假设一个url平均是30个字符,那我们至少需要30个字节
因此对应一个url,我们一共要用掉34个字节
34字节 * 10亿 = 31.6GB。很明显,单机的内存是无法搞定的。
我们要如何解决这个问题?
我们可以将这些数据分成多个文件放在磁盘中,分次载入内存处理。 这样一来我们要解决的就是文件的载入/载出问题。
我们可以通过哈希的方式将数据保存在不同文件,这样一来,大小就不是问题了, 但存取时间就成了问题。硬盘上的哈希表随机读写要耗费较多的时间, 主要花费在寻道及旋转延迟上。关于这个问题, 可以使用电梯调度算法来消除磁头在磁道间的随机移动,以此减少消耗的时间。
我们可以使用多台机器来处理这些数据。这样一来,我们要面对的就是网络延迟。 假如我们有n台机器。
首先,我们对网页做哈希,得到一个哈希值v
v%n 决定这个网页的哈希值会存放在哪台机器
v/n 决定这个哈希值存放在该机器上哈希表的位置
问题:有A,B连个文件,分别存放50亿url,找出A,B中相同的url。
解答:
可以估计每个文件的大小为5G*64=300G (50亿是5000000000,即5G),不可能全部加载到内存中的,因此可以采用分而治之的方法。
- 遍历文件a,对每个url求取hash(url)%1000,然后根据所得值将url分别存储到1000个小文件(设为a0,a1,…a999)当中。这样每个小文件的大小约为300M。遍历文件b,采取和a相同的方法将url分别存储到1000个小文件(b0,b1….b999)中。
- 所有可能相同的url都在对应的小文件(a0 vs b0, a1 vs b1….a999 vs b999)当中,不对应的小文件(比如a0 vs b99)不可能有相同的url。然后我们只要求出1000对小文件中相同的url即可。
- 对于a0 vs b0,我们可以遍历a0,将其中的url存储到hash_map当中。然后遍历b0,如果url在hash_map中,则说明此url在a和b中同时存在,保存到文件中即可。
注: 如果分成的小文件不均匀,导致有些小文件太大(比如大于2G),可以考虑将这些太大的小文件再按类似的方法分成小小文件即可。