插件学习笔记:搜索引擎ElasticSearch

ElasticSearch介绍

ElasticSearch是一个实时分布式的高拓展全文搜索和分析引擎,是ELK(ElasticSearch, Kibana, Logstach的)技术的核心。
ElasticSearch的优势在于模糊与全文查询,虽然关系型数据库也能够做到like关键字的模糊查询,但是会遍历整个表数据,响应很慢,因此需要使用搜索更加快捷的ElasticSearch来进行处理。

主流的搜索引擎

ES与Solr的相同点

ElasticSearch和Solr都属于应用很广的搜索引擎,他们的基础都是Lucene, 一个Apache开源的全文搜索的java核心工具包

ES与Solr对比

  • ElasticSearch的安装比较简单,即开即用,Solr则相对而言比较复杂
  • Solr用Zookeeper进行集群管理,而ElasticSearch有内置的分布式协调管理功能
  • Solr指出JSON,XML等多种的数据格式,ES支支持Json
  • Solr提供的功能更多,而ES更注重核心功能,其他的依赖第三方差价,比如图形化的管理分析工具Kibana
  • Solr查询更快,更新索引慢,而ElasticSearch更新索引更快,也就是实时查询性能更好,因此像商城这种更适合用Solr,而微博这种更新数据量大的更适合用ElasticSearch

Hermes

腾讯的大数据搜索引擎,与ES和Solr相比

  • ES和Solr更注重全文的搜索,Hermes更注重数据的分析
  • ES和Solr相对数据量更小,而Hermes可以处理更大量的数据

ElasticSearch的数据格式

插件学习笔记:搜索引擎ElasticSearch_第1张图片
数据格式的对比,Index相当于一个数据库,Type相当于一个表,而Document对应了一行数据,Field对应了一个列,不过在后期的ElasticSearch中已经删除了Type的概念,一个文档的数据序列化格式为JSON

{
 "name" : "John",
 "sex" : "Male",
 "age" : 25,
 "birthDate": "1990/05/01",
 "about" : "I love to go rock climbing",
 "interests": [ "sports", "music" ]
}

分片

对于一个索引数据量过大的时候,一个节点可能是存储不下这个索引里所有的数据的,因此这里引入了分片的概念,也就是将所有的数据分片存储在不同的节点当中,查询的时候会查询整个集群里面所有属于同一索引的分片。这样的好处在于不仅可以突破节点的数据存储量,还可以并行操作提高效率和吞吐量。这部分对于用户而言是透明的,elasticsearch会自行进行管理。

  • 值得注意的是,Lucene的索引对应了elasticsearch中的分片,elasticsearch中的索引是lucene中索引的集合
  • 分片的内容的路由计算是由master根据一个路由公式来的

副本

允许创建分片的一个或多个拷贝,这个拷贝叫做复制分片/副本,分片的优势在于

  • 提高了可用性
  • 也可以提高搜索量/吞吐量,因为所有的搜索都可以在副本上并行运行

分片的数量可以在创建索引的时候指定,创建索引之后可以动态地改变复制的数量,但是分片数量是不能改变的。默认情况下会有一个主分片和一个复制分片,分片分配给某个节点的过程是由master节点完成的

ElasticSearch的架构

插件学习笔记:搜索引擎ElasticSearch_第2张图片

集群Cluster

一个ES集群有一个对应的名字,节点可以通过这个名字加入到集群当中,默认集群名称为“elasticsearch”

节点Node

一个节点也由一个对应的名字指定,默认是一个漫威漫画的名字,服务器通过指定节点名字可以对节点进行连接。一个集群内部节点的名称要唯一
如果没有指定集群的名称,同一网络下的若干节点只要一启动,就都会加入到“elasticsearch”这个集群当中,他们能够互相发现彼此。
在节点1中的数据在节点2里面也是能被查询到的,当一个节点被选为主节点master时,他就会负责集群范围内所有的变更,包括增加、删除索引或增加删除节点等。

ElasticSearch原理

读写流程

  • 对于写数据,会首先由协调节点计算主分片所在位置,写完主分片后写备份,而后返回成功消息
    插件学习笔记:搜索引擎ElasticSearch_第3张图片
  • 对于读数据,会选择轮询副本的方式来达到负载平衡的目的,当然同步没完成的时候会报告副本不存咋,这种情况下就需要主分片返回数据了
    插件学习笔记:搜索引擎ElasticSearch_第4张图片
    对于多条数据的操作是可以并行进行的

分片原理

分片是ES最小的工作单元

倒排索引

将文档中的内容进行分词,作为索引,索引对应的值是文档的id,也就是从id对应内容反向成了内容对应id

动态更新索引

ElasticSearch是基于Luence的,Luence引入了按段搜索的概念,每一个段就是一个倒排索引,查询时会采取轮询每一个段的方式进行,同时将搜到的内容进行聚合。以保证所有的内容都被正确地计算。
除了所有的段之外,es还会存储一个提交点,即一个列出了所有已知段的文件。
插件学习笔记:搜索引擎ElasticSearch_第5张图片
索引更新流程如下

  1. 新文档进入索引缓存当中
    插件学习笔记:搜索引擎ElasticSearch_第6张图片
  2. 缓存被定时地提交,作为新的段,同时提交点文件更新,缓存清空
    插件学习笔记:搜索引擎ElasticSearch_第7张图片
  3. 当段被删除时,采用的是逻辑删除而非物理删除,也就是在提交点文件中标记为“已删除”。修改也是采用先逻辑删除再增加的形式。

近实时搜索特性

由于从缓存中向磁盘写入新的段是十分浪费事件的,因此elasticSearch采用了一个存在缓存中的段的方式,将缓存区的内容写入一个可以被搜索的段中,这样缓存区的内容就可以被搜索到了,再将耗费时间长的磁盘写入过程异步进行提交就可以了。这个过程被成为refresh,每秒(这个事件是可以修改的)执行一次,也就是添加的新段会在一秒内被搜索到,因此是实时搜索的特性。

持久化变更

进实时搜索的缓存段如果断电的话就消失了,因此持久化,也就是异步存储段到磁盘的过程也是必须被执行的。elasticSearch引入了translog日志来进行处理。
在缓存写入的过程当中,translog也会记录相应的内容,隔一段时间会进行全量的提交,也就是

  1. 缓存内容写入新的段
  2. 缓存内容被清空
  3. 新的提交点写入硬盘
  4. 文件系统换从刷新,即flush
  5. 老的translog被删除
    也就是说通过translog这一个存储了所有没有被持久化操作的日志记录,完成了可靠的数据持久化工作
    插件学习笔记:搜索引擎ElasticSearch_第8张图片插件学习笔记:搜索引擎ElasticSearch_第9张图片
    插件学习笔记:搜索引擎ElasticSearch_第10张图片
    插件学习笔记:搜索引擎ElasticSearch_第11张图片
    分片每30分钟或translog过长的时候进行一次flush
  • 注意:段的持久化是异步的,可以保证数据的可靠性,但是前提是translog的数据能够被正确地存储,translog默认每5秒刷新一次,或者在请求之后执行一次,这就使得translog本身的持久化是同步的,虽然会损失一定的性能,但是数据是可靠的,当然在一些数据可靠性要求不高的应用场景也可以采用异步的方式追求极致的性能。

段合并

采用了分段搜索虽然更新索引的开销较低,但是随着段的暴增,数据的搜索会变得缓慢,因此在elasticSearch当中引入了段合并的后台过程,小的段被合并到了大的段当中。段合并的过程如下

  1. refresh的过程中启用了缓存的段并可以被搜索到

  2. 产生一个大的缓存段

  3. 选择大小相似的小段,将其写入大的缓存段当中(这一步并不影响原先小段被搜索)

  4. 新段flush到磁盘,提交点也被刷新
    插件学习笔记:搜索引擎ElasticSearch_第12张图片

  5. 新段打开用于搜索

  6. 老段删除
    插件学习笔记:搜索引擎ElasticSearch_第13张图片
    合并过程占用了大量的CPU资源,因此elasticSearch对合并过程进行了资源的限制,保证搜索过程能被很好地执行

文档分析

文档分析器的作用在于将一整块内容划分为独立的词条并进行一些相关的处理保证“可搜索性”,一次文档的分析过程被分成了三步

  1. 字符过滤:比如去除html,将&转换成and等,字符过滤器在一个文档分析器中是允许有多个的
  2. 分词:将一整段内容划分为单个词条
  3. Token过滤:改变词条(大小写归一),删除词条(的,地,得),增加词条(基于扫帚增加扫把)

当然分词器是用在查询全文域的,当进行精确查询的时候,比如使用+关键字,是不会用分析器进行处理的

内置分析器

有标准分析器,简单分析器,空格分析器,语言分析器等,它们根据不同的规则进行了词条的划分

IK中文分词器

进行常见的中文分词并支持进行自定义拓展词典(远程的或者本地的都可以)
除了这些之外ES也是支持自定义分词器的

ElasticSearch文档冲突

Web1做了-1操作,Web2也做了-1操作,但是二者引发了并发所带来的冲突,这就是文档冲突现象,其实就是一个并发问题
插件学习笔记:搜索引擎ElasticSearch_第14张图片
文档冲突现象有两种处理方式,一是悲观并发控制,二是乐观并发控制

  • 悲观并发控制:比如mysql的行级锁,只允许一个线程进行数据的处理
  • 乐观并发控制:请求会携带版本号,旧的版本号不会覆盖新的版本号,以保证数据不会丢失,ES采用的就是这种方式

ElasticSearch索引库的操作

HttpUrl操作

采用RESTful风格进行处理,相信的条件封装在请求体的JSON格式数据当中
RESTful风格简而言之就是

  • URL就代表了资源
  • HTTP的操作类型就代表了对资源进行怎样的处理
  • 返回的状态码就能知道操作取得的效果如何

支持以下几种操作:

  • 索引
  • 文档
  • 映射(相当于关系型数据库当中的表结构)

DSL语句操作

同样支持上述的操作,包括排序、过滤、分页等等相关的操作

SpringData ElasticSearch

SpringDataElasticSearch对ES的相应API进行了封装,能够更方便地构建所需的代码
引入依赖

        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-elasticsearchartifactId>
        dependency>

配置文件

spring:
  data:
    elasticsearch:
      cluster-name: my-application
      cluster-nodes: 192.168.211.132:9300

配置说明

connection-timeout:服务连接超时时间
socket-connect:HTTP请求超时时间
ribbon.ReadTimeout: Feign请求读取数据超时时间
timeoutInMilliseconds:feign连接超时时间
cluster-name:Elasticsearch的集群节点名称,这里需要和Elasticsearch集群节点名称保持一致
cluster-nodes:Elasticsearch节点通信地址

启动类

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class SearchApplication {

    public static void main(String[] args) {
        /**
        * Springboot整合Elasticsearch 在项目启动前设置一下的属性,防止报错
        * 解决netty冲突后初始化client时还会抛出异常
        * availableProcessors is already set to [12], rejecting [12]
        ***/
        System.setProperty("es.set.netty.runtime.available.processors", "false");
        SpringApplication.run(SearchApplication.class,args);
    }
}

对于POJO,设置Document和Feild注解设置,需要与elasticSearch中的设置一致

@Document(indexName = "skuinfo",type = "docs")
public class SkuInfo implements Serializable {
    //商品id,同时也是商品编号
    @Id
    private Long id;

    //SKU名称
    @Field(type = FieldType.Text, analyzer = "ik_smart")
    private String name;

    //商品价格,单位为:元
    @Field(type = FieldType.Double)
    private Long price;
}

DAO层的设置也比较简单,实现相应的类即可

@Repository
public interface SkuEsMapper extends ElasticsearchRepository<Sku,Long> {
}

许多简单查询、分页查询、聚合查询、高亮查询都是可以用类中封装的方法进行实现的

ElasticSearch面试题

为什么使用ElasticSearch

系统中的数据,随着业务的发展,时间的推移,将会非常多,而业务中往往采用模糊查询进行数据的搜索,而模糊查询会导致查询引擎放弃索引,导致系统查询数据时都是全表扫描,在百万级别的数据库中,查询效率是非常低下的,而我们使用 ES 做一个全文索引,将经常查询的系统功能的某些字段,比如说电商系统的商品表中商品名,描述、价格还有 id 这些字段我们放入 ES 索引库里,可以提高查询速度。

  • 关键字: 模糊查询效率低,倒排索引,提高速度

master 选举流程

  1. Elasticsearch 的选主是 ZenDiscovery 模块负责的,主要包含 Ping(节点之间通过这个 RPC 来发现彼此)和 Unicast(单播模块包含一个主机列表以控制哪些节点需要 ping 通)这两部分
  2. 对所有可以成为 master 的节点(node.master: true)根据 nodeId 字典排序,每次选举每个节点都把自己所知道节点排一次序,然后选出第一个(第 0 位)节点,暂且认为它是 master 节点。
  3. 如果对某个节点的投票数达到一定的值(可以成为 master 节点数 n/2+1)并且该节点自己也选举自己,那这个节点就是 master。否则重新选举一直到满足上述条件。
  4. master 节点的职责主要包括集群、节点和索引的管理,不负责文档级别的管理;data 节点可以关闭 http功能。
  • 关键词:ping,排序,选取第一个

关于脑裂

概念

简而言之,因为某些原因出现了多个master,比如两个机房分别存放了三个节点,机房间的网络中断导致机房2中的节点无法访问机房1中的节点,于是选取了两个master,就造成了脑裂
插件学习笔记:搜索引擎ElasticSearch_第15张图片
插件学习笔记:搜索引擎ElasticSearch_第16张图片

脑裂成因

  • 网络延迟原因,集群间的网络通信出现问题
  • 节点负载,master作为data节点访问压力过大导致造成大面积延迟
  • data上的垃圾回收导致内存占用较大,使得ES进程失去响应

脑裂解决

  • 增加响应失效时间的阈值,减少误判
  • 参考zookeeper的过半数选举,这也是ES官方推荐的模式,即选举必须超过半数才能成为新的master
  • 角色分离:master不担任data节点(配置文件中是可以进行配置的)

底层流程

索引文档流程

插件学习笔记:搜索引擎ElasticSearch_第17张图片

  1. 协调节点默认使用文档 ID 参与计算(也支持通过 routing),以便为路由提供合适的分片
  2. 当分片所在的节点接收到来自协调节点的请求后,会将请求写入到 Memory Buffer,然后定时(默认是每隔 1 秒)写入到 Filesystem Cache,这个从 Memory Buffer 到 Filesystem Cache 的过程就叫做 refresh;
  3. 当然在某些情况下,存在 Momery Buffer 和 Filesystem Cache 的数据可能会丢失,ES 是通过 translog的机制来保证数据的可靠性的。其实现机制是接收到请求后,同时也会写入到 translog 中,当 Filesystem cache 中的数据写入到磁盘中时,才会清除掉,这个过程叫做 flush;
  4. 在 flush 过程中,内存中的缓冲将被清除,内容被写入一个新段,段的 fsync 将创建一个新的提交点,并将内容刷新到磁盘,旧的 translog 将被删除并开始一个新的 translog。
  5. flush 触发的时机是定时触发(默认 30 分钟)或者 translog 变得太大(默认为 512M)时;
  6. 同时合并段的过程也会执行
  • 关键词: 分片路由、缓存区与translog、refresh开发临时段、flush持久化、合并段

更新和删除文档流程

  1. 删除和更新也都是写操作,但是 Elasticsearch 中的文档是不可变的,因此不能被删除或者改动以展示
    其变更;
  2. 磁盘上的每个段都有一个相应的.del 文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del 文件中被标记为删除的文档将不会被写入新段。
  3. 在新的文档被创建时,Elasticsearch 会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
  • 关键词: 提交点逻辑删除、结果过滤

搜索流程

搜索被执行成一个两阶段过程,我们称之为 Query Then Fetch;

  1. 在初始查询阶段时,查询会广播到索引中每一个分片拷贝(主分片或者副本分片)。 每个分片在本地执行搜索并构建一个匹配文档的大小为 from + size 的优先队列。PS:在搜索的时候是会查询Filesystem Cache 的,但是有部分数据还在 Memory Buffer,所以搜索是近实时的。
  2. 每个分片返回各自优先队列中 所有文档的 ID 和排序值 给协调节点,它合并这些值到自己的优先队
    列中来产生一个全局排序后的结果列表。
  3. 接下来就是取回阶段,协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并丰富文档,如果有需要的话,接着返回文档给协调节点。一旦所有的文档都被取回了,协调节点返回结果给客户端。
  4. Query Then Fetch 的搜索类型在文档相关性打分的时候参考的是本分片的数据,这样在文档数量较少的时候可能不够准确,DFS Query Then Fetch 增加了一个预查询的处理,询问 Term 和 Document frequency,这个评分更准确,但是性能会变差。
  • 关键词: : 并行查询,协调节点合并,评分与过滤

如何实现大数据聚合

通过HLL算法基于概率进行估算所得到的,精度是可控的

如何保证读写一致

  • 可以通过版本号使用乐观并发控制,以确保新版本不会被旧版本覆盖,由应用层来处理具体的冲突;
  • 另外对于写操作,一致性级别支持 quorum/one/all,默认为 quorum,即只有当大多数分片可用时才允许写操作。但即使大多数可用,也可能存在因为网络等原因导致写入副本失败,这样该副本被认为故障,分片将会在一个不同的节点上重建。
  • 对于读操作,可以设置 replication 为 sync(默认),这使得操作在主分片和副本分片都完成后才会返回;如果设置 replication 为 async 时,也可以通过设置搜索请求参数_preference 为 primary 来查询主分片,确保文档是最新版本。
  • 关键词 :乐观锁并发控制、多数分片可用才写,主副分片都完成后才返回

如何监控集群状态

elasticSearch-Head插件(丑的一批)
Kibana(属实好看)

字典树

插件学习笔记:搜索引擎ElasticSearch_第18张图片
字典树又称单词查找树,Trie 树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排
序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
Trie 的核心思想是空间换时间,利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
它有 3 个基本性质:

  • 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。
    对于中文的字典树,每个节点的子节点用一个哈希表存储,这样就不用浪费太大的空间,而且查询速度上可以保留哈希的复杂度 O(1)。

名词解释

集群

集群是一个或多个节点(服务器)的集合,它们共同保存您的整个数据,并提供跨所有节点的联合索
引和搜索功能。群集由唯一名称标识,默认情况下为“elasticsearch”。此名称很重要,因为如果节点设
置为按名称加入群集,则该节点只能是群集的一部分。

节点

节点是属于集群一部分的单个服务器。它存储数据并参与群集索引和搜索功能。

索引

索引就像关系数据库中的“数据库”。它有一个定义多种类型的映射。索引是逻辑名称空间,映射到一个或多个主分片,并且可以有零个或多个副本分片。 MySQL =>数据库 Elasticsearch =>索引

文档

文档类似于关系数据库中的一行。不同之处在于索引中的每个文档可以具有不同的结构(字段),但
是对于通用字段应该具有相同的数据类型。 MySQL => Databases => Tables => Columns / Rows Elasticsearch => Indices => Types =>具有属性的文档

类型

类型是索引的逻辑类别/分区,其语义完全取决于用户。

倒排文档

倒排索引是搜索引擎的核心。搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。ES中的倒排索引其实就是 lucene 的倒排索引,区别于传统的正向索引,倒排索引会再存储数据时将关键词和数据进行关联,保存到倒排表中,然后查询时,将查询内容进行分词后在倒排表中进行查询,最后匹配数据即可

你可能感兴趣的:(插件学习,搜索引擎,elasticsearch,solr)