关于sphinx – 我有话要说

原创文章,转载请注明:  转载自 下雨天

本文链接地址: 关于sphinx – 我有话要说

让子弹飞里面是唱着歌吃着火锅,我这是听着歌写着博客,细细想来,生活本该如此…
前段时间被工作搞得欲仙欲死,终于偷来一点闲暇看看自己究竟整了些啥玩意儿。
此处略去几万字儿,直接进入正题 – sphinx

1. 已经有了数据库的全文检索,为什么需要单独的全文检索引擎
a).
 两者相辅相成。全文检索是数据库的有力补充,全文检索并不能替代数据库在应用系统中的作用。当应用系统的数据以大量的文本信息为主时,采用全文检索技术可以极大的提升应用系统的价值。
b). 两者能力有差别。全文检索引擎比mysql做检索更灵活,性能更高。在数据量到一定量级之后全文检索引擎性能高过mysql很多(貌似是10w,没精确试过),另外,前者提供更的匹配模式,评分标准等检索功能。
c). 两者定位有差别。全文搜索引擎专注于搜索,指标是海量数据的查询和索引建立;数据库更加专注于数据本身,对业务所需要的高并发和高吞吐不那么关注。

2. 为什么是sphinx
a).
 在全文检索领域,入法眼的就3个,lucene,sphinx,xapian。
基于java的lucene最火,有个基于C的分支;xapian基于C++开发,有成熟的迅搜(xunsearch)支持;基于C++的sphinx,有coreseek和sphinx-for-chinese的二次修改版。语言因素加上不想走分支项目,滤掉lucene,剩下xapian和sphinx。
b). sphinx的高性能(搜索都很费时)高过xapian再加上xapian的api头文件让我凌乱,最终扑向sphinx。

3. sphinx他不是搜索引擎
貌似还有很多人以为sphinx就是搜索引擎了,但我更愿意叫他检索引擎,他的关键能力还在于关键词的匹配上即文本检索。而搜索引擎除了检索系统外还包括更多类似数据挖掘,缓存,相关性预测,去重,纠错以及更复杂的数学模型等等。当然围绕sphinx,我们是可以搭建一套搜索引擎。

4. sphinx提供的接口
a).
 传统的api模式(请求发向searchd,searchd回复结果,有原生的和第三方的)
b). sphinxQL(类mysql接口,使用标准mysql的客户端库连接,然后用sphinx扩展的关键字和能力进行检索)
c). sphinxSE(编译进mysql的插件引擎,了解不多)http://sphinxsearch.com/blog/2013/07/23/from-api-to-sphinxql-and-back-again/指出api模式由于打包了所有搜索选项等数据到二进制数据中,所以有额外的开销,且第三方api通常不会随server版本实时更新;sphinxQL性能理论上更改高,且提供一些特殊信息(负载状态,统计特性等)。但目前api方式仍被广泛应用,因为简单且最广为人知。

5. 官方指标
索引建立速度:单核10~15MB/s
搜索速度:单核700 queries/s(100W记录,1.2GB数据,3GHz)
已知应用:分布式索引有30亿,峰值查询5000W queries/day

6. 中文的全文检索(中文分词)
a).
 生在天朝要解决的东西很多,中文分词就是其中一项。sphinx本身提供中文的检索支持,但不支持正统的中文词切分,因为中文不像英文有明确的词分隔符。但提供一元分词,即将中文词一个一个的切分。
b). 分词模型常见的,正向/逆向/双向最大匹配,基于词频采集,还有高级点的歧义消除模型,机器学习模型等。不开源的中科院分词前辈咱们不敢奢望,只剩下scws和libmmseg选择,单从api简洁性上scws更胜一筹(friso也值得研究下)。在全文检索中总共有2个地方可能用到分词,建立索引时和搜索时,这里就诞生出两种模式:

  • 索引时不分词(一元切分) + 搜索时分词
    好处:搜索数据全;词库更改无需重建索引;
    坏处:索引数据较大,间接影响搜索性能,特别是在数据量本身很大的情况下。
  • 索引时分词 + 搜索时分词
    好处:索引数据小,搜索性能更高;
    坏处:需要索引阶段支持分词;词库变动需重建索引。

实际操作过程中,第二种方案可以通过预先分词以固定分隔符分割的方式预处理,然后检索引擎可按此分隔符进行正确区分;实际运用中,第二种方案由于受限于词库完整性,不可避免会丢掉某些不常见的词进而导致搜索不到。

=======================sphinx进阶=======================
7. 汇总下sphinx搜索本身的能力:过滤(索引源过滤,搜索时过滤),匹配(并,交,相似等),排序(默认排序因子和算法,自定义排序因子,各种排序因子的组合), 组合(子查询(有优化))等。过滤属于边缘属性,重点介绍匹配和评价,因为搜索结果除了数据源的因素外这两个是影响最大的。

a). 匹配模式
sphinx一路走来有过很多匹配模式,最新最被推荐也是功能最强大的是SPH_MATCH_EXTENDED。中文搜索下提供的能力包括但不限于:与,有限制的非,或,字段限制匹配,有序匹配,有限近似匹配,阀值匹配等

  • “A B” 等同于 “A & B” ,与匹配,必须同时存在A和B才会匹配,”A”即词组匹配,对于中文限定了双引号时,表示必须精确匹配到A。
  •  “B -A” ,非匹配,匹配B且不匹配A,有一个限制,就是-A必须不涉及到所有文档,这里因为匹配了B,所以不满足这个限制。
  •  “B | A”,或匹配,匹配B或A。
  •  “@(f1,f2) A”,@!(f1,f2) A”,字段限制匹配,限制只在f1和f2两个字段上匹配A,默认是sphinx配置里面的所有field_string;后者取非,限定只在除f1,f2的字段上进行匹配。
  • “A << B”,有序匹配,匹配A和B,且顺序上A必须在B的前面。
  • “A B”~3,有限近似匹配,即有一个少于5个词(不包括5)的串,包含了A和B,实际上限定了词之间的距离。
  • “A B C”/2,阀值匹配,至少匹配其中两个。

上面是最常用的,sphinx官方文档有很详尽描述,使用时需注意运算符优先级。匹配模式只保证能匹配出结果,这只是搜索的第一步,有时排序可能更加重要,因为他跟搜索质量关系更加密切。

b). 评价模型
评价模型通俗的讲就是打分标准,直接影响结果排序,排序的对象就是评价模型所产生的因子。sphinx内建评分器主要有两个因子: phrase proximity, keyword frequencies;前者是词组评分(精准匹配时获得最大分),后者是统计学评分(遵循基于词频的BM25)。这里只考虑SPH_MATCH_EXTENDED,其默认的ranker是SPH_RANK_PROXIMITY_BM25(其他的匹配模式分别对应不同的默认ranker)。
SPH_RANK_PROXIMITY_BM25 = sum(lcs*user_weight)*1000+bm25
SPH_RANK_BM25 = bm25
SPH_RANK_NONE = 1
SPH_RANK_WORDCOUNT = sum(hit_count*user_weight)
SPH_RANK_PROXIMITY = sum(lcs*user_weight)
SPH_RANK_MATCHANY = sum((word_count+(lcs-1)*max_lcs)*user_weight)
SPH_RANK_FIELDMASK = field_mask
SPH_RANK_SPH04 = sum((4*lcs+2*(min_hit_pos==1)+exact_hit)*user_weight)*1000+bm25
SPH_RANK_EXPR = ….

评价模型的灵活性来自于可以自定义(SPH_RANK_EXPR),提供了以下因子:

  • bm25 相关性评分,值越大相关性就越大
  • max_lcs 词组匹配所能获得的最大评分,代表sum(lcs*user_weight)能取到的最大值
  • field_mask 标记已匹配到的字段的掩码
  • query_word_count 查询词的个数(唯一的个数,重复的不算)
  • doc_word_count 整个文档中关键词的个数
  • lcs 最长公共子串,记录匹配到的子串个数
  • user_weight 字段的权重(用户设置的,默认都均等)
  • hit_count 在所匹配到的文档中的关键词命中个数(非唯一,包含所有字段)
  • word_count 在所匹配到的文档中的任意字段里匹配到的唯一关键词的个数(唯一,单个字段)
  • tf_idf 词频和逆文档频率乘积求和(0代表所有文档都出现,1代表唯一关键词仅出现在一个文档中)
  • min_hit_pos 第一个匹配的关键词所出现的位置
  • exact_hit 在当前匹配文档的某一个字段里是否是精确匹配
  • min_best_span_pos 目前还木有搞明白用法
  • sum 目前是按匹配字段求和的,即对每个匹配字段表达式运算后求和

评价模型直接影响内部属性的值,间接影响排序,根据这些因子可以定制自己的评分标准,然后通过sphinx内建属性@weight表现出来。

c). 排序模型

  • SPH_SORT_RELEVANCE模式 = @weight desc, @id asc. 按相关度降序排列(最好的匹配排在最前面)
  • SPH_SORT_ATTR_DESC模式 = attribute asc, @weight desc, @id asc. 按属性降序排列 (属性值越大的越是排在前面)
  • SPH_SORT_ATTR_ASC模式 = attribute desc, @weight desc, @id asc. 按属性升序排列(属性值越小的越是排在前面)
  • SPH_SORT_TIME_SEGMENTS模式, 先按时间段(最近一小时/天/周/月)降序,再按相关度降序
  • SPH_SORT_EXTENDED模式, 按一种类似SQL的方式将列组合起来,升序或降序排列。
  • SPH_SORT_EXPR模式,按某个算术表达式排序。

评价模型的因子在排序模型中抽象成了内部属性@weight(@rank,@relevance),最主流的还是SPH_SORT_EXTENDED模式,绝大部分排序场景都可以用其满足,少部分新闻类数据需要SPH_SORT_TIME_SEGMENTS模式,如果实在不能满足通过SPH_SORT_EXPR模式定制能完成效果。

  • 内建属性排序: @weight,@id,@random。例如: @weight desc。
  • 另外,还可以通过自己source中定义或者api重写select的”虚拟字段”来参与排序; 例如: f_gid desc (f_gid可能是f_id as f_gid)。
  • 当然还可以使用一些运算的组合来作为排序因子。 f_top_weight desc (f_type = 1 as f_top_weight)。
  • 用SPH_SORT_EXPR定制自己的排序模式,聚合成一个因子进行排序。

d). 数据组合

  • 一条查询命令中按顺序组合了多类数据(按类型排序即可)
  • sphinx内部通过子查询,返回类各自的数据到结果集中。
  • sphinx外部,应用层通过多次查询,来取得类各自的数据到结果集中。

第一种灵活性最差,但是最简单,逻辑单一时使用不错。
第二种灵活性适中,能够应付较为复杂的逻辑,且sphinx内部对子查询有优化,理论性能较高。
第三种最为灵活,但时耗是个严峻的考验。

e). 其他能力
sphinx除了基本的查询数据的能力之外,当然还有很多相关能力,包括但不限于:

  • geo能力,能够根据经纬度进行近似计算距离 (http://sphinxsearch.com/blog/2013/07/02/geo-distances-with-sphinx/)。
  • 分布式能力,这个是sphinx扩展上的基本能力。
  • 词形映射,可以映射词A到词B(查询A时相当于是查询B)。
  • 结果分组聚类,有提供内部属性来获取组内匹配数等能力。
  • 自定义函数(https://github.com/minisotm/sphinx-hamming-distance-plugin),目前可以在mysql类的模式中使用,自定义算法和因子时可以采取该方式。

8. 实际运用
a). 关键词搜索
这个是最基本的,就不阐述了。常用来用来搭建论坛和博客的内建检索引擎。
b). 关联搜索(http://sphinxsearch.com/blog/2013/05/21/simple-autocomplete-and-correction-suggestion/)
上面是官方博客的一篇文章,简单介绍了搜索框搜索词建议的实现。即完全匹配的相关性条目进行关联展示。体验就像百度搜索框的自动补全建议。
c). 相关词推荐
这个跟关联搜索类似,但是通常出现在相关词推荐中。简单的实现就是提取自己库的关键词作为一个数据源,然后在其中搜索
与查询词相关的词。体验就像百度搜索页底部的”相关搜索”区域。
d). 做缓存
当然理论上是不需要用sphinx来做缓存的,因为数据源通常是数据库,再不进行检索的情况下,从sphinx取数据和从db取数据性能应该说是相近的。
但db通常会有很多并发任务和操作,而sphinx是读静态文件。这里可以用来做带条件的数据读取。
e). 日志分析(http://sphinxsearch.com/blog/2013/07/31/logzilla-big-data-log-analysis-with-sphinx/)
海量日志的查询也是一件蛋疼的事情,至少从db中检索是贼慢的。

利用这个还可以做出很多有意思的玩意儿,例如词的分类建议(制定好类目,然后特定类目下扩充行业分类词库,对查询词进行搜索,然后给出匹配到的类目)等等。

9. 常见的架构
a).
 数据量少时 => 单机全量索引 多机部署
全量索引时有效控制文档数及检索文本量。单机包含所有数据,机器用来做负载均衡和容灾。
b). 数据量较大(更新频繁) => 单机全量+增量索引+定时合并 多机部署
全量+增量,有多种逻辑,例如有按文档划分的,也有按其他字段,例如更新时间划分的。需要注意的是文档相同时,增量索引中的数据会覆盖主文档数据。单机包含所有数据,机器用来做负载均衡和容灾。
c). 数据量超大 => 划分数据区+增量索引+定时合并 多机分布式部署
划分数据区,可以按文档,也可以按业务来划分。单机放部分文档或某(几)类业务数据,包含部分数据,其他数据需要从其他节点获取,机器用来做分布式数据存储及负载均衡和容灾。

注:
1). 区分主索引和增量索引是为了更快的重建索引应对数据变更。
索引的重建也面临一些问题:最朴素的处理方式就是定时重建,当然这是个很耗cpu的工作。时间间隔长了,达不到准实时的效果,时间间隔短了,cpu利用率间隔性陡增也是非常影响业务的。目前想到有几种途径可有效缓解,部分方法急需在业务中验证:

  • 在其他机器上来重建索引,然后将数据同步到运营机,(.new的新索引后缀,加上手动发出一条sighup信号),这个有效降低cpu占用,当然这里只能是增量索引,否则大量的数据复制也会很耗时太长。
  • 异步事件通知,目前有较多的分布式消息组件,数据的更新可以通过一条异步消息发出,运营机上可以阻塞式的监听消息,若真有数据更新再重建。
  • 主动式的发现,短时间间隔查询db数据是否有更新(这个很轻量级),若更新再重建。

2). 划分数据区主要是出于系统优化的考虑(文档越大查询越慢),例如单个索引超过百万级通常就进行拆分(单个索引的理论文档数是亿级别的),还有部分原因是为了便于业务区分。这类似于理论上数据分布 – “复制”和“分片”,前者用于把所有数据放置在所有节点,后者每个节点存放部分数据。
3). 如果db表并发任务较多,myisam引擎有时会出现死锁或阻塞时间过长,这里有几种解决办法:

  • sphinx重建索引时,使用步进式的拉取方法(sql_query_range,sql_range_step)。但这会让索引建立时间被延长(有时会很长)。
  • 将sphinx的数据源表独立出来供sphin专用,避免重建索引时可能被阻塞的情况。

10. 质量评价标准
搜索的质量评价主观性太强,难以评价,目前主要有三种方式:
a). 精确率/召回率
精确率 = 本次搜索结果中相关文档数 / 本次搜索结果文档总数。
召回率 = 本次搜索结果中相关文档 / 整个文档集合中所有相关文档数。
对搜索引擎和用户而言,召回率太难以估计,没人能翻完所有的相关文档,因此精确率显得更有实际意义。
b). P@n
p@n = 搜索结果中的top(n)中相关文档数 / n
该指标更关注搜索结果排名最靠前的n个文档的结果质量。
c). AP/MAP
AP/MAP = 搜索结果中相关文档的实际位置和 / 搜索结果中相关文档的理想位置和
该指标用以衡量单次或多次查询的平均准确率,理想结果是搜索结果中所有相关文档n都排在top(n),此时AP/MAP = 1。

11. sphinx的roadmap特性和未来
a).
 实时索引能力越来越强,性能也越来越好,未来可能作为主推的一种能力。
b). 富媒体的支持,新版本直接能解析json对象的元素,最近一个版本性能也有大幅提升。
作者对sphinx的愿望是把sphinx做到mysql里面去,作为mysql预置的一个引擎。

【参考文档】
官方博客和论坛是目前最好的资料源
国内最大的sphinx应用及论坛
这就是搜索引擎-核心技术详解(张俊林 著)
Sphinx之Coreseek、Sphinx-for-chinaese、Sphinx+Scws 评测
Sphinx + PHP + scws构建MySQL准实时分布式全文检索
Sphinx多线程分布式搜索
基于Sphinx构建准实时更新的分布式通用搜索引擎平台
基于Sphinx+MySQL的千万级数据全文检索(搜索引擎)架构设计
亿级数据的高并发通用搜索引擎架构设计
很好的专业博客
sphinx的各种第三方api,各种插件和管理工具
sphinx charset table


原创文章,转载请注明: 转载自下雨天
本文链接地址: 关于sphinx – 我有话要说


你可能感兴趣的:(Web开发-搜索篇,web开发,搜索,sphinx)