[文章作者:张宴 本文版本:v1.0 最后修改:2010.02.05 转载请注明原文链接:http://blog.zyan.cc/sphinx_search/]

  前言:

  2008年7月,我写过一篇文章《基于Sphinx+MySQL的千万级数据全文检索(搜索引擎)架构设计》。有不少网友希望阅读全文,我将该文档整理了一下,分享出来。文档解压后大小为7.33M,共19页。

  本站下载地址: http://blog.zyan.cc/book/sphinx/sphinx_mysql.zip

  新浪下载分流: http://ishare.iask.sina.com.cn/f/6728201.html

  上述文档架构存在的局限,我在2008年12月的文章《亿级数据的高并发通用搜索引擎架构设计》中已经指出:一是MySQL本身的并发能力有限,在200~300个并发连接下,查询和更新就比较慢了;二是由于MySQL表的主键与Sphinx索引的ID一一对应,从而无法跨多表建立整站查询,而且新增加类别还得修改配置文件,比较麻烦;三是因为和MySQL集成,无法发挥出Sphinx的优势。虽然如此,但对于一些写入量不大的搜索应用,已经足够了,或许对很多人会有帮助。



  正文:

  在这之后,本人基于《亿级数据的高并发通用搜索引擎架构设计》开发的Sphinx分布式通用站内搜索引擎平台,已经在生产环境运行9个月以上,经过运营中的不断完善与改进,目前已形成了一套可扩展的分布式通用站内搜索引擎框架。CMS、视频、论坛等产品发生的增、删、改操作,文本内容实时写入自行开发的 HTTPSQS 高性能简单消息队列服务,通过队列控制器更新索引和存储。提供支持XML、JSON的API查询接口,支持亿级数据的索引、分布式、中文分词、高亮显示、自动摘要、准实时(1分钟内)增量索引更新。

  点击在新窗口中浏览此图片

  下面是Sphinx新的搜索架构中技术关键点实现方式的一些介绍,与大家分享、交流一下:

  1、一元分词和中文分词的结合:

  ①、一元分词位于索引更新模块。Sphinx索引引擎对于CJK(中日韩)语言(必须是UTF-8编码)支持一元切分,假设【反恐行动是国产主视角射击网络游戏】这段文字,Sphinx会将其切成【反 恐 行 动 是 国 产 主 视 角 射 击 网 络 游 戏】,然后对每个字建立反向索引。如果用这句话中包含的字组成一个不存在的词语,例如【恐动】,也会被搜索到,所以搜索时,需要加引号,例如搜索【"反恐行动"】,就能完全匹配连在一起的四个字,不连续的【"恐动"】就不会被搜索到。但是,这样还有一个问题,搜索【"反恐行动游戏"】或【"国产网络游戏"】就会搜索不到。对于这个问题,采用位于搜索查询模块的中文分词来处理。

  sphinx.conf配置文件中关于UTF-8中文一元分词的配置如下:
...省略...
index t_source_main
{
        source                  = t_source_main
        path                    = /data0/search/sphinx/data/t_source_main
        docinfo                 = extern
        mlock                   = 0
        morphology              = none
        min_word_len            = 1
        charset_type            = utf-8
        min_prefix_len          = 0
        html_strip              = 1
        charset_table           = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
        ngram_len               = 1
        ngram_chars             = U+3000..U+2FA1F
}
...省略...


  ②、中文分词位于搜索查询模块。搜索“反恐行动游戏”、“国产网络游戏”,先调用独立的中文分词系统,分别切分为“反恐行动 游戏”、“国产 网络游戏”,这时候,再给以空格分隔的词语加上引号,去Sphinx搜索【"反恐行动" "游戏"】或【"国产" "网络游戏"】,就能搜索到这条记录了。中文分词词库发生增、删、改,无需重建整个Sphinx搜索索引。



  2、使用自行开发的HTTPSQS(http://code.google.com/p/httpsqs)开源简单队列服务程序,来缓冲高并发数据写入

  新闻、论坛帖子、客服公告、SNS社区等发生的增、删、改操作,文本内容通过更新接口实时写入HTTPSQS队列,再通过队列控制器更新到Sphinx搜索引擎索引中。



  3、Sphinx不能严格按照字段排序的小问题

  如果不想使用权重,只希望严格按照时间、主键等排序,而匹配模式(Matching modes)又为非SPH_MATCH_BOOLEAN时(比较常用的是SPH_MATCH_ALL、SPH_MATCH_EXTENDED),Sphinx搜索结果在某一页中的排序会不太准确。例如:按照UNIX时间戳倒序排序,0,20为第一页,20,40为第二页,第一页的最小时间戳一定会大于第二页的最大时间戳,但是,第一页中的0,20条记录却不会严格按照时间戳排序,第二页亦是如此。因此,如果需要精确排序,用户翻到搜索结果的某一页,就需要对Sphinx在某一搜索结果页中的记录另行再排序,在我的这套搜索架构中,这一再排序操作由search.php查询接口使用array_multisort()函数处理。一般情况下,一页只会显示5~30条记录,因此,只对几十条记录采用PHP再排序,速度也是非常快的。



  4、队列控制器中“时间控制”与“数量控制”相结合,实现搜索索引的1分钟内准实时更新:

  ①、Sphinx 0.9.9生产环境的建索引速度大约在5.5 Mbytes/秒、6400文档/秒。队列控制器可以设置10秒钟更新一次增量索引,只要Sphinx增量索引数据源的文档数在38万以内,就能保证增量索引在1~60秒内得到更新,这是从“时间”上进行控制。

  ②、为了避免增量索引数据源的文档数增长到38万,队列控制器在增量索引数据源的文档数超过1万时,还将激活增量索引合并入主索引的操作,合并完成的文档将从增量索引数据源中删除,这是从“数量”上进行控制。



  5、自行编写的“搜索引擎查询API接口”调用说明:
http://xxx.xxx.xxx.xxx/search.php?query=%E9%87%91%E5%B1%B1  (搜索关键字。程序能够识别关键字是GBK编码还是UTF-8编码,能够识别关键字是否进行了URL编码)
&output=xml  (输出类型支持:xml 或 json)
&excerpts=1  (是否开启高亮显示与文本摘要,1开启 或 0关闭)
&excerpts_before=<font color=red>  (高亮显示与文本摘要,如果为空值则不进行高亮显示与文本摘要。在匹配的关键字前面插入的字符串。)
&excerpts_after=</font>  (高亮显示与文本摘要,如果为空值则不进行高亮显示与文本摘要。在匹配的关键字之后插入的字符串。)
&excerpts_limit=256  (高亮显示与文本摘要,如果为空值则不进行高亮显示与文本摘要。摘要最多包含的符号(码点)数。)
&excerpts_field=c1,c2,c3,c4,c5  (仅对指定的字段进行高亮显示,其余字段不进行高亮显示,如果此参数为空,则默认所有的字符型字段都进行高亮显示)
&offset=0&limit=20  (相当于SQL语句中的limit 0,20)
&max_matches=30000  (最大搜索结果集数量)
&match_mode=SPH_MATCH_EXTENDED2
&ranking_mode=SPH_RANK_PROXIMITY_BM25
&sort_mode=SPH_SORT_EXTENDED&sort_by=@relevance DESC,u1 ASC,@id DESC  (排序模式:@relevance和@id是内置变量,@relevance代表相关度权值,@id等于search_id,u1为字段名)
&field_weights=c1,7;c2,1  (权重设置:字段c1的权重为7,字段c2的权重为1)
&filter=u1:0_1_6,false;u2:4,true  (整数值过滤器:匹配字段u1等于0、1或6,并且字段u2不等于4的结果集。false表示等于,true表示不等于)
&filter_range=u1:0,100,false;u2:50,90,true  (整数范围过滤器:字段u1 >= 0并且u1 <= 100,字段u2 < 50并且u2 > 90)
&filter_range=u1:1.23,99.645,false;u2:1034.3,7834.56,true  (浮点数范围过滤器:字段u1 >= 1.23并且u1 <= 99.645,字段u2 < 1034.3并且u2 > 7834.56)




  6、搜索结果前台页面示例:

  点击在新窗口中浏览此图片



  7、同一套服务器平台与API接口,通用于各类产品:

  示例:

  金山游戏文章与视频搜索:http://s.xoyo.com/result.php?w=%E5%89%91%E7%BD%913

  金山游戏论坛帖子搜索:http://jx3.bbs.xoyo.com/search2.php?srchtxt=%E4%B8%83%E7%A7%80&select=title


Tags: , ,



技术大类 » 搜索引擎技术 | 评论(99) | 引用(0) | 阅读(171465)
Tangboke Homepage
2010-2-8 15:16
好的文章是用心写的,不错。
luoke
2010-2-8 16:21
张宴大师,你好
按照你的文档,我搭建起了sphinx全文搜索环境,也可以正常中文分词搜索了。目前,没有像你做的那么复杂,就是把sphinx全文检索引擎编译到mysql,用最简单的查询语句调用。我只索引的帖子标题。
但是,现在有一个问题,比如我搜索“大学英语第三版” ,按中文分词,应该被分成“大学 英语 第 三 版”。 我期望的是只要帖子标题包含“大学英语”的数据都应该搜索出来,配匹配的结果越靠前。但是发现,只有标题中包含所有分词的帖子才会搜索出来,像标题为“新视野大学英语” 这样帖子就没有搜出来。这个标题虽不完全匹配,但至少也应该被搜素出来,但是发现没有搜到。
这个问题,应该如何解决呢,这是我的sphinx.conf的索引配置:
引用


index test1
{
        source                                  = src1
        path                                    = /usr/local/webserver/sphinx/var/data/test1
        docinfo                                 = extern
        mlock                   = 0
        morphology              = none
        min_word_len            = 1
        charset_type            = utf-8
        min_prefix_len          = 0
#       min_infix_len           = 2
        html_strip              = 1
        charset_table           = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
        ngram_len               = 1
        ngram_chars             = U+3000..U+2FA1F
        charset_dictpath = /usr/local/webserver/sphinx/dict
}
tyler
2010-2-8 18:16
经过我的测试,使用分词和一元切分,查询速度会有10x的差距。我用的是sphinx-for-chinese,测试数据是100W条记录,每条记录数据量大约100汉字-600汉字之间。

引用
using config file '../etc/test.conf'...
indexing index 'test1'...
collected 1000000 docs, 725.1 MB
sorted 117.4 Mhits, 100.0% done
total 1000000 docs, 725127941 bytes
total 204.159 sec, 3551773 bytes/sec, 4898.13 docs/sec
total 53 reads, 17.905 sec, 9865.5 kb/call avg, 337.8 msec/call avg
total 1255 writes, 12.588 sec, 1019.0 kb/call avg, 10.0 msec/call avg



引用
using config file '../etc/test.conf'...
indexing index 'test1'...
collected 1000000 docs, 725.1 MB
sorted 229.2 Mhits, 100.0% done
total 1000000 docs, 725127941 bytes
total 232.872 sec, 3113839 bytes/sec, 4294.19 docs/sec
total 137 reads, 27.771 sec, 5775.9 kb/call avg, 202.7 msec/call avg
total 1884 writes, 13.917 sec, 1016.2 kb/call avg, 7.3 msec/call avg



117.4M的hits和229.2M的hits差距不小,估计就是这个造成了每次查询的时间差别。我是用脚本分别循环100次连续查询,然后取平均值,结果是使用一元切分的时间是使用分词的10倍左右。

另外索引大小一个是740M,一个是1.1G。

请你也测试一下,如果确实是这样,也请你在blog中注明吧。
张宴 回复于 2010-2-9 11:25
你说得不错。一元分词的单字hits比中文分词高,所以一元分词搜索时合并的数据集比中文分词要多7~10倍,也就导致查询时一元分词比中文分词更耗时间。改天我给Sphinx写个二元切分补丁,原理上应该会降低hits数量,从而提升速度。
nicoljiang
2010-2-8 22:16
1、1元切词是不明智的,如果担心内容无法进行最大限度的匹配,可以用多分策略来解决;
2、剑侠情缘网络版叁的论坛实际内容不知道多少,但是从全文搜索一个“的”字得出的120多万结果来看,整个索引应该只有150万数据(+-10万)左右。对这样级别的系统容量来说,搜索一次就需要0.5秒甚至1秒以上的话,效率是不是有点太低了。这个结果跟号称“单机亿级”的负载相比感觉差太多了。就我用lucene的经验看来,200万以内的数据应该是飞快的才对,而且Sphinx还啥都不存储。

我们曾经有一次做1元划分来做测试。我们的方式是,在索引的时候用1元单字索引,在搜索的时候,用最多切分法分词,并且逐个做短语搜索,例如:搜索“通用搜索引擎平台”,我们的处理结果是“"通用"OR"搜索引擎"OR"搜索"OR"引擎"OR"平台"”并以此来搜索,性能确实大打折扣,在系统容量达到了200万的时候,效率下降就比较严重~随便搜索4字以内的关键词,耗时都需要0.3 - 0.6秒以上了。达到500万的时候,搜索单字都需要0.4秒以上,4字以内的词基本上都要0.7 - 1.2秒以上。比分词索引的方式几乎是下降了一个数量级,不过翻页的话只要不是很长(8个以上)的关键词,基本上都在0.3以内。

但是如果用多分方式分词索引,要保持相同的这个数量大约可以增加到1500万 - 2000万左右,如果用普通的最大分词,近4000万的数据,都要比500万的1元索引效率要高一点。

我们的系统是基于lucene二次开发的,几乎所有内容都存到索引里直接读取,所以索引的容量相对是非常大的。

我们的机器配置也比较低:是7200转的单硬盘,普通双核CPU,2G内存的PC架构。
nicoljiang
2010-2-8 22:26
刚才又用了一下,发现这套系统还很“不聪明”:
不仅没有搜索优化,而且连缓存都没有。

例如:我首先查“纯阳之巅”这个关键词,剑侠情缘的论坛搜索用时“0.7秒到0.9秒不等”,刷新和翻页差不多都是这个耗时。
而我们的系统则是这样,第一次搜索“纯阳之巅”耗时0.4秒,再次刷新耗时0秒,说明被缓存,翻页的速度为0.1X - 0.2X左右。

当我再查“纯阳真人”的时候,剑侠情缘的论坛搜索用时“0.9秒”刷新跟翻页仍然差不多,在0.7 - 0.9之间,几乎没有丝毫优化。
而我们的系统查询过“纯阳之巅”之后,再查“纯阳真人”耗时仅仅0.2秒多一点~翻页仍然是0.1 - 0.2之间,刷新耗时0秒。

因为有搜索优化在里面~
peter.zyliu
2010-2-12 22:45
Sphinx是不错。如何多个索引同时进行查询?  关键是怎么分出是那个索引的数据?做综合查询
世界风
2010-2-21 21:07
强烈支持!zanzanzan
krystle Homepage
2010-2-26 23:55
还不错的,支持下楼主的
Mingle Email Homepage
2010-3-4 13:15
你用什么画的图?真不错!
YZZ
2010-3-9 11:07
张宴老师,请问我在输入框中输入双引号搜索"恐动“。但是搜索出来的单词还是分开的。不是一个词。用的index是你写的。怎么解决?
soibhan
2010-3-10 15:57
我测试,也发现“一元分词是比较慢”,一般性能的dell服务器,基本上每次查询的词简单些的差不多0.06秒左右,复杂些的,例如可拆分成三个词或者以上的,那更是慢到要0.5秒了。
      不过我查询的时候也比较复杂点,首先查询整段匹配,如果不够一页然后再分词查询。之后sphinx搜索加亮也有点小问题(需要自行解决)。
ioscas
2010-3-11 17:57
张宴大虾,我根据您的帖子正在试图搭建sphinx环境,但是有2个patch没办法下载下来,不知您可否发到我的邮箱里?(应该不会很大吧?呵呵)。

所缺patch文件: sphinx-0.98rc2.zhcn-support.patch  和 fix-crash-in-excerpts.patch
email: ioscas@gmail.com

非常感谢!
yanyongshan Email
2010-3-14 00:54
你好,根据你原来的文章,我在服务器上架设了sphinx,使用的是Coreseek 全文检索服务器版本,用的他的Libmmseg分词方法,对数据库中的一个表info(id,tag),对tag建立索引,表记录有50万,可是索引速度非常慢,十分钟都没有能够索引完成,请问这是什么原因??
搜索爱好者
2010-3-16 10:30
主流搜索都不怎么使用数据库吧,利用数据库索引去做搜索是简单快捷的方式 但不是最好的方式
sphinx
2010-4-8 20:41
你好,我搭建的是sphinx-for-chinese,我在建立索引的时候总是提示"OUT OF MEMORY",增量索引的时候也会有这个问题.请问这是什么原因?
zzx
2010-4-28 10:59
引用
②、中文分词位于搜索查询模块。搜索“反恐行动游戏”、“国产网络游戏”,先调用独立的中文分词系统,分别切分为“反恐行动 游戏”、“国产 网络游戏”,这时候,再给以空格分隔的词语加上引号,去Sphinx搜索【"反恐行动" "游戏"】或【"国产" "网络游戏"】,就能搜索到这条记录了。中文分词词库发生增、删、改,无需重建整个Sphinx搜索索引。


我怎么在sphinx中达不到这个效果,加引号跟不加引号是一样的... question
zfd
2010-4-30 15:19
分词并不是那么慢,估计是你用得不太对,前面也有人说了sphinx-for-chinese上有性能测试结果,我用了自家的分词测试结果也在这个数量级,当然我没有用mysql,自己写了一个source省掉了读数据库的时间来单独测试索引时间,看起来更快一点。    collected 1000000 docs, 3416.2 MB    sorted 58.4 Mhits, 100.0% done    total 1000000 docs, 3416165000 bytes    total 450.895 sec, 7576400 bytes/sec, 2217.80 docs/sec    total 1 reads, 0.285 sec, 198727.0 kb/call avg, 285.9 msec/call avg    total 472 writes, 0.776 sec, 1016.0 kb/call avg, 1.6 msec/call avg虽然有很多理由不分词,但也有很多理由分词,至少各家搜索引擎都是分词的有空介绍一下增量索引merge的事情,非常感谢
zfd
2010-4-30 18:07
另外二元切分和一元切分得到的hit数显然是基本一样的
dengxi Homepage
2010-5-11 11:00
怎么样区分是那条索引的数据.这个好办.
//索引文件名集合
                string[] arr_Index = IndexName.Split(',');
sc.AddQuery(Key, arr_Index[i].ToString());
//搜索结果集合
                SphinxResult[] result = sc.RunQueries();

下面循环result 就可以得出是那条索引上的结果了
dengxi Homepage
2010-5-11 11:04
张老师,我想请教一下有办法解决WINDOWS下面的高并发查询的方法没有
分页: 2/5 第一页 上页 1 2 3 4 5 下页 最后页
发表评论
表情
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
emotemotemotemotemot
打开HTML
打开UBB
打开表情
隐藏
记住我
昵称   密码   游客无需密码
网址   电邮   [注册]