使用sphinx search打造你自己的中文搜索引擎
Google搜索引擎建立至今已经快20年了,之后全球各类大大小小类似的搜索引擎也陆续出现、消亡。国内目前以百度为大,搜狗、360、必应等也势在必争。搜索引擎技术也发展的相当成熟,同时也就出现了很多开源的搜索引擎系统。比如,Solr、Lucene、Elasticsearch、Sphinx等。
本文以sphinx search为例来介绍如何打造自己的搜索引擎。该搜索引擎的架构大致如下:
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开始建立实时索引
以上就是建立一个自己的搜索引擎的过程。更多配置细节可到官方网站参考文档。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
系列文章|OKR与敏捷(一):瀑布式目标与敏捷的冲突
OKR与敏捷开发的原理有着相似之处,但已经使用敏捷的团队再用OKR感觉会显得多余。这种误解的根源就在于对这两种模式不够了解,运用得当的情况下,OKR和敏捷可以形成强强联合的效果,他们可以创造出以价值为驱动的团队,改变团队的工作方式。 本文第一部分介绍了瀑布式目标与敏捷之间的冲突。 功能工厂 敏捷的诞生是为了确保软件的交付,可以替代瀑布式开发来管理软件项目。敏捷模式注重的是可交付成果(开发需求或软件功能)而非项目价值(业务成果)的管理。 事实上,敏捷中并没有单独跟踪结果的规范。 《敏捷宣言》本身具有一定的误导性,因为它告诉人们要衡量可交付成果,它的第七条原则规定:“可工作的软件是进度的首要度量标准”。 该原则默认所有可使用的软件都有价值,这显然是错误的。因为有些项目能够产生价值,有些不能;用户可能会采用软件的某些功能但却会摒弃其他的功能。 大多数公司陷入了“功能工厂”模式,其团队压根不关注交付产品的价值。正如约翰·卡特勒(John Cutler)所描述的那样,开发人员就像流水线的工人那样“坐在工厂里,批量制造各种功能,然后传送出去”。 马蒂·卡根(Marty Cagan)强调过这种模式可...
- 下一篇
谈谈Java引用和Threadlocal的那些事
1 背景 某一天在某一个群里面的某个群友突然提出了一个问题:"threadlocal的key是弱引用,那么在threadlocal.get()的时候,发生GC之后,key是否是null?"屏幕前的你可以好好的想想这个问题,在这里我先卖个关子,先讲讲Java中引用和ThreadLocal的那些事。 2 Java中的引用 对于很多Java初学者来说,会把引用和对象给搞混淆。下面有一段代码, User zhangsan = new User("zhangsan", 24); 这里先提个问题zhangsan到底是引用还是对象呢?很多人会认为zhangsan是个对象,如果你也是这样认为的话那么再看一下下面一段代码 User zhangsan; zhangsan = new User("zhangsan", 24); 这段代码和开始的代码其实执行效果是一致的,这段代码的第一行User zhangsan,定义了zhangsan,那你认为zhangsan还是对象吗?如果你还认为的话,那么这个对象应该是什么呢?的确,zhangsan其实只是一个引用,对JVM内存划分熟悉的同学应该熟悉下面的图片: 其实z...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,CentOS8安装Elasticsearch6.8.6
- 2048小游戏-低调大师作品
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7安装Docker,走上虚拟化容器引擎之路