sphinx 实时更新笔记

作者: | 更新日期:

之前搭建了采用定时增量更新索引的方式搭建了一个sphinx搜索服务,最近需要改成实时更新索引,简单记录一下。

本文首发于公众号:天空的代码世界,微信号:tiankonguse

背景

sphinx 主要实现的功能是快速的进行全文检索。

什么是全文检索呢?

假设我们使用sql数据库, 要查询字符串, 只能使用 like 语句, 这个查询的还只能是子串。
如果我们想像 google 那样的搜索该怎么实现呢?

这个时候就需要全文检索派上用场了。

而 sphinx 就提供这样一个全文检索的功能。

我们只需要把自己关心的字符串传进入,他就会返回相关的数据来。

你自己去 sphinx 官网学着搭建一个搜索服务来, 慢慢的遇到的问题多了, 你就会逐渐的改造sphinx。
再往后你看看网上的那几篇千万级数据全文检索和亿级数据搜索引擎相关的文章,发现那上面说的也不过如此。
好了,不多说了,下面简单那的来了解一下 sphinx 吧。

基本使用方法

我这里不记录插件式的sphinx使用方法。
那种方法和数据库耦合太深,我没有去了解与实现那种方式。
这里只讨论使用数据库作为数据来源, 在外部创建索引,然后有个sphinx服务来提供搜索。

安装使用的过程中会遇到这么几个问题。

下面简单的讨论一下。

主键问题

使用过 sphinx 的人会发现, sphinx 的每条记录都需要有一个整形id作为主键。

但是我们的表有时候没有这个整形id主键(称为表A), 于是就派生出了另一种使用方法: 专门为sphinx设计一张表(称为表B)。

这样就需要一个同步程序把我们的表A的数据同步到表B.

增量索引

我们有了 sphinx 表之后, 就可以愉快的定时重新创建索引了。
但是当我们的数据量大之后, 发现创建索引是一件很慢的事情。

这个时候我们就想能不能只为新增的或者有变化的那部分数据创建索引呢?

sphinx 也提供了这个功能。

我们需要一个 全量索引 (mainIndex) 和 一个增量索引(IncIndex).

全量索引 只在启动的时候创建, 增量索引 定时执行。

增量肯定有一个时间为分界线的,所以我们这个分界线比较久远了,我们需要把增量索引合并到全量索引中去。

然后更新这个分界线为合并时候的分界线就行了。

数据字段变动

数据字段变动分两种情况。

一种是要当做key值进行搜索的字段,一种是当做返回数据返回的字段。

对于新增字段当做数据返回的话, 我们可以快速实现这个功能。
sphinx 搜索时会返回主键,且数量极少, 我们依靠这个主键可以二次查询数据库, 去查询任何我们想要的东西。

对于要当做key值进行搜索的字段,我们还真没有好的办法。
只好先添加字段,然后重启服务器了, 重建索引了。

分布式搜索

当我们的数据量逐渐增大时,我们会发现使用一台机器搜索会有点慢了。
于是想着能不能使用多台机器来分担一些大数据量的负担。

sphinx 也支持这个功能了。

多台机器组成分布式集群,我们搜索时可以像一台机器一样进行搜索。

集群对查询者是透明的, 即分布式集群会自动合并集群的数据,并删除重复项。

关于分布式的细节问题我这里就不细说了。

api接口查询

其实上面的都是数据层的东西。
数据储存,创建索引,数据变更等问题。

我们做了这些,要做的是能够去查询我们想要的数据。

所以 api 查询接口是必须要有的。

由于只是个网络接口,所以接口实现一个语言后,很容易在其他语言中实现对应的扩展接口。
所以我们可以理解为大部分常用语言都有这个接口了。

我们查询的时候只需要设置好对应的字段,然后使用接口传给 sphinx 服务, 查询结果瞬间就会返回回来的。

实时更新数据

一般,到现在为止,我们能够快速搜出我们的东西来。
但是这个不是实时搜索,而是准实时搜索。

意思就是新增或修改的数据,不会立马生效(毫秒级), 而是几分钟后才会生效。
根本原因是我们的增量索引是个定时程序。

这个时候我们就会想有没有真正的实时索引呢?
这个页面添加数据,跳转的新页面数据就搜索出来呢?

sphinx 也实现了这个功能。

这里简称为 RT 功能。
由于这个功能只简单的实现了,然后后续版本就没有发不过,所以现在的 实时搜索不是很完善的。

这里就简单的罗列出sphinx自带的实时搜索的不足之处吧。

第一个不足是实时索引不能指定数据源的。

什么意思呢?
由于要实时,sphinx是不能实时监听数据源的变更或增加的,所以没有数据源了。

没有数据源怎么办呢?
数据一个一个使用添加函数加进入。
我们一般现有的数据就是百万级的, 一个一个来代价有点大。
不过考虑到只执行一次,还是可以勉强接受的。

向上帝祈求不要新增字段,服务不要挂,机器不要挂吧!

第二个不足是数据量大的时候,性能不好。
这个?
不做评论。

第三个不足是更新数据只能使用 replace .
换一句话说就是更新数据不能使用条件。
不能使用条件怎么更新数据呢? 只好依靠主键了。

增量索引下实时更新

我们看到, 使用sphinx的实时更新有点不靠谱。
于是我们只好使用sphinx的增量索引方式了。
在这个方式下,我们能指定文档ID来更新数据吗?

看了sphinx文档,发现sphinx还真提供了一个实时更新的文档接口。

不过由于需要指定文档ID, 所以我们只能进行实时更新数据,不能实时增加数据。

第二个问题是我们实时更新后,索引数据将会和数据源的数据不一致了。

这里就存在一个问题:我们先更新数据源,在更新内存的索引,后来增量索引执行时,会更新这个索引吗?

什么意思呢?

假设一个数据, A修改的时候我们希望实时更新,于是采用上面的策略。B在后来又更新这条数据,然后采用增量索引的方式更新数据。
这时就会出现数据不一致问题, 索引会采用哪个数据呢?

比较理想的结果应该是后来的数据为准,这样的话也是我们期望的。

为什么我这里会有不确定呢?
因为文档上没有说这种情况。

而且文档上还说下面这句话。

They are very fast because they're working fully in RAM, but they can also be made persistent: updates are saved on disk on clean searchd shutdown initiated by SIGTERM signal. 

简单的理解就是这个更新数据时在内存中完成的,更新后的数据还在内存中,只有sphinx服务关闭的时候才会落实到磁盘上。

这里就会产生很多疑问的。

我的sphinx服务一般一跑就不知道多少月了,都储存在内存中可能吗?
增量索引来了, 数据和原先实时索引的数据不一样,会怎样?增量索引也到内存中吗?

想到这,这里引出一个问题:是不是索引本身就是全内存型的。

如果是全内存的话, 上面的问题与疑问都没有了。

但是我们的索引文件很大的,全内存可能性也不大了。
这个问题以后继续查资料吧。

另一个实时更新疑问是 sphinx 提供的 REPLACE 函数。

sphinx 的 SphinxAPI 文档中由这个几句话。

Everything available via the SphinxAPI is also available SphinxQL but not vice versa; 

这句话的意思大概可以理解为使用 SphinxAPI 能正常使用的功能, 使用 SphinxQL 也可以的。反之不行。

然后看看下面的 UPDATE 语法。

PDATE statement was added in version 2.0.1-beta.   
It can currently update 32-bit integer attributes only.   
Multiple attributes and values can be specified.   
Both RT and disk indexes are supported.   
Updates on other attribute types are also planned.

简单的说就是 sphinx 支持实时更新32位整数字段,其他类型还在计划中,由于好多年还没有出下个版本,所以这里我们可以理解为使用 UPDATE 只能更新整数就行了。

我们想要的事实时搜东西,这个搜一般都是文本的,所以还是不能满足我们的日常生活的。

最后我们看到 INSERT 和 REPLACE 的文档在一个小节中。

INSERT statement, introduced in version 1.10-beta, is only supported for RT indexes.   
It inserts new rows (documents) into an existing index, with the provided column values.
ID column must be present in all cases.   
Rows with duplicate IDs will not be overwritten by INSERT; use REPLACE to do that.

上面的意思大概是 INSERT 只能在 RT索引中使用, 如果 文档ID已存在,不会覆盖就的数据。
紧接着来了一句请使用 REPLACE 做这件事。

然后这就是 REPLACE 的所有文档了。

第一个疑问就是 REPLACE 支持在增量索引中使用吗?
如果不支持,我们完全不关心它会怎样,但是文档没说支持不支持。

如果支持的话,我们是不是就可以用这个来实现实时更新了。
对于这种情况, 我还是什么时候自己测试一下吧。

还是那句话, 如果支持, 我们则可以快速的实现有增量索引的实时搜索。
实时搜索仅提供快速更新关键数据(比如标题更改), 而增量索引定时更新全量变更数据。

如果不支持, 我们只能实时更新整形属性数据。

关于 sphinx 的就是这些了。

本文首发于公众号:天空的代码世界,微信号:tiankonguse
如果你想留言,可以在微信里面关注公众号进行留言。

关注公众号,接收最新消息

tiankonguse +
穿越