阿龙的自留地

兼济天下则达 独善其身则穷

标签归档: Klist

用Sphinx提供的klist处理文章更新和删除


在前面的介绍中,都没有处理更新和删除问题,这里有必要说说。在关于sphinx引擎的一些想法中说过公司所用的引擎中,处理更新和删除的办法是在索引中增加一个属性来标志这条记录是否失效,每次做增量时,就要去主索引和增量索引中更改相应id的属性值,这确实可以解决问题。不过并不是一个很好的解决办法,Sphinx的作者也说过这种方法既麻烦又容易出错。既然有更新和删除这个需求,必然会提供解决的办法,这个办法就是kilst。所谓的klist,就是kill list,按照字面理解,就是删除列表。我们只需要在增量索引中保存一个id列表,搜索时,如果在主索引中搜到相关文档,而文档的id存在于增量索引的id列表中,则这个文档将被丢弃。

这里有一个需要注意的是,当文章被删除时,仅仅通过增量抓取,在增量索引中并不能知道主索引中哪一个文档被删除了,所以这就必须在表中文档被删除时,能够记录下被删除的id,这就需要用到触发器,也需要建立一个辅助表来保存这些id。辅助表的建立如下:

create table sphinxklist(
        id integer not null,
        ts timestamp not null
);

触发器的建立如下:

DELIMITER //
CREATE TRIGGER sphinx_kill
AFTER DELETE ON wp_posts
FOR EACH ROW
BEGIN
        INSERT INTO sphinxklist VALUES (OLD.ID, NOW());
END
//

有了这些准备工作后,我们就可以使用klist了,事实上在之前的配置文件的基础上,只需要修改一点点内容就好了。首先修改主索引

source srcmain : base{
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = UPDATE sphinx_helper SET main_tmp_maxts=NOW() WHERE appid='blog_search';
        sql_query = \
                SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                        post_status='publish' AND post_modified < (SELECT main_tmp_maxts FROM sphinx_helper WHERE appid='blog_search');
        sql_query_post_index = UPDATE sphinx_helper SET main_maxts=main_tmp_maxts WHERE appid='blog_search';
        sql_query_post_index = DELETE FROM sphinxklist WHERE ts < (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search');
        sql_attr_timestamp = post_modified
        sql_field_string = post_title
}

可以看到,相对于之前的配置,这里只添加了一行 sql_query_post_index = DELETE FROM sphinxklist WHERE ts < (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search'); 添加这行是为了防止之前运行引擎时留下的id再次被使用。 之后修改临时索引:

source srcdelta_temp : srcmain {
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET SESSION query_cache_type=OFF
        sql_query_pre = SET @maxtsdelta:=NOW();
        sql_query_pre = UPDATE sphinx_helper SET delta_tmp_maxts=@maxtsdelta WHERE appid='blog_search';
        sql_query = SELECT ID, post_title, post_content, UNIX_TIMESTAMP(post_modified) AS post_modified FROM wp_posts WHERE \
                post_status='publish' AND post_modified &gt;= (SELECT main_maxts FROM sphinx_helper WHERE appid='blog_search')\
                AND post_modified &lt; @maxtsdelta;
        sql_query_killlist = SELECT ID FROM wp_posts WHERE post_modified &gt;= (SELECT main_maxts FROM sphinx_helper WHERE \
                appid='blog_search') AND post_modified &lt; @maxtsdelta UNION SELECT id FROM sphinxklist;
        sql_query_post_index = UPDATE sphinx_helper SET delta_maxts=delta_tmp_maxts WHERE appid='blog_search';
}

也只是添加了一行,也就是将这次抓取的id与sphinxlist中的id合并。 之后还需要修改Shell脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
baseDir=/home/long/sphinxforchinese/blog_search
conf=$baseDir/etc/blog_search.conf
binDir=$baseDir/bin
cd $binDir
while [ true ]
do
        #./indexer -c $conf --merge-klists --rotate --merge delta deltaTemp
        ./indexer -c $conf  --merge-klists --rotate --merge delta delta_temp
        if [ "$?" -eq "0" ]; then
                cat $baseDir/script/post_merge.sql | mysql -u root --password=123456 blog
                ./indexer -c $conf --rotate delta_temp
        fi
        sleep 60
done

这个脚本相对于原来的只增加了--merge-klists这个参数,这个参数的意义是,将delta_temp合并到delta时,并不会删除delta的klist,而是将delta_temp的klist和delta的klist合并,这正是我们想要的。经过这样的变化,一个可以处理更新和删除的main+delta索引就建好了。

感谢Sphinx团队,感谢Sphinx-for-chinese团队,给我们提供了一个这么好用的开源引擎。

搭建Sphinx-for-chinese引擎时遇到的问题


关于sphinx引擎的一些想法说过用Sphinx给同事搭引擎,可是那是建立在之前的配置文件之上,我只要依葫芦画瓢,改一改路径以及查询语句就搞定了,实质上没学到什么东西。在我看来,要想真正了解它,还是得重新造轮子,从头到尾自己搭一遍,在这个过程中出现了许多奇怪的错误,在这里记录一下。

1.checking for clock_gettime in -lrt... 这是我遇到的第一个问题,事实证明,这根本不是问题。到Sphinx-for-chinese下载了编译包,开始编译,之后就卡在了这里。刚开始以为是缺少librt,然而我在lib中找到了这个链接库。将编译包放在其它机器上编译,又是可以通过的,百思不得其解。只好到Sphinx-fro-chinese的QQ群里发问,黑猫给出解答是要将librt所在路径加入到etc/ld.so.conf,并运行ldconfig命令。按照他的办法,结果运行ldconfig命令时卡住了,于是可以断定是机器的问题。

2.ERROR: cannot find MySQL include files.

这个问题比较好解决,就是缺少MySQL的库文件。因为虚拟机装的是Ubuntu,只要运行以下命令就好了。

sudo apt-get install libmysql++-dev libmysqlclient15-dev checkinstall

如果是其它系统,相信也是类似的方法。如果已经有库文件了,则只需要将路径加入到/etc/ld.so.conf中,并执行ldconfig命令

3.index 'test1': search error: query too complex, not enough stack (thread_stack=1217498K or higher required).

这也是一个很奇怪的错误。我是按照文档中给出的例子建好索引,之后用命令行工具,也就是search要搜索的,结果就出现了这个错误。在网上搜索这个错误,没找到有用的信息,于是又求助于Sphinx-for-chinese群,群里的人说是因为命令行存在问题,用客户端搜就没问题。于是用客户端搜果然没问题,可是我还是无法释怀,因为之前公司的引擎中,用命令行是没有问题的。于是对照着公司用的引擎中的配置文件,发现配置文件中没有这一行,在自己的配置文件中注释掉这行后,果然没问题了。

所以对于这个错误的解决办法就是,将sql_query_info = SELECT * FROM documents WHERE id=$id这行注释掉.

这个确实太坑人了,连官方的配置文件都会出错,得浪费多少人的时间。

4.ERROR: index 'main': No fields in schema - will not index.

光运行例子是不行的,还是得自己写一些东西,于是将自己的博客文章来搜索。用了Wordpress中wp_posts表中的数据,我只用的四个字段ID,post_title,post_content,post_modified,将post_title,post_content定义成sql_attr_string,sql_attr_timestamp,结果就出现了这个错误。在网上找了,发现在官方bug报告中有提到这个问题

http://sphinxsearch.com/bugs/view.php?id=1632

管理员说,引擎中需要一个全文索引字段,否则就没有东西需要索引了,这样它就不会建索引。管理员建议定义为sql_field_string,这样就会对这个字段既索引又保存内容。对于我的配置,我并不想保存post_content这个字段,所以不想将它定义为sql_field_string,那怎样才能让它只被所以呢?看过文档之后,才知道默认情况下,是会被索引。这也是为什么,在上面的帖子中,将sql_attr_string = text注释掉就可以建索引了。所以我只能说管理员也没有真正理解这个错误的原因,看来不能迷信权威啊。

5.FATAL: there must be 2 indexes to merge specified

这个是在测试Klist的时,出现的。文档中说,当合并两个索引时,使用--merge-klists就可以将两个索引的klist合并,于是我在合并时加上了这个参数。具体如下:

./indexer -c $conf --rotate --merge --merge-klists delta deltaTemp

运行时就出现这个错误,我纳闷了,明明官方文档中说加入这个参数是没问题的。到网上找资料,有人是用--merge-killlists这个参数,试过之后,同样报这个错误。无奈之际,将--merge-klist参数放到--rotate前面,

./indexer -c $conf --merge-klists --rotate --merge delta deltaTemp

奇迹出现了,这次没有报错。我只能说,这真是个坑。

《Introduction to Search with Sphinx》写的还是非常不错的,毕竟是Sphinx的作者,表达能力和写作能力自然非同凡响,关于Sphinx的知识,许多都来自本书。等有时间了,可以将引擎的搭建过程写一写,应该可以帮助一些人。这次搭建过程,我学到了许多,虽然用的是开源的引擎,但真要从头到尾搭建一个引擎,并提供可靠的服务,并不是那么容易的,还是得多实践才行。