使用sphinx search打造你自己的中文搜索引擎

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: Google搜索引擎建立至今已经快20年了,之后全球各类大大小小类似的搜索引擎也陆续出现、消亡。国内目前以百度为大,搜狗、360、必应等也势在必争。搜索引擎技术也发展的相当成熟,同时也就出现了很多开源的搜索引擎系统。

Google搜索引擎建立至今已经快20年了,之后全球各类大大小小类似的搜索引擎也陆续出现、消亡。国内目前以百度为大,搜狗、360、必应等也势在必争。搜索引擎技术也发展的相当成熟,同时也就出现了很多开源的搜索引擎系统。比如,Solr、Lucene、Elasticsearch、Sphinx等。

本文以sphinx search为例来介绍如何打造自己的搜索引擎。该搜索引擎的架构大致如下:
Screenshot_from_2018_09_09_14_58_55_266x300

Sphinx search

Sphinx search 是俄罗斯人用C++写的,速度很快,可以非常容易的与SQL数据库和脚本语言集成,内置MySQL和PostgreSQL 数据库数据源的支持。其官方网站是: http://sphinxsearch.com/

可以说Sphinx支持包括英文、中文等所有语言的搜索。英文是以空格、标点符号来分割单词的,很容易切分。而中文词汇之间是没有空格的,很难区分,所以才有了自然语言处理中的“中文分词”技术的研究。Sphinx默认把中文按字拆分的,但这样就会产生搜索出不相干的内容来。比如,搜索“中国”,它会把同时包含“中”和“国”但不包含“中国”的文档搜出来。因此,有人就给Sphinx打了中文分词的补丁。

如果没有搞错的话,最早添加中文分词的是Coreseek,好像也是中文圈用得最广的支持中文分词的Sphinx,其它还有sphinx-for-chinese。然而这二者基于的Sphinx版本都太低了,有好多年没有更新。其中存在的一些Sphinx的bug也没有解决。

github上有一个基于Sphinx 2.2.9版本的代码库添加了中文分词: https://github.com/eric1688/sphinx 经测试,该版本稳定性和速度都要好于coreseek。当然它依然支持英文等其它语言的搜索,只是对中文搜索更加准确了。

Sphinx 安装

git clone https://github.com/eric1688/sphinx 
cd sphinx

#编译(假设安装到/usr/local/sphinx目录,下文同) 
./configure --prefix=/usr/local/sphinx
# 说明: --prefix 指定安装路径 --with-mysql 编译mysql支持 --with-pgsql 编译pgsql支持
make 
sudo make install

安装好后,在/usr/local/sphinx目录下有以下几个子目录:
etc/ sphinx配置文件,不同的索引可以写不同的配置文件
bin/ sphinx程序,其中有建立索引的程序:indexer, 搜索守护进程:searchd
var/ 一般用了放置indexer索引好的文件

Sphinx索引的建立

MySQL数据库表结构
从上面的架构图可以看出来,我们要搜索的数据都存放在MySQL数据库中。假设我们的数据库名称叫blog_data,其中有个表叫article,表结构如下:

字段名 说明
id 文章唯一id(主键)
title 文章标题
content 文章内容
created_time 文章创建时间
该article表可以是你本身网站的文本内容存放的表格,也可以是你的网络爬虫抓取到的数据存储表。

还有建立另外一个表sph_counter用来存储indexer已经索引的最大doc id

字段名 说明
counter_id 标记是对哪个表做记录
max_doc_id 被索引表的最大ID
note 注释,可以是表名
update_at 更新时间
建立索引配置文件:
新建或修改/usr/local/sphinx/etc/blog.conf 配置文件:

source blog_main
{ 
    type          = mysql
    sql_host      = localhost
    sql_user      = reader
    sql_pass      = readerpassword
    sql_db        = blog_data
    sql_port      = 3306
    sql_query_pre = SET NAMES utf8mb4
    sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id), 'article', NOW() FROM article

    sql_query     = SELECT id, title, content,  \
                    UNIX_TIMESTAMP(created_time) AS ctime, \
                    FROM article \
                    WHERE id <= (SELECT max_doc_id from sph_counter WHERE counter_id=1)
    sql_attr_timestamp = ctime  #从SQL读取到的值必须为整数,作为时间属性

}

index blog_main
{
    source       = blog_main #对应的source名称
    path         = /user/local/sphinx/var/data/blog_main
    docinfo      = extern
    mlock        = 0
    morphology   = none
    min_word_len = 1 
    html_strip   = 0 

    charset_type     = utf-8
    chinese_dictionary = /user/local/sphinx/etc/xdict  #中文分词的词典
    ngram_len = 0 
    stopwords        = /user/local/sphinx/etc/stop_words.utf8
}

#全局index定义
indexer
{
    mem_limit = 512M
}

#searchd服务定义
searchd
{
    listen          = 9900
    listen          = 9306:mysql41  # 实时索引监听的端口
    read_timeout    = 5 
    max_children    = 90
    max_matches     = 100000
    max_packet_size = 32M 
    read_buffer     = 1M
    subtree_docs_cache = 8M
    subtree_hits_cache = 16M 
    #workers        = threads•
    dist_threads    = 2 
    seamless_rotate = 0 
    preopen_indexes = 0 
    unlink_old      = 1 
    pid_file  = /usr/local/sphinx/var/log/blog_searchd_mysql.pid
    log       = /usr/local/sphinx/var/log/blog_searchd_mysql.log
    query_log = /usr/local/sphinx/var/log/blog_query_mysql.log
}

编辑好以上配置文件,就可以开始建立索引了:

cd /usr/local/sphinx/bin
./indexer -c ../etc/blog.conf

如果已经有searchd在运行了,就要加 --roate 来进行索引

索引建立后,就会在var/data/下面有名称前缀为blog_main.XXX的索引文件生成。

建立实时索引
上面的配置文件是建立一个静态索引,把当时数据库里面的所有数据进行索引。但是,你的数据库往往是不断增加新数据的。为了及时索引并搜索到最新加入的数据,就需要配置实时索引了。

index rt_weixin                                                                                                     {
    type = rt
    path = /usr/local/sphinx/var/data/rt_blog
    rt_field = title
    rt_field = content

    rt_attr_timestamp = pubtime
    ngram_chars = U+3000..U+2FA1F #为了支持中文
    ngram_len = 1
}

该仓库代码的作者可能是忘了给实时索引加中文分词,如果不配置ngram_chars 参数就不能搜到中文,添加后搜索是按单字匹配的,可见作者确实是忘了给实时索引部分加中文分词。

添加以上实时索引后并不能搜索到实时数据。实时索引的更新/添加只能通过SphinxQL(一个类似MySQL的协议),所以还要写一个Python脚本,从数据库读取最新的数据并通过SphinxQL更新到实时索引。

import MySQLdb
# 连接实时索引
db_rt = MySQLdb.connect(
    '127.0.0.1',
    'nodb',  # 对于实时索引来说,db,user,password都是不需要的,随便写。
    'noname',
    'nopass',
    port=9306,  # 实时索引监听的端口
)  

# 向实时索引更新数据的函数
def into_rt(index_name, item):
    cursor = db_rt.cursor()
    fields = item.keys()
    values = item.values()
    fieldstr = ','.join(fields)
    valstr = ','.join(["'%s'"] * len(item))
    for i in xrange(len(values)):
        if isinstance(values[i], unicode):
            values[i] = values[i].encode('utf8')
        elif isinstance(values[i], datetime):
            try:
                values[i] = int(time.mktime(values[i].timetuple()))
            except:
                traceback.print_exc()
                print values[i]
                values[i] = int(time.time())
    sql = 'INSERT INTO %s (%s) VALUES(%s)' % (index_name, fieldstr, valstr)
    # print sql
    sql = sql % tuple(values)
    try:
        cursor.execute(sql)
        db_rt.commit()
    except Exception, e:
        if e[0] == 1064:
            # ignore duplicated id error
            pass
        else:
            traceback.print_exc()
            raise 'realtime index error'
    finally:
        cursor.close()

以上是及时建立实时索引的python程序的主要部分。可以把它设置成后台一直运行的守护程序,也可以在crontab里面配置每隔几分钟运行一次。

索引的更新
静态的主索引如果只建立一次,实时索引的数据量会越积越多,对实时索引的搜索带来很大压力,所以我们要定时重新建立主索引,清理实时索引。
清理实时索引的程序可以参考上面建立实时索引的python程序。

crontab 设置每天凌晨1点运行 indexer
crontab 设置indexer运行完毕后清理实时索引,并从新的max_doc_id开始建立实时索引
以上就是建立一个自己的搜索引擎的过程。更多配置细节可到官方网站参考文档。

文章来源于:猿人学网站的python教程
版权申明:若没有特殊说明,文章皆是猿人学原创,没有猿人学授权,请勿以任何形式转载。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
自然语言处理 算法 搜索推荐
给全文搜索引擎Manticore (Sphinx) search 增加中文分词
Sphinx search 是一款非常棒的开源全文搜索引擎,它使用C++开发,索引和搜索的速度非常快,我使用sphinx的时间也有好多年了。最初使用的是coreseek,一个国人在sphinxsearch基础上添加了mmseg分词的搜索引擎,可惜后来不再更新,sphinxsearch的版本太低,bug也会出现;后来也使用最新的sphinxsearch,它可以支持几乎所有语言,通过其内置的ngram tokenizer对中文进行索引和搜索。
3850 0
|
11月前
|
SQL JSON 自然语言处理
白话Elasticsearch01- 结构化搜索之使用term query来搜索数据
白话Elasticsearch01- 结构化搜索之使用term query来搜索数据
267 0
|
11月前
|
存储 XML SQL
solr之Ik中文分析器的安装
solr之Ik中文分析器的安装
99 0
|
搜索推荐 中间件 Linux
一个基于EntityFrameworkCore+Lucene实现的全文搜索引擎库
这是一个仅70KB的、轻量级的全文检索搜索引擎、基于Lucene实现的。
121 0
一个基于EntityFrameworkCore+Lucene实现的全文搜索引擎库
|
自然语言处理 关系型数据库 MySQL
php sphinx 全文检索 中文分词
php sphinx 全文检索 中文分词
146 0
|
自然语言处理
拼音分词扩展elasticsearch-analysis-pinyin安装
拼音分词扩展elasticsearch-analysis-pinyin安装
179 0
拼音分词扩展elasticsearch-analysis-pinyin安装
|
SQL 自然语言处理 安全
PHP使用elasticsearch搜索安装及分词方法
为什么会用到这个ES搜索?
422 0
|
自然语言处理 PHP 索引
sphinx(七)sphinx匹配模式
Sphinx匹配模式 语法: $sp -> SetMatchMode(常量) SPH_MATCH_ALL 匹配所有词(默认) SPH_MATCH_ANY 匹配一个词 SPH_MATCH_PHRASE 匹配整一个词 SPH_MATCH_BOOLEAN 将查询看作一个布尔表达式 SPH_MATCH_EXTENDED 查询看做一个sphinx的表达式
219 0
sphinx(七)sphinx匹配模式
|
自然语言处理 索引
|
搜索推荐 Java Maven
学习笔记CB011:lucene搜索引擎库、IKAnalyzer中文切词工具、检索服务、查询索引、导流、word2vec
影视剧字幕聊天语料库特点,把影视剧说话内容一句一句以回车换行罗列三千多万条中国话,相邻第二句很可能是第一句最好回答。一个问句有很多种回答,可以根据相关程度以及历史聊天记录所有回答排序,找到最优,是一个搜索排序过程。
1595 0