标签归档:sphinx

sphinx的检索字段配置限制

问题现象
前天很有自信的上传了sphinx配置,结果重建索引出现”No fields in schema”的错误。第一反应是自己姿势不对,仔细检查了相关配置,没发现任何问题。纠结了半天,注释掉新加的sql_attr_string C,然后重建索引成功了,初感不解,然后生效C,注释掉sql_attr_string B,重建也成功了,越觉莫名其妙。

解决办法】
各方途径调查原因如下:sphinx对于sql数据源需要至少一个sql_field_string,当没有指定sql_field_string时会将数据源中没有明确指定为sql_attr_string的字符串字段用来做sql_field_string。因为数据源中总共只有ABC三个字符串数据,这就是为什么全部字符串字段设为sql_attr_string时不能重建索引,而随便注释一条sql_attr_string,重建索引都能成功的原因。

sphinx返回匹配的字段

1. 问题产生的背景
sphinx有一个评分模式
SPH_RANK_FIELDMASK可以在结果中带回所匹配的字段的32位掩码。例如有5个字段ABCDE是sql_field_string,比如一条记录只命中A字段,那么@weight的值就是1,如果5个字段都命中,那么@weight的值就是32。但是我们通常都习惯用SPH_RANK_PROXIMITY_BM25或者SPH_RANK_SPH04等这种能够代表真实相关度的评分模式,既想用其他评分器又想明确知道当前匹配到哪几个字段的矛盾就不可避免了。

2. 目前的解决办法
去官方论坛,stackflow等地方查阅了相关讨论,目前貌似都木有完美解决方法。虽然各种相关讨论都希望这个值作为内部的一个属性直接返回,但至少目前看来,官方开发特性没有这方面的打算。现在普遍的做法是多做一次查询,第一次用自己的评分模式,第二次用SPH_RANK_FIELDMASK这种模式,然后在第一次查询中取数据,在第二次查询中取对应文档的匹配的字段掩码来综合业务逻辑。

3. 我是这么整的
虽然sphinx的多查询有很多优化,通常也不会造成性能上多大的损失,但是毕竟这种方法还是比较费事儿的。考虑到sphinx评分器支持自定义,那我们就可以在某些条件下携带这些数据。例如,假设我的需要参与检索的字段数为n个,那么field_mask的值就不会大于2^n,因此我们只需要把其他评分器的值向左移n位就能保证不会丢失数据。(以SPH_RANK_SPH04+SPH_RANK_FIELDMASK两者融合为例)

当然需要注意的是,这个值不能超过32位整型上限,需要精确的保证需要的公式值所占的位数 + 进行检索的字段数 < 32 即可(所以像这里user_weight的值选取就需要仔细斟酌)。另外这样做了之后@weight的值就是这两者的融合了,因此如果想用之前的评分的话,只需重新定义一个@weight / 2^n as f_new_weight字段,按上面例子的话就可以等价于SPH_RANK_SPH04了。

参考文献
http://sphinxsearch.com/blog/2010/08/17/how-sphinx-relevance-ranking-works/ http://sphinxsearch.com/docs/archives/manual-2.0.8.html#weighting

sphinx新特性展望

sphinx开发团队的速度着实让人害怕,最近基本都是都是2个月左右就一个正式版。目前2.0.8应该算是用得最多的一个版本,原因我想有两点:
1). 2.0.8到2.0.9间隔较长,差不多4个月,使用sphinx的估计已经妥妥的用在了自己生成环境上,再作变动成本会较高
2). 2.0.8之后的release版本新特性还没有达到不能拒绝的程度。
  昨天在群里面有人聊到了2.2.1 beta版的新特性,然后偶就再一次到官网去琢磨了下。以前的版本特性更多的体现在对实时索引的优化和对json支持的优化,2.2.1真的有几项feature让人不能拒绝。

1.  Integration with RLP for Chinese segmentation.
内建中文分词。在这之前我们只有两种选择:
1). 先用分词工具分好词再去创建索引;
2).使用国内coreseek或者sfc之类的修改版在索引时分词
第一种方法处理起来就麻烦一些,属于sphinx干不了时的变通方法,相当于把一段文本分成有固定分隔符的文本,然后索引时配置以这个分隔符来区分汉字建立索引。
第二种方法用起来木有问题,但是完全跟不上官方的节奏,神马新特性更是无从谈起。
这个特性绝对无法拒绝。

2. New data source for indexing CSV/TSV files in the same fashion as for XML files.
支持csv/tsv这些文件了。好处是神马?就是你现在可以用很简单的明文了。在这之前我们能做的不多:基本只有coreseek能用万能的python来聚合各种数据源,sphinx官方就只有xml和db,xml虽然是明文,但按这种结构组织还不是那么方便。现在你可以用linux下各种shell命令工具来聚合数据,这里又让人有了很大的想象空间。

3. GEODIST() now uses (by default) a lighter algorithm which is based on Polar Flat-Earth formula.  It’s now possible to specify the input/output unit type as well as the calculation algorithm to be used.
以前geodist这个函数是很慢的,因为他使劲的算准确,相比 Polar Flat-Earth formula这个要慢很多。之前要用到geo的计算,基本上都是把Polar Flat-Earth formula算式写到sql语句中。还有就是老的单位是弧度,所以以前算了geo真是要写很多”废话”,现在支持各种单位,一下爽多了。

相关姊妹篇: 关于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