这几天都要被Sphinx搞死了。先是发现用API长连接查询的话在建索引时可能会出现错误,可是当自己写了一套索引切换的系统出来后发现丫的合并索引总是不正确的。找了半天没发现哪儿错了,直到最后才想起来可能是把合并命令脚本化之后导致的错误。
先说一下我使用的索引策略。Sphinx里面如果想做到尽量实时搜索的话,完整建索引肯定是不行的,速度太慢了。Live search的功能还在开发中,现在的release版本里并没有这项功能。为了尽量做到实时,目前只能使用增量索引的方式。在sphinx的官方文档上 提供的方案,其实并不适合数据增长过快的用例,因为增量索引并不进行合并,所以时间长了以后,重建增量索引的时间就会变得无法接收。所以目前为了保证数据 能够尽快被搜到,每次只索引新加的数据,在建立了这个增量索引之后,立刻合并到主索引上去,这样来减少增量索引的耗时。
使用这个方案,在更新的时候只需要执行如下几条命令就可以了:
indexer delta --rotate indexer --merge main delta --rotate
为了结果尽量实时,直接把这两条命令写到脚本里,然后通过cron来定时更新。这看起来好像没什么问题,但是事实上会出现索引出错的情况。比如main里 面有6000条数据,合并4000条上去,怎么算都应该有个10000条吧?可是实际情况是合并完以后总文档数远远小于10000条。可是在命令里单独输 入这两条命令,结果却一直是正确的。怎么回事?难道说这玩意儿都不能脚本化了?
仔细研究了Sphinx建索引过程后发现,如果在建索引时加入了--rotate
参数的话,indexer会做两个事情,
一是建立后缀名为.new.typ的新索引,然后发送SIGHUP给searchd通知其滚动索引,然后就退出了。滚动索引是干嘛呢,大体上就是让
searchd首先释放旧索引,然后用那些新建的索引文件覆盖掉旧索引,最后重新读取新索引。这个过程是在searchd的进程内完成的,和
indexer没有任何关系,所以如果我们执行如下命令:
indexer delta --rotate; ls -l /delta.sp*
我们会发现在indexer进程退出以后,delta索引并没有立刻得到更新。在这样的情况下,由于这时的delta还是旧的数据,如果在脚本里执行合并的话,只是在做合并旧的数据而已,自然索引会出错了。
知道了问题所在就好办了,定义如下的bash函数:
idxdir=/data function wait_rotate() { while [ 1 ] do sleep .1 ls $idxdir$1.new.* >/dev/null 2>$1 if [ $? -ne 0 ]; then break fi done }
然后在更新索引的脚本里这样写就可以了:
indexer delta --rotate wait_rotate delta indexer --merge main delta --rotate wait_rotate main
这样改进脚本后果然解决了问题。