sphinx分布式索引简介

sphinx分布式索引原理:
当searchd收到一个对分布式索引的查询时,它做如下操作:
1. 连接到远程代理
2. 执行查询
3. (在远程代理执行搜索的同时)对本地索引进行查询
4. 接收来自远程代理的搜索结果
5. 将所有结果合并,删除重复项
6. 将合并后的结果返回给客户端
在应用程序看来,普通索引和分布式索引完全没有区别。
任一个searchd实例可以同时做为主控端(master,对搜索结果做聚合)和从属端(只做本地
搜索)。这有如下几点好处:
1. 集群中的每台机器都可以做为主控端来搜索整个集群,搜索请求可以在主控端之间获
得负载平衡,相当于实现了一种HA(high availability,高可用性),可以应对某个
节点失效的情况。
2. 如果在单台多CPU或多核机器上使用,一个做为代理对本机进行搜索的searchd实例
就可以利用到全部的CPU或者核。
更好的HA支持已在计划之中,到时将允许指定哪些代理之间互相备份、有效性检查、跟踪
运行中的代理、对检索请求进行负载均衡,等等。
sphinx分布式索引配置:
index dist1
{
    # local index to be searched
    # there can be many local indexes configured
    local               = test1
    local               = test1stemmed
    # remote agent
    # multiple remote agents may be specified
    # syntax for TCP connections is 'hostname:port:index1,[index2[,...]]'
    # syntax for local UNIX connections is '/path/to/socket:index1,[index2[,...]]'
    agent               = localhost:9313:remote1
    agent               = localhost:9314:remote2,remote3
    # agent             = /var/run/searchd.sock:remote4
    # blackhole remote agent, for debugging/testing
    # network errors and search results will be ignored
    # agent_blackhole       = testbox:9312:testindex1,testindex2
    # remote agent connection timeout, milliseconds
    # optional, default is 1000 ms, ie. 1 sec
    agent_connect_timeout   = 1000
    # remote agent query timeout, milliseconds
    # optional, default is 3000 ms, ie. 3 sec
    agent_query_timeout     = 3000
}
原理和配置还是比较简单的,对于索引文件过大的情况,分布式索引非常有用。但是碰到搜索量非常大的时候,分布式索引就显得力不从心了。这个时候就需要做索引复制了。索引复制同步的问题一般采用的方案有两种。
第一种方案是主从同步。有一个主搜索服务器和多个从搜索服务器。在主搜索服务器上生成的增量索引会同步从索引服务器。从而达到主从同步。为了避免网络或者其他情况导致的增量索引不同步的情况,需要定期在一个阶段把主从服务器的主索引同步一次。新增加搜索服务器的话需要在增量索引生成的间隔数据去拷贝其他搜索服务器的主索引。
第二种方案是每个搜索服务器根据增量数据去生成增量索引。通过表来记录不同的搜索服务器是否生成增量索引。来达到同步的目的。新增加搜索服务器的话,就需要去比历史上所有的增量数据全部生成一遍。
这两种策略都是可行的。增量数据在一定时间段量很大的话建议用第一种方案,否则用第二种方案。

[教程]freebsd8.0下安装coreseek

、安装coreseek

A、安装环境配置,为安装coreseek做准备

#pkg_add -r autoconf262 automake110 libtool mysql50-client libxml2 expat
B、下载整个安装包(内含mmseg,coreseek):
#fetch http://www.coreseek.cn/uploads/csft/3.2/coreseek-3.2.13.tar.gz
#tar xzvf coreseek-3.2.13.tar.gz
#cd coreseek-3.2.13
======================================
C 安装coreseek开发的mmseg,为coreseek提供中文分词功能
#cd mmseg-3.2.13
#./bootstrap
#./configure --prefix=/usr/local/mmseg3
#make
#make install
至此,mmseg已经安装完成,下面进入csft-3.2.13目录里进行安装coreseek Continue reading

sphinx在windows下无法启动的解决办法

配置完成了sphinx,也安装成为系统服务,但在dos提示符下服务的时候错误

searchd –-install -–config d:/csft3.1/bin/xxxx.conf

相应的删除服务命令为:

searchd –delete

sphinex-services

这里有两种办法:
1.直接把配置文件复制到c:/windows/system32目录里一份就可以了.
2.在安装服务的时候指定配置文件的物理路径(--config d:/csft3.1/bin/csft.conf)

索引或者查询时提示:ERROR: invalid token in 配置文件 line 1 col 1.

该提示表示当前的配置文件的编码不是UTF-8(无BOM头)格式,无法正确解析,请使用编辑软件打开配置文件,另存为UTF-8(无BOM头)格式;

错误的编码格式包括:Unicode、Unicode BOM、Unicode big endian、Unicode 低位在前、UTF-8 + BOM、UTF-8 Signature、UTF-8 包含签名等;

特别注意:Windows自带的记事本(Notepad)或者写字板(WordPad)无法正确保存为所需格式,请勿使用其编辑配置文件;

推荐编辑器:点击下载Notepad2绿色版;使用Notepad2打开配置文件,依次选择:“文件”菜单--“编码”--“UTF-8”,然后保存文件(快捷键CTRL+S)即可。

[教程]coreseek sphinx在FreeBSD 7.0安装教程

感谢为中文全文检索做出贡献的所有同学。

1、源码安装LibMMSeg 。 先在这里下载压缩包
# fetch  http://www.coreseek.com/opensource/mmseg/
# tar zxvf mmseg-0.7.3.tar.gz
# cd mmseg-0.7.3

# vim src/css/SegmentPkg.cpp 修改第27行, 将 #include <malloc.h> 改为 #include <stdlib.h>
# ./configure && make && make install

2、测试 mmseg
# cd mmseg-0.7.3/data 你会看到一个准备好的UTF-8编码的字典文件 unigram.txt
# mmseg -u unigram.txt 该命令执行后,将会产生一个名为unigram.txt.uni的文件,将该文件改名为uni.lib,完成词典的构造。 你也可以进行分词测试。详见 http://www.coreseek.com/opensource/mmseg/

3、ports安装 gawk
# cd /usr/ports/lang/gawk
# make install clean

4、ports安装 python25
#cd /usr/ports/lang/python25
#make install clean

5、设置环境变量
# cd -
# vim .profile

增加两行
export CPPFLAGS=-I/usr/local/include/python2.5
export LDFLAGS=-lpython2.5

6、源码安装 Coreseek Fulltext Search Server 2.5.2。 先在这里下载压缩包 #fetch http://www.coreseek.com/uploads/sources/coreseek_fulltext_2.5.2.tar.gz
# tar zxvf coreseek_fulltext_2.5.2.tar.gz
# cd coreseek_fulltext_2.5.source
# ./configure && make && make install

7、配置服务
安装程序会默认创建 /usr/local/var/data 和 /usr/local/var/log 这里就是为了让你存放索引文件 和 运行日志的地方。
我们也把字典文件放在/usr/local/var下 。
#cp -r dict /usr/local/var/dict #这里把第一步生成的词典data目录内容复制到dict目录里

安装程序会在 /usr/local/etc/下放一个默认配置文件 Sphinx.conf.dist 。但是 Coreseek 默认要去读 csft.conf,所以要复制一份
# cp /usr/local/etc/sphinx.conf.dist /usr/local/etc/csft.conf

修改 csft.conf 讲索引文件位置 和 日志文件位置 分别指向/usr/local/var/data 和 /usr/local/var/log
如需中文索引的配置节中加入
charset_type = zh_cn.utf-8
charset_dictpath = /usr/local/var/dict

其他配置项请参考手册.

8、导入测试数据
# mysql < source  /usr/local/etc/example.sql

9、测试建立索引
#indexer --all

应该会看到如下输出
Coreseek Full Text Server 2.1
Copyright (c) 2006-2008 coreseek.com
using config file '/usr/local/etc/csft.conf'...
indexing index 'test1'...
collected 5 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 5 docs, 230 bytes
total 0.146 sec, 1577.50 bytes/sec, 34.29 docs/sec
indexing index 'test1stemmed'...
collected 5 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 5 docs, 230 bytes
total 0.011 sec, 21879.74 bytes/sec, 475.65 docs/sec

10、测试全文检索
# search doc

应该会看到如下输出
search doc
Coreseek Full Text Server 2.1
Copyright (c) 2006-2008 coreseek.com
using config file '/usr/local/etc/csft.conf'...
index 'test1': query 'doc ': returned 2 matches of 2 total in 0.091 sec

displaying matches:
1. document=3, weight=1, group_id=2, date_added=Fri Jul 4 10:22:13 2008
id=3
group_id=2
date_added=2008-07-04 10:22:13
title=another doc
content=this is another group
2. document=4, weight=1, group_id=2, date_added=Fri Jul 4 10:22:13 2008
id=4
group_id=2
date_added=2008-07-04 10:22:13
title=doc number four
content=this is to test groups

words:
1. 'doc': 2 documents, 2 hits

index 'test1stemmed': query 'doc ': returned 2 matches of 2 total in 0.000 sec

displaying matches:
1. document=3, weight=1, group_id=2, date_added=Fri Jul 4 10:22:13 2008
id=3
group_id=2
date_added=2008-07-04 10:22:13
title=another doc
content=this is another group
2. document=4, weight=1, group_id=2, date_added=Fri Jul 4 10:22:13 2008
id=4
group_id=2
date_added=2008-07-04 10:22:13
title=doc number four
content=this is to test groups

words:
1. 'doc': 2 documents, 2 hits

11 、启动searchd
# searchd

12 、测试searchd
# cd api
# php test.php doc

应该会输出
Query 'doc ' retrieved 2 of 2 matches in 0.073 sec.
Query stats:
'doc' found 4 times in 4 documents

Matches:
1. doc_id=3, weight=100, group_id=2, date_added=2008-07-04 10:22:13
2. doc_id=4, weight=100, group_id=2, date_added=2008-07-04 10:22:13

12、关于中文全文检索
有些同学安装成功后 不能检索中文, 请检查你数据库的 variables 是否为utf8 ,请仔细反复检查一下。
可以参考这个帖子 http://www.coreseek.com/forum/index.php?action=vthread&forum=2&topic=11

注意:以上操作可能部分设置需要在csft.conf文件里做一些修改,如数据库用户名和密码,还有索引文件存放的目录,及词典存放位置等信息,请根据提示信息修改即可.

在windows生产环境搭建sphinx的注意事项

1、以服务的方式运行sphinx

在开发环境中,只要执行”rake ultrasphinx:daemon:start“,就可以启动一台sphinx服务器。但如果在生产环境还能这么做么?把sphinx安装为服务无疑是个靠谱的办法,这样它可以像mongrel、apache一样随系统启动。sphinx自带了安装为windows服务的命令:

searchd –-install -–config xxxx.conf

相应的删除服务命令为:

searchd --delete

不妨把这个加入到rake命令中,于是我hack了一下ultrasphinx插件的任务列表,加入了一个”rake ultrasphinx:daemon:install“命令。名为ultrasphinx.rake的文件我将稍后提供。
如果在启用服务的时候提示"发生系统错误1067"的话,则需要在安装服务的时候指定配置文件的路径,参考:sphinx在windows下无法启动的解决办法
如: d:\csft3.1\bin>searchd --install --config d:\csft3.1\bin\www.conf

Continue reading

Sphinx增量索引实例

在实际应用中往往有这么一种情况,数据库数据很大,比如我们的歌曲表,如果我们每次都去更新整个表的索引,对系统得开销将非常大,显然这是不合适,这时我 们会发现,每天我们需要更新的数据相比较而言较少,在这种情况下我们就需要使用“主索引+增量索引”的模式来实现实时更新的功能。

这个模式实现的基本原理是设置两个数据源和两个索引,为那些基本不更新的数据建立主索引,而对于那些新增的数据建立增量索引。主索引的更新频率我们 可以设置的长一些(可以设置在每天的午夜进行更新),而增量索引的更新频率,我们可以将时间设置的很短(几分钟左右),这样在用户搜索的时候,我们可以同 时查询这两个索引的数据。

下面,我们通过一个简单的例子来描述一下怎样实现这种模式

以sphinx.conf中默认的数据为例:

1.先在mysql中插入一个计数表和两个索引表

  1. CREATETABLEsph_counter(
  2. counter_idINTEGERPRIMARYKEYNOTNULL,
  3. max_doc_idINTEGERNOTNULL
  4. );

//主索引使用(确认之前是否已经建立过该表,如果已经建立,这里就不需要重新建了)

  1. CREATETABLE`sphinx`(
  2. `id`int(11)NOTNULL,
  3. `weight`int(11)NOTNULL,
  4. `query`varchar(255)NOTNULL,
  5. `CATALOGID`INTNOTNULL,
  6. `EDITUSERID`INTNOTNULL,
  7. `HITS`INTNULL,
  8. `ADDTIME`INTNOTNULL, KEY
  9. `Query`(`Query`)
  10. )ENGINE=SPHINXDEFAULTCHARSET=utf8CONNECTION='sphinx://localhost:3312/test1'

//增量索引使用

  1. CREATE TABLE `sphinx1` (
  2. `id` int(11) NOT NULL,
  3. `weight` int(11) NOT NULL,
  4. `query` varchar(255) NOT NULL,
  5. `CATALOGID` INT NOT NULL,
  6. `EDITUSERID` INT NOT NULL,
  7. `HITS` INT NULL,
  8. `ADDTIME` INT NOT NULL,   KEY
  9. `Query` (`Query`)
  10. )ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://localhost:3312/ test1stemmed '

2.修改sphinx.conf

  1. source src1
  2. {
  3. sql_query_pre = SET NAMES utf8
  4. sql_query_pre = SET SESSION query_cache_type=OFF
  5. sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id) FROM documents
  6. sql_query = SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content FROM documents \
  7. WHERE id<=( SELECT max_doc_id FROM sph_counter WHERE counter_id=1 )
  8. ... //其他可以默认
  9. }
  10. // 注意:sql_query_pre的个数需和src1对应,否则可能搜索不出相应结果
  11. source src1throttled : src1
  12. {
  13. sql_ranged_throttle = 100
  14. sql_query_pre = SET NAMES utf8
  15. sql_query_pre = SET SESSION query_cache_type=OFF
  16. sql_query_pre =
  17. sql_query = SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, title, content FROM documents \
  18. WHERE id>( SELECT max_doc_id FROM sph_counter WHERE counter_id=1 )
  19. }
  20. index test1 //主索引
  21. {
  22. source = src1
  23. ...
  24. }
  25. index test1stemmed : test1 //增量索引
  26. {
  27. source = src1throttled
  28. ...
  29. }

3.重建索引

  1. /usr/local/sphinx/bin/searchd --stop
  2. /usr/local/sphinx/bin/indexer --config /usr/local/sphinx/etc/sphinx.conf --all
  3. /usr/local/sphinx/bin/searchd --config /usr/local/sphinx/etc/sphinx.conf

插入测试数据

  1. INSERTINTO`test`.`documents`(
  2. `id` ,
  3. `group_id` ,
  4. `group_id2` ,
  5. `date_added` ,
  6. `title` ,
  7. `content`
  8. )
  9. VALUES(
  10. NULL , '3', '11', NOW() , '索引合并', '合 并两个已有的索引比重新对所有数据做索引更有效率,而且有时候必须这样做(例如在“ 主索引+增量索引”分区模式中应合并主索引和增量索引,而不是简单地重新索引“主索引对应的数据)。因此indexer有这个选项。合并索引一般比重新索 引快,但在大型索引上仍然不是一蹴而就。基本上,待合并的两个索引都会被读入内存一次,而合并后的内容需要写入磁盘一次。例如,合并100GB和1GB的 两个索引将导致202GB的IO操作(但很可能还是比重新索引少)'
  11. );

执行

  1. SELECTdoc . *
  2. FROMdocumentsdoc
  3. JOINsphinxON(doc.id = sphinx.id)
  4. WHEREquery = '索引'

你会发现你刚添加的数据没有被检索出来
然后执行:

  1. SELECTdoc.* FROMdocumentsdocjoinsphinx1on(doc.id=sphinx1.id)wherequery='索引'

你会发现数据是空的,这时我们就需要来更新增量索引了。

通过执行:

  1. /usr/local/sphinx/bin/indexer --rotate --config /usr/local/sphinx/etc/sphinx.conf test1stemmed

命令来更新增量索引(正式使用时,我们可以将该命令配置到系统计划任务中,每隔几分钟执行一次)
--rotate: 该参数可以使我们在不需要停止searchd的情况下,直接加载索引
执行完命令该命令后,我们再来查看一下增量索引的数据

  1. SELECTdoc.* FROMdocumentsdocjoinsphinx1on(doc.id=sphinx1.id)wherequery='索引'

你会发现新添加的数据被检索出来的。

主索引的更新:

  1. /usr/local/sphinx/bin/indexer --rotate --config /usr/local/sphinx/etc/sphinx.conf test1
  1. collected 997 docs, 1.4 MB
  2. sorted 0.3 Mhits, 100.0% done
  3. total 997 docs, 1430054 bytes
  4. total 1.428 sec, 1001459.38 bytes/sec, 698.19 docs/sec

(我们可以设置成每天的午夜执行)

只有在更新了主索引后,结果才会被更新

  1. SELECTdoc.* FROMdocumentsdocjoinsphinxon(doc.id=sphinx.id)wherequery='索引'

我们也可以通过合并索引的方式使主索引的数据保持更新

  1. /usr/local/sphinx/bin/indexer --merge test1 test1stemmed  --rotate

可以将增量索引test1stemmed合并到主索引test1中去

为创建2个shell脚本,一个用来创建主索引、一个用来创建增量索引(此步可以省略)

1.创建主索引脚本build_main_index.sh

  1. #!/bin/sh
  2. #/usr/local/sphinx/bin/searchd --stop
  3. #/usr/local/sphinx/bin/indexer test1 --config /usr/local/sphinx/etc/sphinx.conf >> /var/log/sphinx/mainindexlog(更新主索引)
  4. #/usr/local/sphinx/bin/searchd

2.创建增量索引脚本build_delta_index.sh

  1. #!/bin/sh
  2. #/usr/local/sphinx/bin/searchd --stop
  3. #/usr/local/sphinx/bin/indexer test1stemmed  --config /usr/local/sphinx/etc/sphinx.conf --rotate>> /var/log/sphinx/deltaindexlog (更新增量索引)
  4. #/usr/local/sphinx/bin/indexer --merge test1 test1stemmed --config /usr/local/sphinx/etc/sphinx.conf --rotate >> /var/log/sphinx/deltaindexlog (合并增量索引)
  5. #/usr/local/sphinx/bin/searchd

每隔5分钟进行索引增量合并,每天2:30重建索引

  1. */5 * * * * /bin/sh /opt/shell/build_delta_index.sh > /dev/null 2>&1
  2. 30 2* * * /bin/sh /opt/shell/build_main_index.sh > /dev/null 2>&1

每周一至周六上早6点增量合并,同日重建索引

  1. 1 6 * * 1-6 /bin/sh /opt/shell/build_delta_index.sh > /dev/null 2>&1
  2. 1 6 * * 7 /bin/sh /opt/shell/build_main_index.sh > /dev/null 2>&1

参考:
http://www.coreseek.com/uploads/pdf/sphinx_doc_zhcn_0.9.pdf

sphinx实现主索引+增量索引

装了几次没把sphinx集成到mysql中去(SphinxSE),只好放弃,使用其自带的api(sphinx.php)来试试。
官方的sphinx0.98不支持索引GBK的数据,后装了Coreseek提供的版本,几经测试后,终于搞定GBK的数据索引。
然后调用sphinx.php写了个搜索测试程序,终于试出来,也蛮好用的。

主要按照Coreseek整理的Sphinx0.98中文参考手册中的内容,这里记录一下。

创建主索引:

bin/indexer --config etc/sphinx.conf hx_9enjoy --rotate

创建增量索引:

bin/indexer --config etc/sphinx.conf delta --rotate

合并主索引和增量索引:

bin/indexer --config etc/sphinx.conf --merge hx_9enjoy delta --merge-dst-range deleted 0 0 --rotate

--rotate参数可以在不停searchd的情况下索引,不然的话会有类似如下的提示:
FATAL: failed to lock /usr/local/coreseek/var/data/hx_9enjoy_delta.spl: Resource temporarily unavailable, will not index. Try --rotate option.
ERROR: index 'delta' is already locked; lock: failed to lock /usr/local/coreseek/var/data/cncn_article_delta.spl: Resource temporarily unavailable

Sphinx速成指南

版权 ? 2008 Dony,<tappony(at)gmail.com>,版权所有,转载请声明来源与作者

2008-01-17

目录

1. Sphinx简介

1.1. 什么是全文检索

1.2. 介绍

1.3. Sphinx的特性

2. Sphinx安装(For MySQL)

2.1. Windows下安装

2.2. Linux下安装 3. 实例说明

4. Sphinx配置

5. 运行Sphinx

6. 搜索(翻译)

6.1. 匹配模式

6.2. 布尔查询语法(Boolean query syntax)

6.3. 扩展查询语法(Extended query syntax)

6.4. 权重(匹配度,Weight)

7. 如何调用Sphinx

8. SphinxSE的SQL查询例子演练

9. 如何自动重建索引

10. 相关资源

1. Sphinx简介

1.1. 什么是全文检索

全文检索是指以文档的全部文本信息作为检索对象的一种信息检索技术。检索的对象有可能是文章的标题,也有可能是文章的作者,也有可能是文章摘要或内容。

1.2. 介绍

Sphinx是一个基于SQL的全文检索引擎,可以结合MySQL,PostgreSQL做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。Sphinx特别为一些脚本语言设计搜索API接口,如PHP,Python,Perl,Ruby等,同时为MySQL也设计了一个存储引擎插件。

1.3. Sphinx的特性

高速索引 (在新款CPU上,近10 MB/秒);
高速搜索 (2-4G的文本量中平均查询速度不到0.1秒);
高可用性 (单CPU上最大可支持100 GB的文本,100M文档);
提供良好的相关性排名
支持分布式搜索;
提供文档摘要生成;
提供从MySQL内部的插件式存储引擎上搜索
支持布尔,短语, 和近义词查询;
支持每个文档多个全文检索域(默认最大32个);
支持每个文档多属性;
支持断词;
支持单字节编码与UTF-8编码;

支持英文,俄文词干提取和音标查询 supports English stemming, Russian stemming, and Soundex for morphology;

  支持MySQ(MyISAM和InnoDB 表都支持);
支持PostgreSQL.

2. Sphinx安装(For MySQL)
2.1. Windows下安装 从http://dev.mysql.com上下载MySQL5.0.45版安装配置好MySQL,采用utf-8字符集

从Sphinx官网上http://www.sphinxsearch.com/downloads.html下载mysql-5.0.45-sphinxse-r871-win32.zip和sphinx-0.9.8-svn-r985-win32.zip

如果您的MySQL服务已启动请先停止掉

解压mysql-5.0.45-sphinxse-r871-win32.zip,将里面bin与share目录覆盖掉你的mysql安装目录下的相应目录

解压sphinx-0.9.8-svn-r985-win32.zip ,将里面的文件解压到D:\sphinx

sphinx的配置与实际应用是相关的,因此以下我以例子进行说明,至此sphinx安装部分结束

2.2. Linux下安装 下载mysql-5.1.22-rc.tar.gz解压至/root/mysql-5.1.22

下载sphinx-0.9.8-svn-r985.tar.gz,解压至/root/sphinx-0.9.8-svn-r985

将/root/sphinx-0.9.8-svn-r985/mysqlse下的文件复制至/root/mysql-5.1.22/storage/sphinx

在/root/mysql-5.1.22目录下运行

sh BUILD/autorun.sh
./configure --prefix=/usr/local/mysql --with-charset=utf8 --with-extra-charsets=all  \
--enable-thread-safe-client --enable-assembler --with-readline --with-big-tables --with-plugins=sphinx
make && make install
groupadd mysql
useradd –g mysql mysql
chown mysql:mysql /usr/local/mysql -R
cd /usr/local/mysql
bin/mysql_install_db –user=mysql
(此时系统可能会提示:
[Warning] Storage engine 'SPHINX' has conflicting typecode. Assigning value 42.可忽略,不影响使用)
cp /root/mysql-5.1.22/support-files/mysql.server /etc/init.d/mysqld
chmod 700 /etc/init.d/mysqld
cp /root/mysql-5.1.22/support-files/my-medium.cnf /etc/my.cnf
/etc/init.d/mysqld start
(至些mysql启动了)

然后进入mysql命令行,运行show engines,看是不是有一个叫sphinx的engine,有的话就表示sphinxSE(mysql的sphinx引擎)安装正常了

进入/root/sphinx-0.9.8-svn-r985,运行

ldconfig /usr/local/mysql/lib/mysql
ldconfig /usr/local/mysql/include/mysql
./configure --prefix=/usr/local/sphinx --with-mysql=/usr/local/mysql
make && make install

3. 实例说明

为更好说明如何应用Sphinx,现结合实例说明,我们以网站的新闻文章表为例。我们想要对新闻文章表进行全文检索(主要是标题与内容),新闻文章表的相关信息如下:

CREATE TABLE `eht_articles` (
`ARTICLESID` int(11) NOT NULL auto_increment,
`TITLE` varchar(100) NOT NULL default '',
`TITLECOLOR` varchar(20) default NULL,
`AUTHOR` varchar(200) default NULL,
`COMEFROM` varchar(200) default NULL,
`KEYWORD` varchar(200) default NULL,
`HTMLURL` varchar(200) default NULL,
`CATALOGID` int(6) default NULL,
`CONTENTS` mediumtext,
`EDITUSERID` int(6) default NULL,
`ADDTIME` int(10) default NULL,
`UPDATETIME` int(10) default NULL,
`HITS` int(6) default NULL,
  PRIMARY KEY  (`ARTICLESID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

在这个表中,我主要想对标题(TITLE)与内容(CONTENTS)字段进行全文检索,在检索过程中可能我会根据文章的栏目(CATALOGID),编 辑(EDITUSERID),时间段(ADDTIME)进行条件性的全文检索,然后可能会根据主键ID(ARTICLESID),人气(HITS)进行排 序显示,如何配置Sphinx来实现呢?

4. Sphinx配置

sphinx是以sphinx.conf为配置文件,索引与搜索均以这个文件为依据进行,要进行全文检索,首先就要配置好sphinx.conf,告诉sphinx哪些字段需要进行索引,哪些字段需要在where,orderby,groupby中用到。

安装完Sphinx后,在D:/sphinx目录有一个sphinx.conf.in,这个相当于sphinx的配置例子文件,我们以这个文件为蓝本,重新创建一个空白内容的sphinx.conf,存放在d:/sphinx根目录。

sphinx.conf的内容组成

source 源名称1{
 …
}
index 索引名称1{
 source=源名称1
 …
}
source 源名称2{
 …
}
index 索引名称2{
 source = 源名称2
 …
}
indexer{
 …
}
searchd{
 …
}

提示

从组成我们可以发现sphinx可以定义多个索引与数据源,不同的索引与数据源可以应用到不同表或不同应用的全文检索。

根据前面的实例,我们配置出我们需要的sphinx.conf,如下:

source cgfinal
{
 type = mysql
 strip_html = 0
 index_html_attrs =
 sql_host = localhost
 sql_user = root
 sql_pass = admin
 sql_db = test
 sql_port= 3306 # optional, default is 3306
 sql_query_pre=  SET NAMES utf8

 sql_query = SELECT ARTICLESID,TITLE,CONTENTS,AUTHOR,CATALOGID,ADDTIME,EDITUSERID,\
HITS FROM a.eht_news_articles
 #sql_query = SELECT * FROM a.eht_news_articles
 sql_attr_uint= CATALOGID
 sql_attr_uint= EDITUSERID
 sql_attr_uint = HITS
 sql_attr_timestamp = ADDTIME

 sql_query_post  =
 sql_ranged_throttle= 0
 #sql_query_info = SELECT * FROM a.eht_news_articles WHERE ARTICLESID=$id
}
index cgfinal
{
 source   = cgfinal
 path   = d:/sphinx/data/cgfinal
 docinfo   = extern
 mlock   = 0
 morphology   = none
 stopwords   =
 min_word_len  = 1
 charset_type  = utf-8
 charset_table = U+FF10..U+FF19->0..9, 0..9, U+FF41..U+FF5A->a..z, U+FF21..U+FF3A->a..z,\
A..Z->a..z, a..z, U+0149, U+017F, U+0138, U+00DF, U+00FF, U+00C0..U+00D6->U+00E0..U+00F6,\
U+00E0..U+00F6, U+00D8..U+00DE->U+00F8..U+00FE, U+00F8..U+00FE, U+0100->U+0101, U+0101,\
U+0102->U+0103, U+0103, U+0104->U+0105, U+0105, U+0106->U+0107, U+0107, U+0108->U+0109,\
U+0109, U+010A->U+010B, U+010B, U+010C->U+010D, U+010D, U+010E->U+010F, U+010F,\
U+0110->U+0111, U+0111, U+0112->U+0113, U+0113, U+0114->U+0115, U+0115, \
U+0116->U+0117,U+0117, U+0118->U+0119, U+0119, U+011A->U+011B, U+011B, U+011C->U+011D,\
 U+011D,U+011E->U+011F, U+011F, U+0130->U+0131, U+0131, U+0132->U+0133, U+0133, \
U+0134->U+0135,U+0135, U+0136->U+0137, U+0137, U+0139->U+013A, U+013A, U+013B->U+013C, \
U+013C,U+013D->U+013E, U+013E, U+013F->U+0140, U+0140, U+0141->U+0142, U+0142, \
U+0143->U+0144,U+0144, U+0145->U+0146, U+0146, U+0147->U+0148, U+0148, U+014A->U+014B, \
U+014B,U+014C->U+014D, U+014D, U+014E->U+014F, U+014F, U+0150->U+0151, U+0151, \
U+0152->U+0153,U+0153, U+0154->U+0155, U+0155, U+0156->U+0157, U+0157, U+0158->U+0159,\
 U+0159,U+015A->U+015B, U+015B, U+015C->U+015D, U+015D, U+015E->U+015F, U+015F, \
U+0160->U+0161,U+0161, U+0162->U+0163, U+0163, U+0164->U+0165, U+0165, U+0166->U+0167, \
U+0167,U+0168->U+0169, U+0169, U+016A->U+016B, U+016B, U+016C->U+016D, U+016D, \
U+016E->U+016F,U+016F, U+0170->U+0171, U+0171, U+0172->U+0173, U+0173, U+0174->U+0175,\
 U+0175,U+0176->U+0177, U+0177, U+0178->U+00FF, U+00FF, U+0179->U+017A, U+017A, \
U+017B->U+017C,U+017C, U+017D->U+017E, U+017E, U+0410..U+042F->U+0430..U+044F, \
U+0430..U+044F,U+05D0..U+05EA, U+0531..U+0556->U+0561..U+0586, U+0561..U+0587, \
U+0621..U+063A, U+01B9,U+01BF, U+0640..U+064A, U+0660..U+0669, U+066E, U+066F, \
U+0671..U+06D3, U+06F0..U+06FF,U+0904..U+0939, U+0958..U+095F, U+0960..U+0963, \
U+0966..U+096F, U+097B..U+097F,U+0985..U+09B9, U+09CE, U+09DC..U+09E3, U+09E6..U+09EF, \
U+0A05..U+0A39, U+0A59..U+0A5E,U+0A66..U+0A6F, U+0A85..U+0AB9, U+0AE0..U+0AE3, \
U+0AE6..U+0AEF, U+0B05..U+0B39,U+0B5C..U+0B61, U+0B66..U+0B6F, U+0B71, U+0B85..U+0BB9, \
U+0BE6..U+0BF2, U+0C05..U+0C39,U+0C66..U+0C6F, U+0C85..U+0CB9, U+0CDE..U+0CE3, \
U+0CE6..U+0CEF, U+0D05..U+0D39, U+0D60,U+0D61, U+0D66..U+0D6F, U+0D85..U+0DC6, \
U+1900..U+1938, U+1946..U+194F, U+A800..U+A805,U+A807..U+A822, U+0386->U+03B1, \
U+03AC->U+03B1, U+0388->U+03B5, U+03AD->U+03B5,U+0389->U+03B7, U+03AE->U+03B7, \
U+038A->U+03B9, U+0390->U+03B9, U+03AA->U+03B9,U+03AF->U+03B9, U+03CA->U+03B9, \
U+038C->U+03BF, U+03CC->U+03BF, U+038E->U+03C5,U+03AB->U+03C5, U+03B0->U+03C5, \
U+03CB->U+03C5, U+03CD->U+03C5, U+038F->U+03C9,U+03CE->U+03C9, U+03C2->U+03C3, \
U+0391..U+03A1->U+03B1..U+03C1,U+03A3..U+03A9->U+03C3..U+03C9, U+03B1..U+03C1, \
U+03C3..U+03C9, U+0E01..U+0E2E,U+0E30..U+0E3A, U+0E40..U+0E45, U+0E47, U+0E50..U+0E59, \
U+A000..U+A48F, U+4E00..U+9FBF,U+3400..U+4DBF, U+20000..U+2A6DF, U+F900..U+FAFF, \
U+2F800..U+2FA1F, U+2E80..U+2EFF,U+2F00..U+2FDF, U+3100..U+312F, U+31A0..U+31BF, \
U+3040..U+309F, U+30A0..U+30FF,U+31F0..U+31FF, U+AC00..U+D7AF, U+1100..U+11FF, \
U+3130..U+318F, U+A000..U+A48F,U+A490..U+A4CF
 min_prefix_len  = 0
 min_infix_len  = 1
 ngram_len = 1

 ngrams_chars = U+4E00..U+9FBF, U+3400..U+4DBF, U+20000..U+2A6DF, U+F900..U+FAFF,\
U+2F800..U+2FA1F, U+2E80..U+2EFF, U+2F00..U+2FDF, U+3100..U+312F, U+31A0..U+31BF,\
U+3040..U+309F, U+30A0..U+30FF, U+31F0..U+31FF, U+AC00..U+D7AF, U+1100..U+11FF,\
U+3130..U+318F, U+A000..U+A48F, U+A490..U+A4CF
}
indexer
{
 mem_limit   = 32M
}
searchd
{
 # address    = 0.0.0.0
 port    = 3312
 log     = d:/sphinx/log/searchd.log
 query_log   = d:/sphinx/log/query.log
 read_timeout  = 5
 max_children  = 30
 pid_file   = d:/sphinx/log/searchd.pid
 max_matches   = 1000
 seamless_rotate  = 1
}

相关配置项说明:

Source部分配置项说明

#type 数据库类型,目前支持mysql与pgsql
#strip_html 是否去掉html标签
#sql_host 数据库主机地址
#sql_user 数据库用户名
#sql_pass 数据库密码
#sql_db   数据库名称
#sql_port 数据库采用的端口
#sql_query_pre 执行sql前要设置的字符集,用utf8必须SET NAMES utf8
#sql_query  全文检索要显示的内容,在这里尽可能不使用where或group by,将where与groupby的内容交给sphinx,由sphinx进行条件过滤与groupby效率会更高
#注意:select 出来的字段必须至少包括一个唯一主键(ARTICLESID)以及要全文检索的字段,你计划原本在where中要用到的字段也要select出来
#这里不用使用orderby
#sql_attr_开头的表示一些属性字段,你原计划要用在where,orderby,groupby中的字段要在这里定义
#根据我们原先的SQL:
#select * from eht_articles where title like ? and catalogid=? And edituserid=?  And addtime between ? and ? order by hits desc
#我们需要对catalogid,edituserid,addtime,hits进行属性定义(这四个字段也要在select的字段列表中),定义时不同的字段类型有不同的属性名称,具体可以见sphinx.conf.in中的说明

index部分配置项说明

#source 数据源名
#path   索引记录存放目录,如d:/sphinx/data/cgfinal,实际存放时会存放在d:/sphinx/data目录,然后创建多个cgfinal名称,不同扩展名的索引文件。
#其他的配置如min_word_len,charset_type,charset_table,ngrams_chars,ngram_len这些则是支持中文检索需要设置的内容。
#如果检索的不是中文,则charset_table,ngrams_chars,min_word_len就要设置不同的内容,具体官方网站的论坛中有很多,大家可以去搜索看看。

5. 运行Sphinx

首先要对数据进行索引或重建索引

进入命令行,运行d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf cgfinal

如果您在sphinx.conf中配置了多个数据源,想一次性全部索引则d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf –all

如果只是想对某个数据源进行索引,则d:/sphinx/bin/release/indexer –config d:/sphinx/sphinx.conf 索引名称(这里的索引名称是你在sphinx.conf中定义的索引名称)

运行检索守护进程searchd

进入命令行,运行d:/sphinx/bin/release/searchd –config d:/sphinx/sphinx.conf,此时系统会在3312端口侦听mysql的全文检索请求,所以如果您的mysql与sphinx不在同一台 机器,要保证3312端口不被防火墙阻隔。

6. 搜索(翻译)

6.1. 匹配模式

SPH_MATCH_ALL,匹配所有查询词(缺省模式)

SPH_MATCH_ANY,匹配任意查询词

SPH_MATCH_PHRASE,短语匹配

SPH_MATCH_BOOLEAN,布尔表达式匹配

SPH_MATCH_EXTENDED,查询匹配一个Sphinx内部查询语言表达式

6.2. 布尔查询语法(Boolean query syntax)

布尔查询允许使用下面特殊操作符:

AND:hello & world

OR:hello | world

NOT:hello -world或hello !world

Grouping:(hello world)

举一个使用这些操作符的例子:

( cat -dog ) | ( cat -mouse)

AND是一个隐式操作符,“hello world”就相当于“hello & world”。

OR的优先级高于AND,所以“looking for cat | dog | mouse”的意思是“looking for (cat | dog | mouse)”而不是“(looking for cat) | dog | mouse”

象“-dog”这种隐式地包含了所有查询记录,是不会被执行的。这主要是考虑到技术上与性能上的原因,从技术上来说,sphinx不能总保持所有文章的ID列表,性能上来说,当结果集巨大(10-100M),执行这样的查询将费耗较长时间。

6.3. 扩展查询语法(Extended query syntax)

扩展查询允许合我下面特殊操作符:

操作符OR:hello | world

操作符NOT:hello -world或hello !world

字段搜索操作符:@title hello @body world

短语(phrase)搜索符:”hello world”

临近(proximity)搜索符:”hello world”~10

举例:

“hello world” @title “example program”~5 @body python -(php|perl)

AND是一个隐式操作符,”hello world”表示hello与world都要出现在匹配的记录中。

OR的优先级高于AND,所以“looking for cat | dog | mouse”的意思是“looking for (cat | dog | mouse)”而不是“(looking for cat) | dog | mouse”

临近距离在串中标明了,主要是用来调整单词数量,应用在引号中的所有查询字串。”cat dog mouse”~5表示包括这三个单词在内,总共不能多于8个单词的间隔。比如”CAT aaa bbb ccc DOG eee fff MOUSE”就不能匹配这个查询,因为单词间隔刚好是8个。

象aaa | ( bbb ccc | ( ddd eee ) )这样的括号嵌套查询目前还不支持,但以后会修正的。

否定(如NOT)只允许出现在顶层,不允许出现在括号内(如groups)。这点是不会改变的。因为支持否定嵌套查询会让短语排序(phrase ranking)的实现变得过于复杂。

6.4. 权重(匹配度,Weight)

采用什么权重功能取决于搜索模式(Search mode)

在权重函数中,有两个主要部分:(短语排名)phrase rank和statistical rank(统计排名)

短语排名是基于搜索词在文档和查询短语中的最长公共子序列(LCS)的长度。所以如果在记录中有切确的短语匹配,记录的短语排名将有可能是最高的,等于查询单词的总个数。

统计排名是建立在经典的BM25算法基础之上,它只考虑词频。词在全部文档集合中以低的频度出现或高频度出现在匹配的文档中,那么它获得的权重就越大,最终的BM25权重是一个介于0到1之间的小数。

好的子短语匹配得到好的排名,最好的匹配放到最顶端。Sphinx作者的经验是:基于排名的密切短语比其它任何单独的统计方式表现出较好的搜索质量。

在SPH_MATCH_BOOLEAN 模式中,不需要计算权重,每条匹配记录的权重都是1

在SPH_MATCH_ALL和SPH_MATCH_PHRASE模式中,最终的权重是短语排名权重的总和

FIXME (TOFIX:翻译不畅)在SPH_MATCH_ANY模式中,本质上是一样的,但它也增加了每个字段的匹配单词数量,在这之前,短语排名权重乘以一个足够大的值以保证在任意一个字段的较高短语排名可以匹配排名较高者,即使它的字段权重比较低。FIXME

在SPH_MATCH_EXTENDED模式中,最终的权重是短语权重和BM25权重的总和,再乘以1000取整。

7. 如何调用Sphinx

按上面配置,第5节点对数据库进行了索引,通过Sphinx自带的search(在bin/release目录)就可以在命令行进行搜索:

(搜索CGArt)
windows上:
search -c d:/sphinx/sphinx.conf CGArt
Linux上:
cd /usr/local/sphinx
./bin/search -c sphinx.conf CGArt
运行后,系统提示一堆信息:
....
....
words:
1. 'cgart': 36 documents, 189 hits
这个表示库中有36条记录符合要求,出现CGArt的有189处。

应用程序如果想调用Sphinx,可以从两个方面:

一是通过Sphinx官方提供的API接口(接口有Python,Java,Php三种版本)

二是通过安装SphinxSE(具体见1.2部分),然后创建一个中介sphinxSE类型的表,再通过执行特定的SQL语句实现。

通过官方API调用Sphinx(以PHP为例)

在sphinx安装目录有一个API目录,里面有三个PHP文件:test.php,test2.php和sphinxapi.php。 sphinxapi.php是sphinx调用接口封装文件,test.php是一个在命令行下执行的查询例子文件,test2.php是一个生成摘要的 例子文件。

在命令下行运行test.php(Linux上没有API目录,需要从源程序包中复制api目录至/usr/local/sphinx)

Windows上:
 D:\sphinx\bin\release>c:\php5.2\php.exe -c c:\php5.2\php.ini ..\..\api\test.php -i cgfinal CGart

Linux上(php在/usr/local/php目录,sphinx.conf在/usr/local/sphinx目录):

 cd /usr/local/sphinx
 /usr/local/php/bin/php api/test.php -i cgfinal CGArt

Sphinx的API查询接口主要有这些内容(其实对照 一下sphinxapi.php就清楚了):

  //创建Sphinx的客户端接口对象
  $cl = new SphinxClient ();

  //设置连接Sphinx主机名与端口
  $cl->SetServer('localhost',3312);

  //可选,为每一个全文检索字段设置权重,主要根据你在sql_query中定义的字段的顺序,Sphinx系统以后会调整,可以按字段名称来设定权重
  $cl->SetWeights ( array ( 100, 1 ) );

  //设定搜索模式,SPH_MATCH_ALL,SPH_MATCH_ANY,SPH_MATCH_BOOLEAN,SPH_MATCH_EXTENDED,SPH_MATCH_PHRASE
  $cl->SetMatchMode(SPH_MATCH_ALL);

  //设定过滤条件$attribute是属性名,相当于字段名(用SPH_MATCH_EXTENDED时),$value是值,$exclude是布尔型,
  当为true时,相当于$attribute!=$value,默认值是false
  $cl->SetFilter($attribute, $values, $exclude);

  //设定group by
  //根据分组方法,匹配的记录集被分流到不同的组,每个组都记录着组的匹配记录数以及根据当前排序方法本组中的最佳匹配记录。
  //最后的结果集包含各组的一个最佳匹配记录,和匹配数量以及分组函数值
  //结果集分组可以采用任意一个排序语句,包括文档的属性以及sphinx的下面几个内部属性
  //@id--匹配文档ID
  //@weight, @rank, @relevance--匹配权重
  //@group--group by 函数值
  //@count--组内记录数量
  //$groupsort的默认排序方法是@group desc,就是按分组函数值大小倒序排列
  $cl->SetGroupBy($attribute, $func, $groupsort);

  //设定order by的内容,第一个参数是排序方法名,值有
  // SPH_SORT_RELEVANCE,SPH_SORT_ATTR_DESC,SPH_SORT_ATTR_ASC,SPH_SORT_TIME_SEGMENTS,SPH_SORT_EXTENDED
  //$sortby的值如"HITS desc"
  $cl->SetSortMode(SPH_SORT_EXTENDED, $sortby);

  //set count-distinct attribute for group-by queries,$distinct为字符串
  $cl->SetGroupDistinct ( $distinct );

  //相当于mysql的limit $offset,$limit
  $cl->SetLimits($start,$limit)

  //$q是查询的关键字,$index是索引名称,当等于*时表查询所有索引
  $res = $cl->Query ( $q, $index );

$cl->Query()返回的内容print_r后大概是:

Array
(
  [error] =>
  [warning] =>
  [status] => 0
  [fields] => Array
      (
          [0] => title
          [1] => contents
          [2] => author
      )
  [attrs] => Array
      (
          [catalogid] => 1
          [addtime] => 2
          [edituserid] => 1
          [hits] => 1
      )
  [matches] => Array
      (
          [380] => Array
              (
                  [weight] => 1
                  [attrs] => Array
                      (
                          [catalogid] => 7
                          [addtime] => 1112677492
                          [edituserid] => 1
                          [hits] => 1470
                      )
              )
          [599] => Array
              (
                  [weight] => 101
                  [attrs] => Array
                      (
                          [catalogid] => 7
                          [addtime] => 1115910729
                          [edituserid] => 1
                          [hits] => 1749
                      )
              )
          [850] => Array
              (
                  [weight] => 1
                  [attrs] => Array
                      (
                          [catalogid] => 2
                          [addtime] => 1118741392
                          [edituserid] => 1
                          [hits] => 289
                      )
              )
          [877] => Array
              (
                  [weight] => 1
                  [attrs] => Array
                      (
                          [catalogid] => 2
                          [addtime] => 1118898869
                          [edituserid] => 1
                          [hits] => 9870
                      )
              )
          [1040] => Array
              (
                  [weight] => 101
                  [attrs] => Array
                      (
                          [catalogid] => 2
                          [addtime] => 1120708579
                          [edituserid] => 1
                          [hits] => 318
                      )
              )
      )
  [total] => 129
  [total_found] => 129
  [time] => 0.000
  [words] => Array
      (
          [design] => Array
              (
                  [docs] => 129
                  [hits] => 265
              )
      )
)

从上面可以看出Query并不能全部取得我们想要的记录内容,比如说Title,Contents字段就没有取出来,根据官方的说明是sphinx并没有 连到mysql去取记录,只是根据它自己的索引内容进行计算,因此如果想用sphinxAPI去取得我们想要的记录,还必须将Query的结果为依据去查 询MySQL才可以得到最终我们想要的结果集。

test2.php是一个摘要生成的例子文件,如果你的本地机器已装好sphinx,php运行环境,你可以通过浏览器看查看test2.php的运行效果。

假设我要搜索关键词”test”,通过sphinx可以取到搜索结果,在显示搜索结果时,我希望将含有”test”的进行红色或加粗显示,同时,我 不希望全部都显示出来,只需要显示一段摘要,就象google或百度那样,搜出来的结果不是全篇显示,只是部分显示,这个就是摘要的作用。

以test2.php中为例,以下是test2.php的代码:

require ( "sphinxapi.php" );
$docs = array
(
 "this is my test text to be highlighted, and for the sake of the testing we need to pump its length somewhat",
 "another test text to be highlighted, below limit",
 "test number three, without phrase match",
 "final test, not only without phrase match, but also above limit and with swapped phrase text test as well",
);
$words = "test";
$index = "cgfinal";
$opts = array
(
 "before_match"  => "<span style='font-weight:bold;color:red'>",
 "after_match"  => "</span>",
 "chunk_separator" => " ... ",
 "limit"    => 60,
 "around"   => 3,
);

foreach ( array(0,1) as $exact )
{
 $opts["exact_phrase"] = $exact;
 print "exact_phrase=$exact\n";

 $cl = new SphinxClient ();
 $res = $cl->BuildExcerpts ( $docs, $index, $words, $opts );
 if ( !$res )
 {
  die ( "ERROR: " . $cl->GetLastError() . ".\n" );
 } else
 {
  $n = 0;
  foreach ( $res as $entry )
  {
   $n++;
   print "n=$n, res=$entry<br/>";
  }
  print "\n";
 }
}

在IE上运行的效果是:

dev.cgfinal.com_sphinx_img_test2.jpg

在实际环境中,上面代码的$docs是我们用sphinx搜索出来的结果,这个结果利用BuildExcerpts方法可以实现摘要的功能。

采用SphinxSE方式调用Sphinx

采用sphinxSE必须要求为mySQL安装sphinxSE Engine驱动,方法在第1节中我已讲到

要创建一张sphinx 专用表,你可以这样建

CREATE TABLE `sphinx` (
`id` int(11) NOT NULL,
`weight` int(11) NOT NULL,
`query` varchar(255) NOT NULL,
`CATALOGID` INT NOT NULL,
`EDITUSERID` INT NOT NULL,
`HITS` INT NULL,
`ADDTIME` INT NOT NULL,
KEY `Query` (`Query`)
) ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://localhost:3312/cgfinal';

警告

注:与一般mysql表不同的是ENGINE=SPHINX DEFAULT CHARSET=utf8 CONNECTION='sphinx://localhost:3312/cgfinal';,这里表示这个表采用SPHINXSE引擎,字符集是 utf8,与sphinx的连接串是'sphinx://localhost:3312/cgfinal,cgfinal是索引名称

根据sphinx官方说明,这个表必须至少有三个字段,字段起什么名称无所谓,但类型的顺序必须是 integer,integer,varchar,分别表示记录标识document ID,匹配权重weight与查询query,同时document ID与query必须建索引。另外这个表还可以建立几个字段,这几个字段的只能是integer或TIMESTAMP类型,字段是与sphinx的结果集 绑定的,因此字段的名称必须与在sphinx.conf中定义的属性名称一致,否则取出来的将是Null值。

比如我在上面有定义了sql_attr_uint= CATALOGID,sql_attr_uint= EDITUSERID,sql_attr_uint = HITS,sql_attr_timestamp = ADDTIME,那么在这个表里头,你就可以再定义CATALOGID,EDITUSERID,HITS,ADDTIME四个字段。

通过sql语句实现查询。通过select * from sphinx where query='sphinx表达式' 的方式可以实现查询,通过让sphinx表与eht_articles或其他表并联查询(条件是 sphinx.id=eht_articles.Articlesid)还可以实现更为复杂的sql,基本上可以符合我们日常的要求。

sphinx表达式在sphinx的手册中也提到了,这里我简单说明几条:

query='关键字' ,关键字就是你要搜索的关键字,如query='CGArt'表示你要全文搜索CGArt
mode,搜索模式,值有:all,any,phrase,boolean,extended,默认是all
sort,排序模式,必须是relevance,attr_desc,attr_asc,time_segments,extended中的一种,在所有模式中除了relevance外,
       属性名(或用extended排序)前面都需要一个冒号。
   ... where query='test;sort=attr_asc:hits';
  ... where query='test;sort=extended:@weight desc,hits asc';
offset,结果记录集的起始位置,默认是0
limit,从结果记录集中取出的数量,默认是20条
index,要搜索的索引名称
  ... where query='test;index=cgfinal';
  ... where query='test;index=test1,test2,test3;';
minid,maxid,匹配最小与最大文档ID
weights,以逗号分割的分配给sphinx全文检索字段的权重列表
   ... where query='test;weights=1,2,3;';
filter,!filter,以逗号分隔的属性名与一堆要匹配的值
   #只包括1,5,19的组
   ... where query='test;filter=group_id,1,5,19;';
  #不包括3,11的组
   ... where query='test;!filter=group_id,3,11';
range,!range,逗号分隔的属性名一最小与最大要匹配的值
   #从3至7的组
   ... where query='test;range=group_id,3,7;';
   #不包括从5至25的组
   ... where query='test;!range=group_id,5,25;';
maxmatches,每个查询最大匹配的值
   ... where query='test;maxmatches=2000;';
groupby,group by 方法与属性
   ... where query='test;groupby=day:published_ts;';
   ... where query='test;groupby=attr:group_id;';
groupsort,group by 的排序
   ... where query='test;gropusort='@count desc';

需要注意的重要一点是让sphinx进行排序,过滤,切分结果记录集比用MySQL的where,orderby 和limit将有更好的效率。有两个原因,首先sphinx做了很多优化,在这些任务上它比mySQL做得更出色,其次searchd在打 包,sphinxSE在传输与解包上需要的数据量更少。

你可以通过运用join在sphinxSE的搜索表和其他引擎类型的表做并联查询。这有一个从example.sql中documents表的例子:

mysql> SELECT content, date_added FROM test.documents docs
-> JOIN t1 ON (docs.id=t1.id)
-> WHERE query="one document;mode=any";
+-------------------------------------+---------------------+
| content                             | docdate             |
+-------------------------------------+---------------------+
| this IS my test document number two | 2006-06-17 14:04:28 |
| this IS my test document number one | 2006-06-17 14:04:28 |
+-------------------------------------+---------------------+
2 rows IN SET (0.00 sec)

mysql> SHOW ENGINE SPHINX STATUS;
+--------+-------+---------------------------------------------+
| Type   | Name  | STATUS                                      |
+--------+-------+---------------------------------------------+
| SPHINX | stats | total: 2, total found: 2, time: 0, words: 2 |
| SPHINX | words | one:1:2 document:2:2                        |
+--------+-------+---------------------------------------------+
2 rows IN SET (0.00 sec)

8. SphinxSE的SQL查询例子演练

从eht_articles中查询标题含有“动画”关键字的记录。

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 动画;mode=extended'

提示

说明:要指定某个字段进行搜索,要用@字段名+空格+关键字+分号+mode=extended 如果不指定字段,则系统会对TITLE,CONTENTS进行搜索 ,对什么字段进行全文检索取决于在sphinx.conf中sql_query定义的select 中的字段(文本类型)

从eht_articles中查询文章内容或标题含有“CGArt”关键字的记录。

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=sphinx.id AND query='动画'

若AUTHOR,TITLE,CONTENTS三个字段都全文索引了,但只想搜title,或contents中含有“动画”关键字的文章

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 动画 | @contents 动画;
mode=extended'

查询标题含有“动画”关键字,catalogid为7,edituserid为1的记录

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 动画;
filter=edituserid,1;filter=catalogid,7;mode=extended'

提示

采用filter=字段名称,值就相当于where中的 字段名=值,filter提到的字段必须在sphinx的source部分的字段属性定义中定义,如

sql_attr_uint = CATALOGID
sql_attr_uint = EDITUSERID
sql_attr_uint = HITS
sql_attr_timestamp = ADDTIME

查询标题含有“动画”关键字,按人气Hits从大至小,栏目ID从大至小排序

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 动画;mode=extended;
sort=extended:hits desc,catalogid desc'

在sphinx中,select出来的内容是按weight从大至小排序的,weight是根据sphinx内部一定的算法算出来的,越大就表示越匹配,如果想按匹配度从大至小排序,则可以:

SELECT c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='@title 动画;mode=extended;
sort=@weight desc'

搜内容或标题含有优秀或Icon或设计,按catalogid分组,按匹配度从高至低排序

SELECT t.*,c.* FROM eht_articles AS c,sphinx AS t WHERE c.articlesid=t.id AND query='优秀 | Icon | 设计;
mode=extended;groupby=attr:catalogid;groupsort=@weight;'

9. 如何自动重建索引

10. 相关资源

用php构建自定义搜索引擎 官方手册文档 本文中提到的sphinx.conf配置文件(用GBK编码查看)

Sphinx在Windows下安装使用[支持中文全文检索]

前一阵子尝试使用 了一下Sphinx,一个能够被各种语言(PHP/Python/Ruby/etc)方便调用的全文检索系统。网上的资料大多是在linux环境下的安装 使用,当然,作为生产环境很有必要部署在*nix环境下,作为学习测试,还是windows环境比较方便些。

本文旨在提供一种便捷的方式让Sphinx在windows下安装配置以支持中文全文检索,配置部分在linux下通用。

一、关于Sphinx

Sphinx 是一个在GPLv2 下发布的一个全文检索引擎,商业授权(例如, 嵌入到其他程序中)需要联系作者(Sphinxsearch.com)以获得商业授权。

一般而言,Sphinx是一个独立的搜索引擎,意图为其他应用提供高速、低空间占用、高结果相关度的全文搜索功能。Sphinx可以非常容易的与SQL数据库和脚本语言集成。

当前系统内置MySQL和PostgreSQL 数据库数据源的支持,也支持从标准输入读取特定格式的XML数据。通过修改源代码,用户可以自行增加新的数据源(例如:其他类型的DBMS的原生支持)。

搜索API支持PHP、Python、Perl、Rudy和Java,并且也可以用作MySQL存储引擎。搜索API非常简单,可以在若干个小时之内移植到新的语言上。

Sphinx特性:

  • 高速的建立索引(在当代CPU上,峰值性能可达到10MB/秒);
  • 高性能的搜索(在2–4GB的文本数据上,平均每次检索响应时间小于0.1秒);
  • 可处理海量数据(目前已知可以处理超过100GB的文本数据,在单一CPU的系统上可处理100M文档);
  • 提供了优秀的相关度算法,基于短语相似度和统计(BM25)的复合Ranking方法;
  • 支持分布式搜索;
  • 提供文件的摘录生成;
  • 可作为MySQL的存储引擎提供搜索服务;
  • 支持布尔、短语、词语相似度等多种检索模式;
  • 文档支持多个全文检索字段(最大不超过32个);
  • 文档支持多个额外的属性信息(例如:分组信息,时间戳等);
  • 停止词查询;
  • 支持单一字节编码和UTF-8编码;
  • 原生的MySQL支持(同时支持MyISAM和InnoDB);
  • 原生的PostgreSQL支持.

中文手册可以在这里获得(酷勤网备用下载地址:sphinx_doc_zhcn_0.9.pdf)。

二、Sphinx在windows上的安装

1.直接在http://www.sphinxsearch.com/downloads.html找到最新的windows版本,我这里下的是Win32 release binaries with MySQL support,下载后解压在D:\sphinx目录下;

2.在D:\sphinx\下新建一个data目录用来存放索引文件,一个log目录方日志文件,复制D:\sphinx\sphinx.conf.in到D:\sphinx\bin\sphinx.conf(注意修改文件名);

3.修改D:\sphinx\bin\sphinx.conf,我这里列出需要修改的几个:

type        = mysql # 数据源,我这里是mysql
sql_host    = localhost # 数据库服务器
sql_user    = root # 数据库用户名
sql_pass    = '' # 数据库密码
sql_db      = test # 数据库
sql_port    = 3306 # 数据库端口
sql_query_pre   = SET NAMES utf8 # 去掉此行前面的注释,如果你的数据库是uft8编码的
index test1
{
# 放索引的目录
 path   = D:/sphinx/data/
# 编码
 charset_type  = utf-8
 #  指定utf-8的编码表
 charset_table  = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
 # 简单分词,只支持0和1,如果要搜索中文,请指定为1
 ngram_len    = 1
# 需要分词的字符,如果要搜索中文,去掉前面的注释
 ngram_chars   = U+3000..U+2FA1F
}
# index test1stemmed : test1
# {
 # path   = @CONFDIR@/data/test1stemmed
 # morphology  = stem_en
# }

# 如果没有分布式索引,注释掉下面的内容

# index dist1
# {
 # 'distributed' index type MUST be specified
 # type    = distributed
 # local index to be searched
 # there can be many local indexes configured
 # local    = test1
 # local    = test1stemmed
 # remote agent
 # multiple remote agents may be specified
 # syntax is 'hostname:port:index1,[index2[,...]]
 # agent    = localhost:3313:remote1
 # agent    = localhost:3314:remote2,remote3
 # remote agent connection timeout, milliseconds
 # optional, default is 1000 ms, ie. 1 sec
 # agent_connect_timeout = 1000
 # remote agent query timeout, milliseconds
 # optional, default is 3000 ms, ie. 3 sec
 # agent_query_timeout  = 3000
# }
# 搜索服务需要修改的部分
searchd
{
 # 日志
 log     = D:/sphinx/log/searchd.log
 # PID file, searchd process ID file name
 pid_file   = D:/sphinx/log/searchd.pid
 # windows下启动searchd服务一定要注释掉这个
 # seamless_rotate  = 1
}

4.导入测试数据

C:\Program Files\MySQL\MySQL Server 5.0\bin>mysql -uroot test<d:/sphinx/example.sql

5.建立索引

D:\sphinx\bin>indexer.exe –all
Sphinx 0.9.8-release (r1533)
Copyright (c) 2001-2008, Andrew Aksyonoff

using config file ‘./sphinx.conf’…
indexing index ‘test1′…
collected 4 docs, 0.0 MB
sorted 0.0 Mhits, 100.0% done
total 4 docs, 193 bytes
total 0.101 sec, 1916.30 bytes/sec, 39.72 docs/sec

D:\sphinx\bin>

6.搜索’test’试试

D:\sphinx\bin>search.exe test
Sphinx 0.9.8-release (r1533)
Copyright (c) 2001-2008, Andrew Aksyonoff

using config file ‘./sphinx.conf’…
index ‘test1′: query ‘test ‘: returned 3 matches of 3 total in 0.000 sec

displaying matches:
1. document=1, weight=2, group_id=1, date_added=Wed Nov 26 14:58:59 2008
id=1
group_id=1
group_id2=5
date_added=2008-11-26 14:58:59
title=test one
content=this is my test document number one. also checking search within
phrases.
2. document=2, weight=2, group_id=1, date_added=Wed Nov 26 14:58:59 2008
id=2
group_id=1
group_id2=6
date_added=2008-11-26 14:58:59
title=test two
content=this is my test document number two
3. document=4, weight=1, group_id=2, date_added=Wed Nov 26 14:58:59 2008
id=4
group_id=2
group_id2=8
date_added=2008-11-26 14:58:59
title=doc number four
content=this is to test groups

words:
1. ‘test’: 3 documents, 5 hits
D:\sphinx\bin>

都所出来了吧。

6.测试中文搜索

修改test数据库中documents数据表,

UPDATE `test`.`documents` SET `title` = ‘测试中文’, `content` = ‘this is my test document number two,应该搜的到吧’ WHERE `documents`.`id` = 2;

重建索引:

D:\sphinx\bin>indexer.exe –all

搜索’中文’试试:

D:\sphinx\bin>search.exe 中文
Sphinx 0.9.8-release (r1533)
Copyright (c) 2001-2008, Andrew Aksyonoff

using config file ‘./sphinx.conf’…
index ‘test1′: query ‘中文 ‘: returned 0 matches of 0 total in 0.000 sec

words:
D:\sphinx\bin>

貌似没有搜到,这是因为windows命令行中的编码是gbk,当然搜不出来。我们可以用程序试试,在D:\sphinx\api下新建一个foo.php的文件,注意utf-8编码

<?php
require ’sphinxapi.php’;
$s = new SphinxClient();
$s->SetServer(’localhost’,3312);
$result = $s->Query(’中文’);
var_dump($result);
?>

启动Sphinx searchd服务

D:\sphinx\bin>searchd.exe
Sphinx 0.9.8-release (r1533)
Copyright (c) 2001-2008, Andrew Aksyonoff

WARNING: forcing –console mode on Windows
using config file ‘./sphinx.conf’…
creating server socket on 0.0.0.0:3312
accepting connections

执行PHP查询:

php d:/sphinx/api/foo.php

结果是不是出来?剩下的工作就是去看手册,慢慢摸索高阶的配置。