py-instantse:一个问答网站的实时搜索功能后台实现
在网上搜了一下实时搜索的架构,大概看了几个,比较类似,主要参考的是王鹏云的这个PPT,这个主要是相对于搜索引擎来实施的,和我们的搜索有一点差别,因此在这个基础上进行了简化。当然也有比较超前的,比如使用redis这样的key/value型的内存数据库来进行搜索,速度很快,redis在我们的网站中也有使用用,但是评估了一下我们的硬件条件以及运维方面的因素,决定还是采用传统的在硬盘上索引的方式来实现。
那么接下来就是架构的简化,和工具的选择了。仔细观察王鹏云PPT上的架构图,可以发现这个架构主要可以分为三部分:
- Push部分:对于搜索引擎来说Push又包括两个子模块,一个是传统的Spider,另一个就是Push API。Spider就没什么说的了,这个Push API主要就是对于那些信息产生非常快的网站或者应用,例如Twitter,可以将他们的数据主动发给搜索引擎,使之能够很快被索引,如果用spider去爬,会比较慢,资源的开销也比较大。对于我们的网站来说,这个地方只需要有一个Push API就可以了,用户新产生的问题等可以直接发到Push API,然后快速建立索引。
- Index部分:在这里王鹏云的PPT里是使用了两个内存索引交替索引Push API发过来的数据,达到一定量之后与磁盘上的主索引合并,内存索引的主要作用其实是缓冲一下Push API发过来的大量数据,避免持续频繁的磁盘索引操作,减小I/O的开销。对于我们来说,在一定时间段内数据量还没有这么大的情况下,直接将Push API发来的数据进行硬盘索引是可以接受的,当然还需要考虑到后期存在升级的可能性,因此,对于这一部分,我们的计划是,最开始的时候可以只有一个主索引库,Index部分将接受到的数据直接写在主索引库中,查询端也从主索引库查询,就是类似一个生产者,多个消费者的模型。当数据量增大一些之后,可以进行主从分离,Index部分还是往主索引库写,读的的时候是从从库里读,然后主从库之间进行同步。当数据量再往上涨的时候,压力其实主要是在写索引的部分了,这时候可以建立内存索引库来缓冲,这时候需要处理好的就是合并和同步的时间间隔,这时候已经是伪实时的了,不过对于问答类的网站,这个也是可以接受的。
- Query部分:这一部分主要就是接受客户端Http请求然后返回查询结果的部分,主要需要考虑的就是并发性和相应速度。可以采用Comet长连接的方式,也可以采用传统的轮询的模式。在这里我们结合自身的情况选择了后者。
OK,大体的结构上就是这样了,然后是工具的选择,开发语言用Python就没得选了,对于第一部分的Push API,我们使用python自身的SocketServer模块进行开发,将传来的数据写到Python Multiprocessing模块的Queue中,然后Index这边就负责读,然后开始索引。在索引工具的选择上,主要看了纯Python的Whoosh,Sphinx还有Xapian,Java写的Luence没有考虑。其中Whoosh开发比较简单,不过考虑到性能还有对中文的支持上,没有选择它,Sphinx使用的比较多,Quroa,者也都曾经用过它,不过看了看文档,它主要支持的是从数据库中查询然后建立索引,和我们这样脱离SQL数据库的Push API的方式不太相符,因此,最终选择了使用C++开发的Xapian,性能强劲,支持内存索引,也支持索引库的一写多读,而且对分布式的索引数据库支持也比较好,在建立索引时需要进行的中文分词,我们使用python的第三方模块mmseg。最后Query部分,就使用了最近常用的Gevent中的WSGI server,后面也很统一改。下面给出我们自己的实时搜索模块的架构图:
注意:图中是最终完整的设计,对于我们这样刚上线数据量不大的网站,可以适当简化,例如Xapian部分可以先只用一个主索引库,读写都在主索引库上,Query Server部分也可以只用一个,不用做负载均衡。这个图是说明,我们考虑了不同阶段系统的架构方案,保证该方案的可用性。
该模块已经开发完成,代码稍后开源,若干实施上的细节问题后面也会一并附文说明。如果对此感兴趣或者有好的建议,请联系作者Troycheng(frostmourn716@gmail.com)。
代码已经开源,地址:http://code.google.com/p/py-instantse/
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。