当前位置: 首页 > 工具软件 > Sphinx > 使用案例 >

sphinx

解晟睿
2023-12-01

一、Introduction

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

搜索api支持php、python、perl、ruby和java,并且也可以用作mysql存储引擎。

coreseek全文检索服务器2.0是在sphinx基础上开发的全文检索软件,按照GPLv2协议发行。中文版的sphinx。

sphinx的特性,高速建立索引,高性能的搜索,处理海量数据,优秀的相关度算法,支持分布式搜索,可作为mysql存储引擎提供搜索服务,支持布尔、短语、词语相似度等多种检索模式等等。

支持多数操作系统。

二、windows下使用sphinx

一般的搜索中,使用关键字搜索时一般是使用like  ‘%xxx%’,这样查询在数据量大时非常慢,like是全表扫描。

到coreseek官网下载软件包解压。

1.        使用sphinx为要搜索的数据创建全文索引

制作配置文件,在解压包的etc/下,复制一份csft_mysql.conf,改名为sphinx.conf,放到解压包的根目录/下。修改配置文件。

#MySQL数据源配置,详情请查看:http://www.coreseek.cn/products-install/mysql/

#源定义,为eshopphp数据库中的es_goods表为数据源

source goods

{

    type                    = mysql



    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre            = SET NAMES utf8

    #主查询,要为哪些数据建索引就使用一条sql语句把这些数据取出来

    #第一个字段一定要求是id

    #为所有的商品的goods_name,goods_desc,attr_value这三个字段创建全文索引

    sql_query                = SELECTa.id,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value) attr_value  FROM es_goods a left join es_goods_attr b ona.id=b.goods_id group by a.id

}



#index定义,一个数据源对应一个index

#定义索引文件

index goods

{

    source          = goods             #对应的source名称

    #生成的索引文件存放的目录\索引文件的名字,不要中文路径,下面最后的goods为索引文件的名字

    path            = D:\002php\shpinx\var\data\goods

    docinfo         = extern

    mlock           = 0

    morphology      = none

    min_word_len    = 1

    html_strip      = 0



    #中文分词配置,中文分词词库文件所在目录,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/

    #charset_dictpath = /usr/local/mmseg3/etc/#BSD、Linux环境下设置,/符号结尾

    #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...

    charset_dictpath = D:\002php\shpinx\etc

    #shpinx只支持utf-8

    charset_type     = zh_cn.utf-8

}



#全局index定义

indexer

{

    mem_limit            = 128M

}



#searchd,sphinx服务器配置

searchd

{

    listen              =   9312

    read_timeout        = 5

    max_children        = 30

    max_matches         = 1000

    seamless_rotate     = 0

    preopen_indexes     = 0

    unlink_old          = 1

    #配置以下三个文件存放的绝对路径,目录中不要出现中文

    pid_file =D:\002php\shpinx\var\log\searchd_mysql.pid

    log =D:\002php\shpinx\var\log\searchd_mysql.log

    query_log = D:\002php\shpinx\var\log\query_mysql.log

}

使用sphinx为商品数据生成索引

使用bin/indexer.exe命令。

indexer.exe -c  D:\002php\shpinx\sphinx.conf  goods

2.        安装并启动sphinx服务器

使用bin/searchd.exe命令

searchd.exe -c  D:\002php\shpinx\sphinx.conf  --install

打开windows服务窗口找到sphinx并启动

执行services.msc打开服务窗口。

3.        在项目中使用php代码查询sphinx

先复制sphinx的api目录下的sphinxapi.php文件到项目

然后在关键字搜索的方法中调用sphinx的客户端类。

    //接收要搜索的关键

    $key = I('get.key');

    //搜索sphinx

    require ('./sphinxapi.php');

    $sph = new \SphinxClient();

    $sph->SetServer('localhost', 9312);

    //第二个参数,sphinx中索引的名字默认是*,表示所有的索引

$ret =$sph->Query($key, 'goods');

sphinx的返回结果中会返回符合条件的id,不会返回关于这个商品的具体数据,然后通过数据库取出商品的详细信息。

三、sphinx的实时索引更新

如果整个数据量非常大,以至于难以经常性的重建索引,但是每次新增的记录却相对来说少。比如,一个论坛有100000000000个已经归档的帖子,但是每天只有1000个新帖子。这中情况下使用主索引+增量索引(main+delta)模式来实现近实时的索引更新。

这种方法的基本思路是设置两个数据源和两个索引,对很少更新或根本不更新的数据建立主索引,而对新增文档建立增量索引。在上述例子中,那个量大的已经归档的帖子们放在主索引中,而每天新增的1000个帖子放在新增索引中。增量索引更新的频率可以非常快,而文档可以在出现几分钟内就可以检索到。

比如商城中,后添加的商品在sphinx中无法搜索出来,必须要重新创建索引。如果数据量非常大,有10000000件商品,每天新添加100个商品,每天都要为所有的商品重新索引。

实现步骤

1.        创建一张表

用于保存已经创建好索引的商品的最大id。根据这个id找出哪些商品没有创建索引。每次创建好索引后把最大的id存进去,定期取出新的商品创建新增索引,创建完新的索引再把最大的id更新到这个表。

create tablees_sphinx_id(

         id mediumint unsigned not null default'0' comment '已经创建好索引的最后一件商品的id'

)engine=innodbdefault charset=utf8 comment 'sphinx';

insert intoes_sphinx_id values(0);

2.        修改sphinx配置文件

创建好索引后把已经建好的最后的商品id更新到这个表。

#源定义,为eshopphp数据库中的es_goods表为数据源

source goods

{

    type                    = mysql



    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                 = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #主查询,要为哪些数据建索引就使用一条sql语句把这些数据取出来

    #第一个字段一定要求是id

    #为所有的商品的goods_name,goods_desc,attr_value这三个字段创建全文索引

    sql_query       = SELECT a.id,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value)attr_value  FROM es_goods a left joines_goods_attr b on a.id=b.goods_id group by a.id

    #在创建好索引后最后一个商品id更新到表中

    sql_query_post          = update es_sphinx_id set id= (selectmax(id) from es_goods where is_on_sale='是')

}

关闭sphinx,重新创建商品索引。再启动sphinx服务。

3.        修改配置文件,为后添加的没有创建索引的数据生成一个增量索引

在配置文件中为增量索引创建数据源和索引文件的配置

#后添加的还没有索引的数据的数据源

source goods_new

{

    type                    = mysql

    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #取出后添加的还没有建索引的商品

    sql_query               = SELECTa.id,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value) attr_value  FROM es_goods a left join es_goods_attr b ona.id=b.goods_id where a.is_on_sale='是' and a.id > (select id from es_sphinx_id) group by a.id

    #在创建好索引后最后一个商品id更新到表中

    sql_query_post          = update es_sphinx_id set id= (selectmax(id) from es_goods where is_on_sale='是')

}



#定义新创建的数据源的索引文件

index goods_new

{

    source          = goods_new             #对应的source名称

    #生成的索引文件存放的目录\索引文件的名字,不要中文路径,下面最后的goods为索引文件的名字

    path            =D:\002php\shpinx\var\data\goods_new

    docinfo         = extern

    mlock           = 0

    morphology      = none

    min_word_len    = 1

    html_strip      = 0



    #中文分词配置,中文分词词库文件所在目录,详情请查看:http://www.coreseek.cn/products-install/coreseek_mmseg/

    #charset_dictpath = /usr/local/mmseg3/etc/#BSD、Linux环境下设置,/符号结尾

    #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...

    charset_dictpath = D:\002php\shpinx\etc

    #shpinx只支持utf-8

    charset_type     = zh_cn.utf-8

}

4.        编写脚本处理增量索引

先为增量索引生成索引文件;把新生成的增量的索引文件合并到主索引文件。

windows系统是bat脚本,*.bat。

linux系统的是shell脚本,只要有可执行的权限就可以执行。

比如在项目的根目录下创建updateNewSphinxGoods.bat

D:\002php\shpinx\bin\indexer.exe  -c D:\002php\shpinx\sphinx.conf goods_new

D:\002php\shpinx\bin\indexer.exe  -c D:\002php\shpinx\sphinx.conf --merge  goods  goods_new --rotate

--rotate是为了便于在sphinx服务器运行期间执行合并的命令,否则goods被锁无法执行。

双击脚本执行。为了便于关于观察执行过程和结果,也可以拖到cmd黑窗口执行。

添加了新的商品之后只要执行这个脚本就可以在sphinx中搜索到了。但是每次都手动执行这个脚本太麻烦了,可以配置这个脚本每隔一段时间自动执行一次。

5.        配置脚本自动执行

如果是windows系统,windows—>管理工具--> 任务计划程序-- >创建基本任务,创建完成后在右键这个任务,选择属性,点击触发器,新建,设置为一次,重复任务间隔为5分钟,持续时间为无限期。

如果是linux系统,配置crond进程。

四、修改数据库数据时更新sphinx

当修改了数据库中的数据时,sphinx中已经生成的索引并没有更新,所以使用修改前的关键字还是能搜索出可能不符合条件的商品,这就需要在修改商品时把sphinx中的索引也一起更新。

思路是,只能为修改了的商品重新创建索引并合并到主索引上,在合并之前先删除原索引。

1.        在创建索引时必须找出该修改了的数据。

修改商品表添加一个字段,is_updated,默认值是0,一旦被修改了就设置为1。然后定期让sphinx把is_updated=1的重新创建索引。

is_updated  tinyint  unsigned  not  null  default '0'  comment  '是否被修改',

然后,修改商品模型,当修改商品时,将这个字段更新为1。

2.        修改sphinx的配置文件,为修改的商品重建索引

#后添加的还没有索引的数据或者修改过的商品的数据源

source goods_new

{

    type                    = mysql

    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #取出后添加的还没有建索引的商品

    sql_query               = SELECTa.id,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value) attr_value  FROM es_goods a left join es_goods_attr b ona.id=b.goods_id where a.is_on_sale='是' and (a.id > (select id from es_sphinx_id) or a.is_updated=1)group by a.id

    #在创建好索引后最后一个商品id更新到表中

    sql_query_post          = update es_sphinx_id set id= (selectmax(id) from es_goods where is_on_sale='是')

   

}

3.        修改搜索的php代码让sphinx先过滤掉被标记为修改的,只放行is_updated为0的

    //过滤掉被标记为修改的

    $sph->SetFilter('is_updated', array(0);

4.        然后在sphinx中定义is_updated为一个属性

首先要取出这个字段,然后才可定义为一个属性。同时将获取is_updated段时将这个字段固定为0。

主索引和增量索引都要添加这个属性,否则在merge主索引和增量索引时将失败。

#########################主索引的数据源与索引配置#######################

#源定义,为eshopphp数据库中的es_goods表为数据源

source goods

{

    type                    = mysql



    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #主查询,要为哪些数据建索引就使用一条sql语句把这些数据取出来

    #第一个字段一定要求是id

    #为所有的商品的goods_name,goods_desc,attr_value这三个字段创建全文索引

   sql_query               = SELECTa.id,0 is_updated,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value)attr_value  FROM es_goods a left joines_goods_attr b on a.id=b.goods_id where a.is_on_sale='是' group by a.id

    #在创建好索引后最后一个商品id更新到表中

    sql_query_post          = update es_sphinx_id set id= (selectmax(id) from es_goods where is_on_sale='是')



    #把一个字段的值定义为一个属性,属性可以用来过滤、排序

    sql_attr_uint           = is_updated

}



#后添加的还没有索引的数据或者修改过的商品的数据源

source goods_new

{

    type                    = mysql

    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #取出后添加的还没有建索引的商品

   sql_query               = SELECTa.id,0 is_updated,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value)attr_value  FROM es_goods a left joines_goods_attr b on a.id=b.goods_id where a.is_on_sale='是' and (a.id > (select id from es_sphinx_id) or a.is_updated=1)group by a.id

    #在创建好索引后最后一个商品id更新到表中

    sql_query_post          = update es_sphinx_id set id= (selectmax(id) from es_goods where is_on_sale='是')

    #把一个字段的值定义为一个属性,属性可以用来过滤、排序

    sql_attr_uint           = is_updated

}

由于修改了配置文件,所以要重建索引。

D:\002php\shpinx\bin>indexer.exe  -c D:\002php\shpinx\sphinx.conf goods --rotate

5.        然后,在商品模型中,修改一件商品后要把sphinx中这件商品的is_updated属性设置成1。

//设置sphinx中的这条记录的is_updated属性为1

//搜索sphinx

require('./sphinxapi.php');

$sph = new\SphinxClient();

$sph->SetServer('localhost',9312);

//把id=$id这件商品的is_updated属性更新为1

$sph->UpdateAttributes('goods',array('is_updated'),array($id=>array(1)));

6.        删除旧的索引

旧的索引仍然在sphinx的索引文件中,应该定期的把is_updated=1的删掉。删除旧的索引时需要使用indexer.exe   --merge dst  src  --merge-dst-range  [attr] [min]  [max]  [attr] [min]  [max]命令。这个命令的意思是合并dst索引和src索引,在合并前先过滤dst索引中属性值在min和max(包含max)之间的索引,这个过滤条件可以设置多个。由于必须需要两个索引合并才能实现对dst索引的过滤,也就是案例中对goods索引的过滤,以实现删除is_updated值为1的旧索引。所以,创建要删除的索引的数据源和索引配置 goods_del。为了保证合并时不报错,goods_new索引文件的字段要和goods的保持一致。创建goods_del的数据源和索引配置时需复制goods的。配置时将is_updated固定为1,以便合并后能够将要删除的索引在goods主索引中标记出,即其is_updated为1。由于- -merge-dst-range和- -merge的执行可能是异步的。为了保证过滤的成功,再合并goods_new索引时进行过滤。

#########################要删除的商品的数据源与索引配置#######################

#要删除的商品的数据源

source goods_del

{

    type                    = mysql

    sql_host                = localhost

    sql_user                = root

    sql_pass                = 123456

    sql_db                  = eshopphp

    sql_port                = 3306

    sql_query_pre           = SET NAMES utf8

    #取出后添加的还没有建索引的商品

    sql_query               = SELECT a.id,1 is_updated,a.goods_name,a.goods_desc,GROUP_CONCAT(b.attr_value)attr_value  FROM es_goods a left joines_goods_attr b on a.id=b.goods_id where a.is_on_sale='是' and a.is_updated=1 group by a.id

   

    #把一个字段的值定义为一个属性,属性可以用来过滤、排序

    sql_attr_uint           = is_updated

}



#定义新创建的数据源的索引文件

index goods_del

{

    source          = goods_del             #对应的source名称

    #生成的索引文件存放的目录\索引文件的名字,不要中文路径,下面最后的goods为索引文件的名字

    path            =D:\002php\shpinx\var\data\goods_del

    docinfo         = extern

    mlock           = 0

    morphology      = none

    min_word_len    = 1

    html_strip      = 0



    #中文分词配置,中文分词词库文件所在目录,详情请查看:http:src="__PUBLIC__/Home/images/www.coreseek.cn/products-install/coreseek_mmseg/

    #charset_dictpath = /usr/local/mmseg3/etc/#BSD、Linux环境下设置,/符号结尾

    #Windows环境下设置,/符号结尾,最好给出绝对路径,例如:C:/usr/local/coreseek/etc/...

    charset_dictpath = D:\002php\shpinx\etc

    #shpinx只支持utf-8

    charset_type     = zh_cn.utf-8

}

7.        然后修改脚本文件,加入合并时的过滤条件

::--merge-dst-range表示在合并时过滤,但是合并时未必已经将is_updated为1的商品合并到goods索引主表中

::所以可能是先过滤了然后合并的,这导致没有将is_updated为1的商品索引从goods索引中过滤掉,为了妥善

::分开执行,先将要过滤的商品索引一起合并到goods主索引,然后在后面合并goods_new时做过滤,删除is_updated为1的索引

::合并时只保存is_updated=0其他的属性值的索引都过滤掉

D:\002php\shpinx\bin\indexer.exe  -c D:\002php\shpinx\sphinx.conf goods_del  --rotate

D:\002php\shpinx\bin\indexer.exe  -c D:\002php\shpinx\sphinx.conf --merge  goods  goods_del --rotate

D:\002php\shpinx\bin\indexer.exe  -c  D:\002php\shpinx\sphinx.conf  goods_new --rotate

D:\002php\shpinx\bin\indexer.exe  -c D:\002php\shpinx\sphinx.conf --merge  goods  goods_new --merge-dst-range is_updated  00  --rotate

由于sphinx的脚本文件的执行是在内存中执行,又在磁盘中执行的,所以,这个执行的结果需要根据实际生产环境中服务器的运行情况,等待一定时间才能见效,并不会执行了代码就立即生效,因为后台可能在做这个脚本的执行中的数据上的传输与存储。

 类似资料: