1 概述
倒排索引是搜索引擎中不可或缺的数据结构,利用倒排索引可以快速搜索到包涵搜索关键词的一系列文章。倒排索引的常见结构如下图:
在倒排索引中,每个term与一系列的postings相关联,每个postings由文章的id以及payload组成,而payload常见的是该词在该文章中的词频,有的也加上了位置信息。
2 算法机理
使用MapReduce进行倒排索引的构造是目前大多数大数据公司所采用的方式,因为其可以很好地处理大规模的数据。最基本的倒排索引MapReduce算法如下:
在该算法中,mapper端的输入是doc id以及doc content,每篇文章都倍分配给一个mapper,在mapper端先对文章进行分词,统计词频等,然后组成postings,最后将每个词以及其对应的postings发送出去; reducer端接收一个term,以及其对应的一系列postings,将每一个postings加入一个列表中,然后对该列表按照doc id排序,最后发送term和其对应的postings列表。 处理流程可见下面的示意图:
然而,你估计也已经发现了,上述的算法存在一个很明显的可扩展性瓶颈,就是在reducer端它假设有足够大的内存来储存每一个term对应的一系列postings,因为后续还要对这些postings进行排序。因而,当postings变得很大时,reducer端将会耗尽内存。
一个很简单的解决方案是:我们改变mapper端发送出的key的格式,将key的格式变为
3 程序实现
从上部分可以看出,使用MapReduce实现倒排索引的构建原理其实并不是很复杂,然而在实现中还是有一些问题需要注意的。
- 从算法中我们可以看到,mapper的输入是key是doc id,value为doc的内容,那么如何保证在分片时能恰好按每篇文章分成各个分片传给mapper?
- 中文文章的分词如何处理?
第二个问题比较容易,我们可以使用第三方分词包来进行文章的分词处理,我这里使用的是ansj中文分词。
第一个问题比较复杂一点,我们需要重定义一个FileInputFormat,使用该FileInputFormat mapper可以把整个文件当作一条记录处理。下面代码实现了一个WholeFileInputFormat。
package mp.invertedIndexing;
import java.io.IOException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.ap