Sphinx 被删除的索引怎么又出来啦?

󰃭 2016-05-29

场景

按照之前的创建的索引, 我们已经基本进行搜索了, 但这个时候, 我们会发现, 有些已经被删除(标记)的索引, 为何在我们的搜索中还会出现呢, 甚至说已经被更新了状态的文档, 却搜到了老的状态文档

原因解析

我们使用增量的方式创建了索引, 假设是maindelta, 索引查询的时候指定的索引顺序是main delta, 这样索引查询时会先在delta中查询文档, 如果没有的话, 就会再到main 中查找。

一般情况下, 我们只是新增文档, 新增的这部分就在delta 中了, 老的则还在main 里, 如果我们只新增, 不更改状态(包括标记删除), 那么我们可以正常的搜索, 不会有问题, 可一旦有在main中的数据状态更新了,且新的状态文档在delta中,那我们搜索这条文档的时候就会出现问题, 老的状态就可能被搜出来(包括标记删除)

解决方式

索引创建时, 在sphinx相关索引的source中配置 sql_query_killlist

sql_query_killlist会创建一个文档id列表,与某个索引项绑定在一起,用于隐藏从另一个索引中出现的结果,比如上面, 在delta中设置这个参数, 那么这部分冲突的数据就会只在delta中查找, 而忽略main 中的

实践

首先我们把之前的索引配置修改一下, 把 is_del 这个状态值加到索引中

  1. 修改articlearticle_deltasource, 把is_del 添加到sql_query
  2. articlesource中设置 sql_attr_uint = is_del

修改部分如下

source article:src_db{
    sql_query = select id,title, content, cid, is_del, unix_timestamp(ctime) ctime,\
                unix_timestamp(mtime) mtime from article

    sql_query_post = replace into sysconfig (varname, info, value) select \
                     "article_delta","article delta time node", max(mtime) from article

    sql_attr_uint = cid
    sql_field_string = title
    sql_field_string = content
    sql_attr_timestamp = ctime
    sql_attr_timestamp = mtime
    sql_attr_uint = is_del
}

source article_delta:article
{
    sql_query = select id,title, content, cid, is_del, unix_timestamp(ctime) ctime,\
                unix_timestamp(mtime) mtime from article where mtime >= \
                (select value from sysconfig where varname = "article_delta")
    sql_query_post = SET NAMES UTF8
}

这里修复一下以前的一个算是bug的地方, 在article_delta 中, 我们要重置sql_query_post, 否则delta中也会继承mainsql_query_post, 这样就会导致第二次delta创建数据为空了(因为每次增量创建时,增量的时间起始点应该是不变的)

然后重建所有的索引即可

然后我们来重现一下开始的场景

场景重现

首先, 我们的索引里有且仅有一个文档包含了关键字全是红包啊, 我们将其搜索出来, 并要求是未删除的is_del=0

python test.py -p 3316 -i "article article_delta" -f "is_del" -v 0 "全是红包啊"

结果如下

1. doc_id=3, weight=6, title=全是红包啊, content=<p>都在发红啊<img width="530" height="340" src="http://api.map.baidu.com/staticimage?center=116.404,39.915&zoom=10&width=530&height=340&markers=116.404,39.915"/> </p>, cid=1, is_del=0, ctime=2015-12-23 08:15:44, mtime=2016-05-30 00:44:11

因为标题里有这个关键字了,为求简单,我们都把结果中的content字段去除

1. doc_id=3, weight=6, title=全是红包啊, cid=1, is_del=0, ctime=2016-05-30 00:44:11, mtime=2016-05-30 00:39:21

然后, 我们将原始数据(mysql)的这条文档的is_del值做修改, 刷新mtime, 并重建delta索引, 然后再执行上面的搜索

python test.py -p 3316 -i "article article_delta" -f "is_del" -v 0 "全是红包啊"

我们依然能搜结果,即便是已经被删除了的, 但时间(mtime)却是旧的索引时间

1. doc_id=3, weight=6, title=全是红包啊, cid=1, is_del=0, ctime=2015-12-23 08:15:44, mtime=2016-05-30 00:42:48

这样,我们就把场景重现出来了

sql_query_killlist 设置

现在, 我们来设置这个参数, 试试能不能达到预期

article_deltasource 中设置

sql_query_killlist = select id from article where mtime > (select value from \
        sysconfig where varname = "article_delta")

这样, 所有的增量文档的id 就能在 sql_query_killlist了, 增量数据也就不会与老的数据冲突了

配置好后,把所引发服务重启, 并重建articlearticle_delta 索引(索引配置修改了)。

为了后面验证这个设置,我们把在重建索引之前, 把数据恢复一下, id=3is_del 设置为 0

验证

依次执行场景重现的步骤, 我们发现, 第二次的查询中, 我们就不会把已经删除的数据查出来了.

我们把is_del=0 这个过滤给去除, 查询结果发现,is_del 真的已经变成1(已删除)了, 而且时间是新的(mtime)

python test.py -p 3316 -i "article article_delta" "全是红包啊"
1. doc_id=3, weight=6, title=全是红包啊, cid=1, is_del=1, ctime=2015-12-23 08:15:44, mtime=2016-05-30 00:48:49