天天看点

一篇了解分布式实时搜索引擎——Elasticsearch1. 基本概念2. 基本原理3. 客户端API4. 主要内部模块

什么是Elasticsearch?

Elasticsearch是一款分布式实时搜索引擎,内部基于Lucene做索引与搜索。

上面这段简短的介绍有几个关键字值得注意:实时,实时意味着ES的数据在入库后1s内就可以被搜索到。分布式,ES支持动态调整集群规模,弹性扩容。Lucene则是一款全文搜索框架,提供建立索引和执行搜索的功能,但是并不包含分布式服务。目前除了搜索,ES还提供了大量的聚合功能,所以它不仅仅是一款搜索引擎,还可以进行数据分析、统计,生成指标数据,所有这些功能都在快速迭代。

文章目录

  • 1. 基本概念
    • 1.1. ES的索引结构
    • 1.2. 分片(shard)
    • 1.3. 动态更新索引
    • 1.4. 近实时搜索
    • 1.5. 段合并
  • 2. 基本原理
    • 2.1. 节点角色
    • 2.2. 集群健康状态
    • 2.3. 扩容
  • 3. 客户端API
  • 4. 主要内部模块

1. 基本概念

1.1. ES的索引结构

Elasticsearch是面向文档的,一般使用JSON作为文档的序列化格式,由于文档可以有多个字段,所以在创建索引的时候,我们需要描述文档中的每个字段的数据类型,并且可能需要指定不同的分析器。

在存储结构上,有_index、_type和_id唯一标识一个文档。_index指向一个或多个物理分片的逻辑命名空间,_type用于区分同一个集合逻辑上的不同细分(在7.x版本将完全删除),_id文档标记符由系统自动生成或使用者提供。

1.2. 分片(shard)

分布式系统中为了提高系统水平扩展的能力一般使用分片和多数据副本的方式。分片是将数据分成若干个小块分配到各个机器上;多副本则是复制数据到不同机器上,这种方式可增加系统的可用性,也可以提供读并发的执行能力,但也带来了一致性问题。

为了处理并发更新的问题,ES将数据副本分为主从两部分,即primary shard和replica shard。写过程先写主分片,成功后再写副本分片。恢复阶段以主分片为准。分片是数据的容器,文档保存在分片内,不会跨分片存储。当集群规模扩大或缩小时,ES会自动在各节点中迁移分片,使数据仍然均匀分布在集群里。

一个ES索引包含很多分片,一个分片是一个Lucene的索引,它本身可以独立执行建立索引和搜索任务。Lucene索引又由很多Segment组成,每个Segment都是一个倒排索引。ES每次“refresh”都会生成一个新的Segment,其中包含若干文档的数据。在每个分段内部,文档的不同字段被单独建立索引,每个字段的值由若干词(Term)组成,Term是经过分词器和语言处理后的最终结果。

ES的主分片数在较早版本(5.x)不可以修改,副分片数可以随时修改。现在ES已经支持在一定限制下,对某个索引的主分片进行拆分或缩小。但是我们仍然应该提前规划好主分片数量。分片数不够时可以考虑新建索引,搜索一个有50个分片的与搜索50个只有一个分片的索引完全等价。

1.3. 动态更新索引

倒排索引一旦被写入文件具有不可变性。那么索引如何进行更新呢?答案时使用更多的索引,新增内容被写入到一个新的倒排索引,查询时再对结果进行合并。每次内存缓存的数据被写入文件时,会产生一个新的段,在元信息文件中记录当前Lucene索引有哪些分段。

由于分段的不变性,更新、删除操作实际上是标记删除,并不会真正释放磁盘空间。

1.4. 近实时搜索

写操作时一般会再内存中缓存一段数据再写入磁盘,由于调用操作系统的write接口返回成功时,数据会先到达缓存,未必被写入磁盘。但无论是否被刷入磁盘,该数据已经对读取可见。

ES正是利用了这种特性实现近实时搜索,每秒产生一个新分段,新分段先写入文件系统缓存,稍后再执行flush刷盘操作。由于系统先缓存一段再写,所以可能存在丢失数据的风险。通用的做法是使用WAL机制。

1.5. 段合并

在ES中,每秒会产生一个新的分段写入文件系统,这个过程叫做refresh。但是分段太多会产生消耗文件句柄和内存,每个搜索请求都会轮流检查每个分段,查询完再对结果进行合并。所以需要一定的机制对于这些段进行合并,常用的方法是选择大小相似的分段进行合并。在合并的过程中标记为删除的数据会被删除。

2. 基本原理

ES采用的集群方式是主从模式。主从模式可以简化系统设计,Master作为权威节点维护集群元数据信息。这种模式的缺点是Master节点存在单点故障的问题,并且集群规模受限与Master节点的管理能力。

因此从角色划分的角度来说,存在主节点和数据节点,另外还有协调节点、预处理节点和部落节点。

2.1. 节点角色

  1. 主节点

    负责集群层面的操作,维护集群变更。主节点也可以作为数据节点,当应尽量少的分配任务。

  2. 数据节点

    负责保存数据、执行:CURD、搜索、聚合等操作。一般情况,数据读写流程只与数据节点打交道。

  3. 预处理节点

    5.0版本引入的概念,预处理操作允许写入数据之前,通过定义好的process(处理器)和pipline(管道)对于数据进行某种转化、富化。processors和pipine拦截bulk和index请求,在应用相关操作之后将文档传回index或bulk API。

  4. 协调节点

    负责处理客户端请求的节点称为协调节点。协调节点将请求转发给保存数据的数据节点,每个数据节点执行请求之后将结果返回给协调节点。协调节点将每个数据节点的结果合并之后返回给客户端。由于涉及到结果收集和排序可能需要很多CPU和内存资源。

  5. 部落节点

    tribes(部落)功能允许部落节点在多个集群中间充当联合客户端。

2.2. 集群健康状态

Green,所有主副分片都正常运行。

Yellow,主分片都正常运行,但不是所有副分片都正常运行,存在单点故障风险。

Red,有主分片没有正常运行。

2.3. 扩容

当集群扩容、添加节点时,分片会均匀分配到集群各个节点,这些是自动完成的。需要注意的是,主分片和它的副本分片不能分配到同一个节点,以防止单点故障。当主分片节点异常时,集群会重新进行选举,将副分片提升为主分片。

3. 客户端API

目前可以选择的操作方式有REST接口、Java REST API,或者Java API。

其中Java REST API是对原生REST接口的封装,通过9200端口通信,采用JSON over HTTP的方式,而Java API使用9300端口通信,数据序列化为二进制。

Java API虽然理论上有微弱的效率优势,但是却带来了兼容性问题,从8.0版本开始完全移除。

4. 主要内部模块

ES使用Guice框架进行模块化管理。Guice是Google开发的轻量级依赖注入框架(IoC)。模块化的封装让ES易于扩展,插件本身也是一个模块,节点启动时被模块管理器添加进来。

  1. Cluster

    Cluster是主节点执行集群管理的封装实现,管理集群状态和维护集群配置信息。主要功能如下:

    • 管理集群状态;
    • 调用allocation模块执行分片分配;
    • 迁移分片,保持数据平衡。
  2. allocation

    封装了分片分配的功能和策略,由主节点调用。

  3. Discover

    负责发现集群中新加入的节点,以及选举主节点。

  4. gateway

    负责对Master广播下来的集群状态信息持久化存储,并在集群完全重启时恢复它们。

  5. indices

    管理全局级的索引设置,它还封装了索引数据恢复功能。集群启动阶段需要的主分片恢复和副分片恢复就是在这个模块实现的。

  6. HTTP

    通过JSON over HTTP的方式访问ES的API。

  7. Transport

    用于集群节点之间的内部通信。使用TCP通信,每个节点都与其它节点维持若干TCP长连接。

  8. Engine

    封装对Lucene的操作及translog的调用。

继续阅读