搜索与信息检索

什么是搜索,提起搜索大家可能第一时间想到的就是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的使用门槛,

索引联合查询 es_搜索

先构建索引定义mapping,写入只需要将构建好的JSON对象通过API直接put过去了,es会根据预定义的mapping 通过doc-paser对象解析并构建成Lucene文档对象, 查询只需要定义好query JSON DSL。图上功能和上一节Lucene代码功能是一致的。这种设计使得ES的变得很简单,支持多种开发语言。

ES为Lucene提供了分布式支持,实现动态可扩展性,高容错性,整个分布式实现对用户来说是透明的。

ElasticSearch 相关概念

1. Elasticsearch与关系型数据库概念对照表

索引联合查询 es_关系型数据库_02

2.索引与分片

索引联合查询 es_全文搜索_03

索引是一个逻辑概念,实际的数据存储在一个个分片中,分片实际就是一个Lucene索引,类似于关系型数据库的分库分表。p代表主分片 r 代表副分片,副分片是主分片的拷贝,是和主分片同步的,主分片挂了可以用副分片来替换,这种设计提高了ES的数据可靠性。

3.ElasticSearch分布式架构

索引联合查询 es_关系型数据库_04

上图是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具体有什么样的应用场景呢

  1. 日志分析(ELK目前最强大的开源日志解决方案)
  2. 安全审计
  3. 监控(将ES做为时序数据库,对标prometheus)
  4. 数据分析
  5. 最关键的还是搜索功能,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的映射,检索过程实际就是检索这个映射表的过程。

索引联合查询 es_关系型数据库_05

2. Analyzer

Analyzer实际是将非结构化的文本数据,转化为结构化的格式,方便搜索。

索引联合查询 es_全文搜索_06

实际过程就是 字符处理 切词分词 词型转化,倒排索引中存储的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_关系型数据库_07

一种比较流行的方式是把,ES当做第二数据源,关系型数据库为第一数据源,数据先写入SQL server然后再序列化到ES中,用户的所有请求压力都打在ES上。这里ES充当一个代理角色,提供搜索功能。或者把ES理解为一个提供搜索的插件。

这种设计的好处即利用了关系型数据库的性稳定性和成熟建模能力,又得到了es的强大的搜索能力和高性能,高可靠性。这种方式也非常适合在现有系统中引入ES功能,这种对原有系统代码侵入性很小,几乎不需要改变原先代码,只需要添加将关系型数据库数据同步到到ES的功能,或者可以直接用官方的DB到ES迁移插件,整体引入过程可以说是无伤的。

2.关系型数据库到ES转换与建模:

索引联合查询 es_搜索_08

通常采用大数据工具常用的构建大宽表数据冗余的方式,将相关表结构数据合并成一个大的JSON对象供ES使用。