hash 映射

为了便于计算机在有限的内存中处理大数据,我们通过一种映射散列的方式让数据均匀分布在对应的内存位置(如大数据通过取余的方式映射成小数据存放在内存中, 或大文件映射成多个小文件),而这种映射散列的方式便是我们通常所说的 hash 函数,好的 hash 函数能让数据均匀分布而减少冲突。

对于海量数据而言,由于无法一次性装进内存处理,不得不把海量的数据通过 hash 映射的方法分割成相应的小块数据,然后再针对各个小块数据通过 hashMap 进行统计或其他操作。

1. 寻找 TOP IP

问题

海量日志数据,提取出某日访问百度次数最多的那个 IP。

思路

如果想一次性把所有 IP 数据装进内存处理,则内存容量通常不够,故针对数据太大,内存受限的情况,可以把大文件转化成(取模映射)小文件,从而大而化小,逐个处理。即,先映射,而后统计,最后排序

步骤

  • 先映射。
    首先将该日访问百度的所有 IP 从访问日志中提取出来,然后逐个写入到一个大文件中,接着采取 hash 映射的方法(比如 hash(IP)%1000),把整个大文件的数据映射到 1000 个小文件中。

  • 统计。
    当大文件转化成了小文件,那么我们便可以采用 hashMap(ip,cnt) 来分别对 1000 个小文件中的 IP 进行频率统计,找出每个小文件中出现频率最大的 IP,总共 1000 个 IP。

  • 堆排序/快速排序。
    统计出 1000 个频率最大的 IP 后,依据它们各自频率的大小进行排序,找出那个出现频率最大的 IP,即为所求。

2. 寻找热门查询

问题

搜索引擎会通过日志文件把用户每次检索所使用的所有查询串都记录下来, 每个查询串的长度为 1-255 字节。假设目前有 1000 万个查询记录(但因为这些查询串的重复度比较高,所以虽然总数是 1000 万,但如果除去重复后,不超过 300 万个查询字符串),请统计其中最热门的 10 个查询串,要求使用的 内存不能超过 1G。

一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。

思路

针对本题数据规模是比较小的,能一次性装入内存。虽然有 1000 万条查询记录,但是去除重复后,不超过 300 万条查询记录。占用的最大内存为 300 0000 * 255 B = 0.75 GB,可以将字符串都放入内存进行处理。

步骤

  • 统计。
    使用 hashMap 进行频率统计。时间复杂度为 O(n)

  • 堆排序。
    借助堆这个数据结构,找出 Top K,维护一个 K 大小的小根堆,然后遍历 300 万条查询记录,分别和根元素进行比较。时间复杂度为 N’O(log K)。

所以,最终的时间复杂度是:O(n)+N’O(log K),其中,N 为 1000 万,N’ 为 300 万,K 为 10。

3. 寻找频数最高的 100 个词

问题

有一个 1G 大小的文件,里面每一行是一个词,词的大小不超过 16 字节,内存限制大小是 1M。返回频数最高的 100 个词。

思路

内存大小只有 1 M,而文件却有 1 GB,所有必须将该大文件切分为多个小于 1 M 的小文件。

步骤

  • 哈希映射。
    按先后顺序读取文件,对于每个词 x,执行 hash(x)%5000,然后将该值存到 5000 个小文件(记为 x0, x1, …, x4999)中。如此每个文件的大小大概是 200k 左右。当然,如果其中有的小文件超过了 1M 大小,则可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过 1M

  • 统计。
    对每个小文件中出现的词进行频率统计。

  • 堆排序 / 归并排序。
    取出每个文件中出现频率最大的 100 个词(可以用含 100 个结点的小根堆)后,再把 100 个词及相应的频率存入文件,这样又得到了 5000 个文件。最后把这 5000 个文件进行归并(可以用归并排序)。

4. 寻找共同的 URL

问题

给定 a、b 两个文件,各存放 50 亿个 url,每个 url 各占 64 字节,内存限制是 4G,请找出 a、b 文件共同的 url。

思路

可以估计每个文件的大小为 5G * 64=320G,远远大于内存限制的 4G。所以不可能将其完全加载到内存中处理。

步骤

  • 哈希映射。
    遍历文件 a,对每个 url 求取 ,然后根据所取得的值将 url 分别 存储到 1000 个小文件(记为 a1 a2 a3 a4)中。这样每个小文件的大约为 300M。
    遍历文件 b,采取和 a 相同的方式将 url 分别存储到 1000 小文件中(记为 b1 b2 b3 b4)。
    这样处理后,所有可能相同的 url 都在对应的小文件url。然后我们只要求出 1000 对小文件中相同的 url 即可。

  • 统计。
    求每对小文件中相同的 url 时,可以把其中一个小文件的 url 存储到 hashSet 中。然 后遍历另一个小文件的每个 url,看其是否在刚才构建的 hashSet 中,如果是,那么就是共同的 url,存到 文件里面就可以了。