Elasticsearch分布式全文检索引擎
全文检索(搜索)引擎:汇合了网络爬虫技术、检索排序技术、网页处理技术、大数据处理技术、自然语言处理技术等综合性的学科
检索引擎分类:Lucene、Nutch、Solr、Elasticsearch
下面Elasticsearch以7.0+版本做介绍
1、基本概述
-
Elasticsearch(简写es), Elasticsearch是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理PB级别(1PB=1024T=1048576G)的数据。
-
Elasticsearch使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,Lucene非常复杂,而es通过简单的RESTful API隐藏Lucene的复杂性,可理解为简化使用Lucene进行开发
-
es的定位是:专注于搜索的非关系型数据库,其严重“偏科”,相比其他数据库,其搜索的性能甩几条街,但是对应的DML操作就非常复杂且低性能,因此,使用es就是海量的数据的DQL操作,而DML操作则需要对应权衡
2、es数据架构(与mysql对比区别)及数据类型
- 关系型数据库中的数据库(Database),等价于ES中的索引(Index)
- 一个数据库下面有N张表(Table),等价于1个索引Index下面仅有一个类型(Type)是_doc
- 一个数据库表(Table)下的数据由多行(ROW)多列(column,属性)组成,等价于1个Type由多个文档(document)和多字段Field组成。
- 在数据库中的增insert、删delete、改update、查search操作等价于ES中的增PUT/POST、删Delete、改_update、查GET
3、es基本语法操作
- 创建索引/表(索引即表)
创建映射 PUT /索引名(可以和索引一起创建,映射就是对应的文档类型)
每个文档都有一个ID,如果插入的时候没有指定ID的话,ElasticSearch会自动生成一个字符串_id。
- 删除DELETE /索引名、DELETE /索引名/_doc/文档ID
注意:这里的删除并且不是真正意义上的删除,仅仅是清空文档内容,并且标记该文档的状态为删除而已。如果后续有数据新增进来则会替换它的位置,如果一直没有数据替换则定时删除
- 修改 PUT/索引名/_doc/文档ID
注意:如果有其它字段没有指定的话会清空该字段值,如果仅是更新某些字段可以如此
- 查询GET /索引名/_doc/xxx
- 全文搜索(核心)
- 高亮显示
4、分词器
es的分词器把文本内容按照一定标准进行切分,默认使用standard分词器,该分词器按单词(注意是单词不是字母)、文字(单拆)匹配查询
es支持额外的分词器插件IK分词器
IK分词器:
-
ik_smart 粗粒度分词
会做最粗粒度的拆分,即尽可能长地拆分。比如会将“中华人民共和国人民大会堂”拆分为中华人民共和国、人民大会堂。 -
ik_max_word 细粒度分词
字段尽可能短地拆分,且会从短到更短地拆分。比如会将“中华人民共和国人民大会堂”拆分为“中华人民共和国、中华人民、中华、华人、人民共和国、人民、共和国、大会堂、大会、会堂等词语。
5、倒排索引(查询)
正排索引(模糊查询):所谓正排就是在模糊匹配的时候,扫描索引库中的所有文档,找出所有包含关键词的文档,再根据打分模型进行打分(即匹配度高低),说白了就是找到所有文档再筛选。MySQL的索引在使用模糊匹配时即失效,并且索引没有全局性。
倒排索引则是根据映射直接定点索引(查询)
这也是es为什么如此快搜索的原因,具体什么是倒排索引我们需要先了解es搜索的完整过程:
①es新增文档的时候,使用事先指定的分词器对文档中的每个内容进行拆词
②将拆词之后的到的每一个Term (词根),保存在一张倒排索引列表中(一个main.dic文件,也即Term Dictionary字典),然后会建立词根与文档id(新增的时候就有指定了文档id(也就是_id),所以是直接对应)的一对多映射(一个词根对应很可能有多个id)
③到了搜索时,分词器会对搜索的内容进行分词,然后对应也得到词根
④根据词根到字典中进行匹配文档id列
⑤获得文档id(一般将表id列作为文档id列,为了后面查询方便)之后进行综合处理,然后对其进行热度排序,数据封装成一个集合返回给搜索者(正排索引是全表查询后筛选)
总结:
像通过id查找表中的某行就是正排索引,而倒排索引就是通过某个列或者某些列甚至全表列(具体看你的关键词需要在哪部分出现)中的某个关键词(也即模糊词)到表中查找某行;一般我们从mysql中预热数据到es的时候,都会选择关键词会出现的列作为es的keyword,这将是提高es效率关键的一步。
注意事项:
-
es只有text类型的数据会分词,而keyword类型的数据是直接建立索引的
-
所以实际的倒排列表中并不只是存了文档ID那么简单,还有一些其它的信息,比如:词频(Term出现的次数)、偏移量(offset)等。而上述过程也反映了对应的DML操作的复杂性,特别是UPDATE操作,特别消耗性能(update其实就是delete+put)如 当用户在主页上搜索关键词“华为手机”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“华为手机”的文档,再根据打分模型进行打分,排出名次后呈现给用户。
6、全文搜索思考问题
我们进行全文搜索的时候,涉及到的问题无非是:
- 搜索的数据从何而来:mysql中加载,或者说从主库中获取(因为es是搜索能手,所以自然是从主库中备份数据过来)
- 有哪些数据需要初始化:搜索的数据对象,即目标对象
- 对应的数据有哪些明确字段:也就是说,确定好数据对象之后,进行检索的时候,对应的关键词总要匹配某个或某些字段,因为倒排索引收集的字典的词根是从字段内容中拆分出来的,而我们检索的关键词对应的也是有具体方向的,比如我要找有关广州的文章,有关指的是目的地或标题或发布者,对应的字段就应该是这些,而不是无脑全选
注意:我们在初始化数据的时候,除了需要需要所以要明确字段外,其次更重要的原因是,es只是做搜索的,主库中的数据会随时更新,那对应的es也要更新,但是由于es“偏科“,所以我们一般会周期性并且避峰进行更新数据到es(比如凌晨1点)
7、SpringBoot集成Elasticsearch
数据准备,从mysql中初始化数据到es,下面以全文检索目的地/攻略/游记/用户为例
使用es的时候要开启终端bat
mgsire/DataController
本项目在core的pom和properties中操作
1.依赖
2.配置文件
3.自定义domain
其余相关domain差不多,区别一下检索字段即可(就是你的关键词检索内容的位置)(跟mongodb一样,使用es的时候最好分search或elasticsearch包区分,对应的domain后缀名用ES、Es)
4.通用化repository接口
5全文搜索接口
SearchResultVO结果集封装
全文检索及高亮显示,所谓高亮显示,就是在检索的时候,对出现的关键词加颜色或者高亮区别其他非关键词,虽然实现很简单,但确是友好对待用户的基本。
es中的分页api和mongodb类似(毕竟同样继承spring-data依赖,可以说是完全一样了)
QueryBuilders:高亮条件构建
PageRequest:分页条件构建
NativeSearchQuery:整合条件构建
SearchHits:template.search的条件检索结果
6.初始化数据
es从mysql(或者其他关系型数据库)初始化数据有两种方式:同步更新、异步更新
- 同步更新
所谓同步更新就是代码对mysql数据进行增删改(下面统称更新)操作之后,紧接着对es数据更新。
抛开es性能不说,这个方法存在一个弊端,就是mysql支持事务,如果mysql在更新完成之后,紧接着es更新数据出现异常,按照事务回滚来说,本该更新完成的数据却因为外界而导致无法更新成功,它们之间互相影响显然不是一个好结果。因此该方案是不可行的。
- 异步更新
异步更新有两种方式,一是使用数据库中间件方式(数据库中间件canal将在后面补充),一种是定时器更新方式。
定时器更新即使用定时器从mysql中获取数据到es中,es更新性能很低,所以对应的时间应该设置在凌晨或者用户访问量较低的时间段
3、MySql、MongDB、Redis、Elasticsearch选型
-
mysql作为主库,存储核心数据,其数据关系可以很复杂,因此对应支持复杂联表条件查询。非关系型数据库中的数据都是从关系型数据库获取的,无论后者的数据如何,都是从前者中引申、或者备份而来的。
-
redis是非关系型数据库,严格来说定位是缓存。用于存储具有时效性、海量的数据。时效性如存储登录用户信息(类session)、短信验证码。redis的读写性能优于mysql,性能大概是mysql的1.0x10^6倍,而其缺点就是断电即失效,毕竟是内存操作,所以对应的关键数据(相比之下短时效的数据是没关系的)需要定期持久化到关系型数据库中。
-
**mongodb作为关系型数据库和非关系型数据库之间,其可以单独实现如redis的内存操作、mysql的持久化。将mongodb作为类似redis、memcache来做缓存db,为mysql提供服务,或是后端日志收集分析。 **考虑到mongodb属于nosql型数据库,sql语句与数据结构不如mysql那么亲和 ,也会有很多时候将mongodb做为辅助mysql而使用的类redis、memcache 之类的缓存db来使用。 **亦或是仅作日志收集分析。**或者是存储某文章相关的评论,n方级别的数据(sql是中间表的形式,数据量太大)。说白了就是
- elasticsearch是非关系型数据库,其直接操作内存,其数据来源于关系型数据库,插入数据时生成字典供mysql目标检索使用,字典文件存储在main.dic文件中,而其作为字典不需要持久化数据回关系型数据库。其最大的优势就是通过倒排索引做到瞬时检索,但也因为倒排索引是从数据插入就开始定义的,索引其对应的增删改操作性能就会特别慢,而其只能在用户访问少的时候做数据更新。