搜索与信息检索
什么是搜索,提起搜索大家可能第一时间想到的就是google,有着强大的搜索和过滤功能, 大家在使用google时有没有发现google非常的懂你,不管有拼写错误、语法错误,时态错误往往都能得到想要的结果,大家有没有想过google背后的实现原理。
其实搜索背后关联一门学科,叫做Information retrieval信息检索简称IR,这门学科主要是研究搜索相关的理论算法,数据结构算法,如空间向量模型,布尔模型,如倒排索引,相似度算法,索引存储压缩算法等。IR实际是一门交叉学科,关联大数据、NLP自然语言处理(属于AI人工智能范畴)等等,是一门很有意思的学科。
大家有没有想过自己实现一个类似goole的搜索引擎,或者给自己的产品,添加一个好用的强大的搜索功能。自己实现全套IR算法几乎是不可能的,于是我们就需要一个强大第三方算法库。
Lucene
Lucene就是IR相关理论算法实现的一个java库,是目前最强大的全文搜索库,没有之一。Lucene开发者Doug Cutting是全球IR领域的顶级专家,同时也是hoodop开发者。基本使用如下
public static void main(String[] args) throws IOException, ParseException { // 定义分析器 StandardAnalyzer analyzer = new StandardAnalyzer(); // 创建索引 Directory index = new RAMDirectory(); IndexWriterConfig config = new IndexWriterConfig(analyzer); IndexWriter w = new IndexWriter(index, config); addDoc(w, "Lucene in Action", "193398817"); w.close(); // 构建Query String querystr = args.length > 0 ? args[0] : "lucene"; Query q = new QueryParser("title", analyzer).parse(querystr); int hitsPerPage = 10; // 打开索引 IndexReader reader = DirectoryReader.open(index); // 构建IndexSearcher 执行搜索 IndexSearcher searcher = new IndexSearcher(reader); TopDocs docs = searcher.search(q, hitsPerPage); ScoreDoc[] hits = docs.scoreDocs;}// 定义文档对象结构private static void addDoc(IndexWriter w, String title, String isbn) throws IOException { Document doc = new Document(); doc.add(new TextField("title", title, Field.Store.YES)); doc.add(new StringField("isbn", isbn, Field.Store.YES)); w.addDocument(doc);}
Lucene本身使用起来相对复杂需要大量编码,并且Lucene 是一个单机应用,虽然性能很好但是有瓶颈,真正的性能提升来自横向扩展。于是就有了ElasticSearch。
Lucene & ElasticSearch
ElasticSearch构建于Lucene之上,为Lucene提供了一个基于JSON的Resful API 大大降低了Lucene的使用门槛,
先构建索引定义mapping,写入只需要将构建好的JSON对象通过API直接put过去了,es会根据预定义的mapping 通过doc-paser对象解析并构建成Lucene文档对象, 查询只需要定义好query JSON DSL。图上功能和上一节Lucene代码功能是一致的。这种设计使得ES的变得很简单,支持多种开发语言。
ES为Lucene提供了分布式支持,实现动态可扩展性,高容错性,整个分布式实现对用户来说是透明的。
ElasticSearch 相关概念
1. Elasticsearch与关系型数据库概念对照表
2.索引与分片
索引是一个逻辑概念,实际的数据存储在一个个分片中,分片实际就是一个Lucene索引,类似于关系型数据库的分库分表。p代表主分片 r 代表副分片,副分片是主分片的拷贝,是和主分片同步的,主分片挂了可以用副分片来替换,这种设计提高了ES的数据可靠性。
3.ElasticSearch分布式架构
上图是ES的群集架构图,群集中每个data node 相当于一台装有lunce的物理机,然后通过控制节点整合在一起。
master节点相当于控制节点,存储mapping信息、节点信息、 分片信息、路由信息等并实现这些配置信息的的同步,提供了选主服务,服务发现等等, 整体功能类似于zookeeper etcd功能, ES是自己实现了一个类似于raft一致性算法库,没有依赖第三方。
4.ElasticSearch写入与搜索过程
4.1. ElasticSearch写入过程:
通过doc id hash算法计算出路由,将文档均匀的发送到index对应的不同分片上。
4.2. ElasticSearch搜索过程:
如果用户要查询满足条件的10条数据,ES实际会将查询请求发送给协调节点,协调节点再将查询请求广播给index对应节点的对应分片,对应分片按照查询条件分别取出自身满足条件的10条数据的id,返回给协调节点,协调节点再取出最满足条件的10条数据的id,然后通过id获取文档,最后将结果返回给用户,实际是一个的map reduce过程。
ElasticSearch具体有什么样的应用场景呢
- 日志分析(ELK目前最强大的开源日志解决方案)
- 安全审计
- 监控(将ES做为时序数据库,对标prometheus)
- 数据分析
- 最关键的还是搜索功能,active.com就是主要应用ES的搜索功能。
active.com 为什么选择ES
active.com作为一个大型活动类的门户网站,目的是让用户尽快的找到自己想要的events,所以搜索是active.com的核心功能,active.com对搜索功能尤其是全文检索功能有很高的要求, 网站本身数据量大(千万级数据),并发量大,对可靠性要求高,如果采用传统关系型数据库,像sql server虽然也有全文搜索功能但是比较弱,更无法应对大数据高并发场景。
如果你的项目需要强大的搜索功能尤其是全文搜索功能,数据量大,并发量大,Elasticsearch就是你的不二选择,ES会颠覆你对搜索的认识,不管你想到还是想不到的搜索行为,他都能做到。
active.com 从2012开始使用ES,当时ES才是0.2版本,在当时选择es可以说是非常大胆的决定,但事实也证明是一个非常成功的决定。
ElasticSearch在active.com的具体应用:
Typeahead功能,全文搜索功能,地理位置搜索,过滤功能:基于日期过滤,基于距离的过滤,基于topic的过滤。排序相关功能,boost和ranking功能
ES在active.com有很多方面,下边重点讲解下全文搜索、ranking这两个核心应用场景,这也是ES的核心。
全文搜索
什么是全文检索,实际就是像谷歌,输入关键字获取结果,或者像sql like查询Where name like '%active%'。ES全文检索是如何实现的呢背后的原理是什么,实际是主要是依赖两个概念一个是倒排索引,一个是分析器。
1. 倒排索引:
什么是倒排索引,其实非常好理解,你打开一本书,翻到最后,看到一个索引页,实际是由关键字对应具体页数,ES中简单理解就是词和文档id的映射,检索过程实际就是检索这个映射表的过程。
2. Analyzer
Analyzer实际是将非结构化的文本数据,转化为结构化的格式,方便搜索。
实际过程就是 字符处理 切词分词 词型转化,倒排索引中存储的term实际是经过分析器处理过的结果。
你在active.com输入26.2实际会搜到马拉松的活动,26.2英里实际代表马拉松,我们通过定义了一个同义词的分析器来实现。
Ranking
如果有知道SEO的很好理解,就是控制搜索结果出现的位置。ranking对搜索来说非常重要,用户往往只关心第一页的搜索结果。
ranking不同于关系数据库的sort简单按时间,按数值排序, ranking过程实际就是通过多因素计算得到每个文档分数,然后按文档分数从高到低排序的过程。
那么影响文档分数有哪些因素呢,一个是相似度,一个是Function score query
1. Similarity
TF/IDF是经典的相似度算法,简单理解,词越特殊文档分数越高,词在文档中出现的频率越高分数越高。
Okapi BM25 是ES默认的相似度算法,可以简单按照TF/IDF来理解。
2. Function score query
是通过Function影响文档分数,是一种可以人为影响分数的方法。
具体有两种,一种是通过filter条件改变weight,另一种通过衰减函数,如高斯函数,指数函数线性函数等等。
以active.com为例,如果我们想要虚拟的event 并且可注册,并且最新发布event排在前边,如果用SQL的sort是很难实现的,这就体现出ES的神奇之处,我们可用用 function score query来轻松解决。
{ "query": { "function_score": { "functions": [ { "filter": { "term": { "assetLegacyData.onlineRegistration": true } }, "weight": 1.4 }, { "filter": [ { "term": { "assetCategories.category.categoryName.name": "virtual events" } } ], "weight": 2 }, { "gauss": { "activityStartDate": { "scale": "180d", "offset": "0d", "decay": 0.9 } } } ], "score_mode": "multiply" } }}
要想得到boost效果是需要一点点调试的,可以借助explain来一点点调试,找出最佳的参数。
如何使用ElasticSearch
1. 关系型数据库与ElasticSearch结合
一种比较流行的方式是把,ES当做第二数据源,关系型数据库为第一数据源,数据先写入SQL server然后再序列化到ES中,用户的所有请求压力都打在ES上。这里ES充当一个代理角色,提供搜索功能。或者把ES理解为一个提供搜索的插件。
这种设计的好处即利用了关系型数据库的性稳定性和成熟建模能力,又得到了es的强大的搜索能力和高性能,高可靠性。这种方式也非常适合在现有系统中引入ES功能,这种对原有系统代码侵入性很小,几乎不需要改变原先代码,只需要添加将关系型数据库数据同步到到ES的功能,或者可以直接用官方的DB到ES迁移插件,整体引入过程可以说是无伤的。
2.关系型数据库到ES转换与建模:
通常采用大数据工具常用的构建大宽表数据冗余的方式,将相关表结构数据合并成一个大的JSON对象供ES使用。