本文基于sphinx 2.2.11。
sphinx以它的简单、快速吸引着我。但它的plain增量索引有两大问题一直困扰着我:
一是增量索引只能定时创建,必须会造成数据到主索引时存在一个时间差,而且经测试,增量索引merge到主索引时还经常会被阻塞一段时间(--rotate);
二是在增量索引中无法对字符串属性进行修改UPDATE,所以发布的物品修改了名称就甭想搜索到了。
sphinx也实现了一个rt(实时)索引,可以在生产环境中使用。但是,它也有几个缺点:
1. 搜索性能差很多,见下图:
红色为rt索引,蓝色为plain索引。主要差距来自于rt实时索引在超出rt_mem_limit 之后就会将内存中的内容RAM CHUNK写入磁盘块DISK CHUNK,当多次写入磁盘块后,每次搜索的结果都需要从多个磁盘块中读取,然后再合并结果。当然这可以通过加大rt_mem_limit来解决:
2. 如果加大了rt_mem_limit,那么rt索引会在内存中保留大量数据,为了从崩溃中恢复,可能需要flush加binlog获取更好的恢复效果,然而大数据量flush时会有些卡顿。
3. 高频的DELETE/REPLACE会引起碎片,当然可以用OPTIMIZE命令来整理。
但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索引,直接从数据源重建索引的速度是很快的:
4. 这种方案的检索速度比plain方案相当,因为全量索引的DISK CHUNK只有一个,而只要rt_mem_limit设置合理,每天新增的DISK CHUNK也不会很多,这样在一两个CHUNK之间合并,速度只比纯plain方案略低。参考下图的2048M(5+1),重点不在2048M,而在后面的5+1,即5个DISK CHUNK+1个RAM CHUNK。