将sphinx配置为更好的实时搜索引擎

时间:2022-01-20 08:27:14

本文基于sphinx 2.2.11。


sphinx以它的简单、快速吸引着我。但它的plain增量索引有两大问题一直困扰着我:

一是增量索引只能定时创建,必须会造成数据到主索引时存在一个时间差,而且经测试,增量索引merge到主索引时还经常会被阻塞一段时间(--rotate);

二是在增量索引中无法对字符串属性进行修改UPDATE,所以发布的物品修改了名称就甭想搜索到了。


sphinx也实现了一个rt(实时)索引,可以在生产环境中使用。但是,它也有几个缺点:

1. 搜索性能差很多,见下图:

将sphinx配置为更好的实时搜索引擎

红色为rt索引,蓝色为plain索引。主要差距来自于rt实时索引在超出rt_mem_limit 之后就会将内存中的内容RAM CHUNK写入磁盘块DISK CHUNK,当多次写入磁盘块后,每次搜索的结果都需要从多个磁盘块中读取,然后再合并结果。当然这可以通过加大rt_mem_limit来解决:

将sphinx配置为更好的实时搜索引擎

2. 如果加大了rt_mem_limit,那么rt索引会在内存中保留大量数据,为了从崩溃中恢复,可能需要flush加binlog获取更好的恢复效果,然而大数据量flush时会有些卡顿。

3. 高频的DELETE/REPLACE会引起碎片,当然可以用OPTIMIZE命令来整理。

将sphinx配置为更好的实时搜索引擎

但OPTIMIZE不但需要大量IO,也需要大量CPU时间,因为它需要读取多个CHUNK,然后再合并结果。

4. 一旦修改了数据库表结构,那要修改rt索引中的数据也是比较麻烦的,需要手工修改。


综上所述,纯使用sphinx的rt索引还是比较娇贵的。我一直想把rt索引的实时和可修改字符串优势与plain索引的快速创建优势结合起来,经过不断查看官方文档,我找到了一个兼顾两者优点的方案。


简而言之就是:创一个每日定时任务,在夜里创建全量plain索引,然后将全量索引TRUNCATE,同时将plain索引ATTACH 到rt实时索引,随后只在rt索引中搜索和修改(在修改DB中的数据后)。


这样做的优点我们最后来看。下面先用实例来演示一遍---------------:

假设我们有一个求购表,叫ask_buy,简化的表结构为:

CREATE TABLE `ask_buy` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`title` VARCHAR(200) NOT NULL,
`deleted` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
)
COMMENT='求购'
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;

首先我们创建一个全量plain索引(片断):

source ask_buy_src
{
type= mysql

sql_host= localhost
sql_user= root
sql_pass= root
sql_db= taoquan
sql_port= 3306# optional, default is 3306

sql_query_pre= SET NAMES utf8
sql_query_pre = SET SESSION query_cache_type=OFF

sql_query= SELECT id, title, deleted FROM t_ask_buy

sql_attr_uint = deleted
sql_field_string = title
sql_query_post_index= REPLACE INTO search_counter SELECT 'ask_buy',MAX(id) FROM ask_buy

sql_ranged_throttle= 0
}

index ask_buy
{
type= plain
source= ask_buy_src

path= /usr/local/sphinx/data/ask_buy
docinfo= extern
dict= keywords
mlock= 0
morphology= none
min_word_len= 1

ngram_len= 1
ngram_chars= U+3000..U+2FA1F
html_strip= 0
}

index rt_ask_buy
{
type = rt
path = /usr/local/sphinx/var/data/rt_ask_buy
rt_mem_limit = 512M
rt_field = title
rt_attr_uint = deleted
}


然后创建一个脚本index.sh来创建plain全量索引并ATTACH到rt索引:

#!/bin/bash
/usr/local/sphinx/bin/indexer goods --rotate --all >> /usr/local/sphinx/var/log/index_rt.log
mysql -P 9306 -h 127.0.0.1 -e"TRUNCATE RTINDEX rt_ask_buy;ATTACH INDEX ask_buy TO RTINDEX rt_ask_buy;"

然后在每天夜里2点30创建:

30 2 * * * sudo ~/index.sh

最后来检验结果了:

优点:

1. 这样rt索引就无需定期flush到磁盘,因为即便它丢失了也可以直接进行重建,而且重建后还没有碎片。

2. 还测试了,如果修改了表结构,只要修改sphinx配置文件,然后重启searchd进程,最后再执行index.sh即可。

3. 如果sphinx崩溃,只需要再执行index.sh即可,无需从binlog或dump中恢复到rt索引,直接从数据源重建索引的速度是很快的:

将sphinx配置为更好的实时搜索引擎

4. 这种方案的检索速度比plain方案相当,因为全量索引的DISK CHUNK只有一个,而只要rt_mem_limit设置合理,每天新增的DISK CHUNK也不会很多,这样在一两个CHUNK之间合并,速度只比纯plain方案略低。参考下图的2048M(5+1),重点不在2048M,而在后面的5+1,即5个DISK CHUNK+1个RAM CHUNK。

将sphinx配置为更好的实时搜索引擎