Q1:不到1秒的时间怎么在网上检索到那么多的东东? 1
Q2:什么是倒排索引? 2
Q3:像mp3、image这种非文本对象怎么建立倒排索引? 2
Q4:为什么要进行切词?怎么进行切词? 2
Q5:检索系统是怎么实现Q1中所说的检索过程的? 2
Q6:前端检索服务程序之间是怎么分工合作的? 3
Q7:as、bs、di的检索架构有什么好处? 3
Q8:建索引模块如何获取上游模块数据? 5
Q9:大规模的数据如何高效的建立倒排索引? 5
Q10:对实时更新的数据如何快速的建立倒排索引? 5
Q11:如何为标题检索、站点搜索这样一些特殊的检索考虑倒排索引? 5
Q12:bs如何高效的处理索引拉链? 6
Q13:相关性需要考虑哪些因素? 6
Q14:实时信息的检索是如何实现的? 6
Q15:什么是offset索引,有什么用? 6
Q16:有哪些用于提高性能的索引技术? 7
Q17:对于可修改的信息如何建立实时索引? 7
Q1:不到1秒的时间怎么在网上检索到那么多的东东?
A:检索所需的信息,需要完成收集信息、分析信息和查询信息三个工作,我们的检索系统也不例外。
收集信息:通常通过spider(或crawler)根据网络上的各种链接遍历Web空间采集网页资料。不同的应用,信息的采集方式也不尽相同。
分析信息:通常需要完成三件事情:(1)解析网页(见网页解析FAQ);(2)相关性因素的分析或计算;(3)建立倒排索引。
查询信息:根据关键词,通过倒排索引查找与关键词匹配的数据集,按照相关性将最终结果有序输出即可。
收集信息的全部工作和分析信息的绝大部分工作已经被检索系统预先做好了,我们平时的检索主要进行的是最后一步工作,速度自然很快。这是目前百度所采用的搜索引擎模型,也被人称为全文搜索引擎(FullText Search Engine)。
Q2:什么是倒排索引?
A:顾名思义,倒排就是将对象到描述的这种“正排”关系“倒”过来成为描述到对象的“倒排”关系,这样利用这种关系我们就可以通过描述信息检索到对象了,倒排索引就记录了这种关系。例如,网页1、网页2的描述信息如下:
网页1:中国人民的大学
网页2:中国人民大学
对描述信息进行切词:
网页1:中国人民的大学
网页2:中国人民大学
可以得到描述信息到两个网页对象的倒排关系:
中国à1,2
人民à1,2
的à1
大学à1,2
倒排关系的记录方式,即倒排索引,随应用环境的不同而各异。一般在建立倒排关系的过程中,为了方便动态追加,在内存中采用链式结构;存储到硬盘上时,为了高效读取,采用顺序存储。习惯上把倒排索引数据称之为倒排拉链或索引拉链。
Q3:像mp3、image这种非文本对象怎么建立倒排索引?
A:目前ns的产品线检索系统都是基于文本的,还没有基于内容的系统。Mp3、image这种非文本对象,在网页解析时,需要根据html页面的tag等解析与对象相关的文本(歌曲名、图像周围文字等),解析出文本之后,建立倒排索引的过程与文本对象一致。
Q4:为什么要进行切词?怎么进行切词?
A:中文词汇之间没有天然的分隔符,如果按照单字建立倒排索引,将会带来两方面的问题:(1)单字的平均索引拉链过长,每次查询需要进行合并的拉链数过多;(2)tf-idf、人名书名识别等与相关性有关的因素难以考虑。目前ns都是调用nlp组提供的切词接口。需要注意的是,后端建倒排索引时的切词要求保证召回率(recall),前端查询时的切词要求保证准确率(precise),因此需要对切词结果按不同策略进行处理或者调用不同的切词接口。通常把切词接口给出的一个基本单元叫做一个term。
Q5:检索系统是怎么实现Q1中所说的检索过程的?
A:目前检索系统架构上大同小异,大体上分两大类程序来实现检索:
后端建库程序:完成收集信息工作和分析信息的绝大部分工作。根据不同的应用,有的直接从大搜索网页库中取数据(mp3、image),有的需要从web站点抓取数据(news、mp3),有的直接获取用户提交的数据(tieba、iknown、space)。得到数据之后,建索引模块建立倒排索引,生成索引数据。
前端检索服务:由若干个程序合作,完成对用户检索串的解析、倒排索引数据的匹配查找、摘要的生成、最终结果的组装等工作。
Q6:前端检索服务程序之间是怎么分工合作的?
A:图1中各前端检索服务程序的功能如下:
apache:web服务器,接受用户检索请求,返回检索结果。
ui:User Interface,根据检索结果生成页面。
as:Advance Search,从ui接收检索请求,将请求分发到一个或多个bs,并对bs返回的结果进行合并除重等操作,根据bs(们)的结果向di(们)获取di信息,最后将整个检索的最终结果返回给ui。
bs:Basic Search,解析as的检索请求,得到对应的term以及他们之间的逻辑关系,读取各term的索引拉链并根据他们之间的逻辑关系得到满足检索请求的索引集合,计算结果集合中每个元素的相关性权值,按权值对最终结果排序并返回给as。
di:Display Information,接受as的请求,返回给as一些检索结果中需要展示的信息,比如摘要、标题、url等。
根据上述各个服务程序的功能,很清楚一个典型检索的流程为:用户检索请求à apacheà uià asà bsà asà dià asà uià apacheà检索结果。
关于apache和ui请参考相关的FAQ。
Q7:as、bs、di的检索架构有什么好处?
A:这种架构有如下一些优点:
方便数据扩容。采用分层(组)的方式,将数据分层(组),每层(组)数据有相应的bs和di。一旦系统的数据容量达到上限,新增数据层(组),即可实现扩容。
索引数据和di数据相分离。Di数据相对索引数据比较大,在该架构下只有前端需要展现的数据才去di服务获取相应数据,避免检索过程中读入过多无用数据。
便于服务合理配置优化。相比而言,bs和di需要从硬盘读取数据,比较耗io资源;as需要居中调度,比较耗cpu和内存资源。可以利用这些特点合理的在不同配置的服务器上分布服务。另外,可以针对具体产品线的特点,对as、bs和di采用不同的cache策略来优化系统性能。
Q8:建索引模块如何获取上游模块数据?
A:从获取数据的主动性角度,分为两种:
主动获取。建索引模块启动时,主动以上游模块的输出做为自己的输入。数据量较大,时时性要求不高时多采用这种方式。
被动获取。被动接受上游模块(transfer)分发过来的数据。
Q9:大规模的数据如何高效的建立倒排索引?
A:要做到这一点,需要考虑两个方面的因素:
分层次完成建索引的过程。首先,以一定数目的对象为一组建索引,保证对每组对象建索引的过程能够在内存中完成而不需要存取中间数据到硬盘;然后,将各组结果进行合并,得到最终的倒排索引数据。
注意内存的使用和硬盘数据的存取。尽量申请足够的大内存块,然后在整个进程周期内重复使用。为了方便对各组索引合并,每组数据输出到硬盘时,将term表排序,索引拉链进行顺序存储。合并时,尽量利用辅助结构,避免大块内存的频繁复制。读取索引拉链时,按块读取。
Q10:对实时更新的数据如何快速的建立倒排索引?
A:对该问题,目前ns通用内存索引机制进行解决。该方案的核心思想就是,处理时时数据的建索引模块只负责少量数据,保证建索引过程能够在内存中完成而不需要到硬盘存取中间数据,这样就能快速的处理时时数据,并且根据时效性要求,将内存中的索引数据定期存到硬盘,以供相应的bs模块查询。一但数据量超过内存空间限制,则将相应数据合并到历史数据中并清空占用的内存。具体可参看实时数据处理FAQ
Q11:如何为标题检索、站点搜索这样一些特殊的检索考虑倒排索引?
A:把标题中的term、站点域名等这些特殊的词按照特殊的格式建到索引中,在检索的时候还按照这种特殊格式去检索,就能够完成检索。例如标题检索,某篇文章标题中有“百度”,建立倒排时,对来源于标题的term“百度”增加前缀“T_”,即对term“T_百度”建立倒排索引,检索时,如果查询标题中含有“百度”的文章,检索程序(一般是bs)会根据要求自动将检索term修改为“T_百度”,如此则可查到想要的结果。
但是这种解决方案取决于索引拉链的长度。比如想按图片类型进行检索,则term“jpg”的索引拉链会超长,即不适合通过在term前加前缀的方法来实现。类似这种情况可以将具体信息记录在brief表或直接记录在索引中。
Q12:bs如何高效的处理索引拉链?
A:在ns组的实践中,有一些经验能够保证这一点:
先读取索引拉链最短term的拉链,因为最终归并结果肯定短于该拉链长度。如果最短的拉链过长,可以考虑截断(比如只读前10万个)。
利用A∩B∩C=(A∩B)∩C,将多路归并转化为多个二路归并,避免过多的读取无用数据。
读取索引拉链时按块读取,每归并完一块再读取下一块。一方面节省内存,另一方面避免读入过多的数据。
合理选择排序算法。比如权值空间在百量级时采用基数排序;过大的话采用堆排序。
Q13:相关性需要考虑哪些因素?
A:只能具体应用具体分析,针对ns的产品,可以考虑如下一些角度:
长文本:tf-idf、offset、文本质量等;
短文本:不同类型给予不同权重、检索串与匹配文本的基本词数比例、不同term的切词属性、覆盖关系等
query特点:query中心词提取、term权重标识等
具体产品的特点:mp3的下载速度、image的图片质量、news的实效性等
Q14:实时信息的检索是如何实现的?
A:对于动态数据类检索(比如tieba,zhidao),用户实时提交的信息需要能够及时的检索到,这通常是通过使用两种类型的索引库来实现的:
实时库。也称day库。该库会以较高的频率进行重建(通常5分钟~半小时),使得新提交的信息能够及时的合并入该索引,并提供检索。为了保证实时库重建的效率,必须控制库的size,因此每天夜里将实时库合并入历史库并清空。
历史库。也称mon库。该库维护了历史以来的所有数据(除了当天的实时数据),每天夜里合并实时库时重建一遍。某些服务(如tieba)数据量巨大,甚至连每天夜里重建一次的负荷都无法承受,则会分出一些独立静止的库(也称lmon库),这些库不再参与合并和重建。
Q15:什么是offset索引,有什么用?
A:offset索引就是在索引单元中记录了term在文本中出现位置信息的索引。某些term会大量在文本中出现,所以通常会控制记录的位置数(比如只记录出现的前16个位置),以控制索引数据的大小。
offset索引在长文本检索系统(比如zhidao)中对于调权的意义很重大,通过offset信息可以发现匹配“最好”的结果。比如query为“计算数学”,分为两个term:“计算”和“数学”,对于通常的索引,没有term在文本中的位置信息,因此不能知道这两个term在该文本中是否连在一起。通过offset索引则可以算出来。
Q16:有哪些用于提高性能的索引技术?
A:当一个检索系统的数据规模变大以后,利用普通索引来检索会导致性能下降。主要是由于索引拉链数据过长,导致IO负荷过大。为了提高检索效率,通常可以有如下的一些索引组织方式:
位图索引。也称bitmap索引。该索引主要针对DF(文档频率)非常高的term,比如“的”(如果它不是一个停用词的话)。它用一个位来表示是否在某个文档中出现,因此,对于总文档数为N的索引库,每个term对应的索引大小为N/8。
压缩索引。压缩索引通常针对于索引较长的term,它将其中次要的一些信息剔除,仅保留文档ID。这些信息可能包括文档相关的、用于过滤或类聚的属性(比如文档签名),这些信息通常可以存放在另外一个单一的结构中,减少信息重复。offset信息也可以被剔除。
差分索引。普通索引中,文档ID使用u_int表示。对于索引比较长的term,其连续两个文档ID之间的差通常比较小,因此可以采用记录差值来代替记录ID,并且使用特定的编码(比如huffman编码)使得小数字占用更少的位。以此来减少索引的大小。
截断索引。在使用offset等信息的索引中,索引会比较大,而位图、压缩索引中又丢失了这些信息,因此考虑考虑一个折中,保留一定长度的记录了这些信息的索引。保留的原则是选择当前可决定的权值较高的文档(比如TF较大)。这种索引称为截断索引。
预索引。对于一些静态数据的检索系统,可以将检索高频词预先检索一遍,得到结果并存储为新的索引文件,称为预索引。查询时如果命中,可以直接读取预索引的数据。这种索引从本质上将相当于基于硬盘的cache。
Q17:对于可修改的信息如何建立实时索引?
A:在某些类型的动态数据检索系统中,需要提供某个文档可修改的功能。如果对实时性要求很低,可以采用定期重建索引库的方法。但如果实时性要求较高,就必须采用其他方式。处理上的难点在于如何及时将文档中原来存在,修改以后不存在的term的对应的索引作废,使得检索该term不再能够取到该文档。
通常可以采用的方法有:
记录修改次数。一个全局的位图结构记录每个文档的当前修改次数,每次修改该次数增1。这样只需要对修改后内容的term的索引拉链进行追加,并在索引中记录当前修改次数,原来的term不需要动。检索的时候在获取索引拉链时比较修改索引中记录的修改次数是否与全局结构的修改次数相同,不同则是“过期”的索引,不再使用。这些过期的索引可以在合并的索引库的时候丢弃。
大小ID编号方法。小ID表示全局ID,大ID表示检索系统内部ID。一个全局的位图结构记录大ID当前是否有效。每次文档修改时,都会为文档重编一个大ID(小ID不变),并且将该文档修改之前对应的大ID在位图中置为无效,并且和修改次数方法一样,只追加到新term,旧term不动。检索时,读取term的索引,在全局位图中判断是否有效。过期的索引也可以在合并索引库时丢弃。注意:这里小ID并没有起什么作用,它只是和外部系统的一个接口,放到DI中即可。