一、基本概念

  • NRT:
    Near Realtime,近实时,有两个层面的含义,一是从写入一条数据到这条数据可以被搜索,有一段非常小的延迟(大约1秒左右),二是基于Elasticsearch的搜索和分析操作,耗时可以达到秒级。

  • Cluster
    集群,对外提供索引和搜索的服务,包含一个或多个节点,每个节点属于哪个集群是通过集群名称来决定的(默认名称是elasticsearch)

  • Node
    单独一个Elasticsearch服务器实例称为一个node,node是集群的一部分,每个node有独立的名称,默认是启动时获取一个UUID作为名称,也可以自行配置. 一个node只能加入一个Elasticsearch集群当中

  • primary shard
    主分片, 数据存储单位, 通过增加分片进行横向扩展, 创建索引时指定且创建后不能再修改

  • replica shard
    副本分片, 作为shard的数据拷贝, 保障数据不丢失高可用, 同时分担shard的搜索请求, 提升整个集群的吞吐量和性能

  • Index
    索引, 相同结构的文档集合, 类比于数据库的数据库实例(Es 6.0 废除type 后其实就相当于单个数据表了)

  • type
    类型, Es 6.0以后废除, 参考链接: Removal of mapping types

  • Document
    文档,Elasticsearch最小的数据存储单元,JSON数据格式,类似于关系型数据库的表记录(一行数据),结构定义多样化,同一个索引下的document,结构尽可能相同

Lucence检索

Lucence 概念

image-20201012212234564

检索基本流程: 查询分析 => 分词 => 关键词检索 => 搜索排序

倒排索引

传统的我们的检索是通过文章,逐个遍历找到对应关键词的位置。而倒排索引,是通过分词策略,形成了词和文章的映射关系表,从而能够在O(1)时间里通过关键词查找到文章列表. 简单来说就是根据内容找文档, 而像MySQL等的正排索引是根据ID找文档

底层实现是基于:FST(Finite State Transducer)数据结构。lucene从4+版本后开始大量使用的数据结构是FST。FST有两个优点:

  • 空间占用小。通过对词典中单词前缀和后缀的重复利用,压缩了存储空间;

  • 查询速度快。O(len(str))的查询时间复杂度

参考链接🔗:

elasticsearch 倒排索引原理

关于Lucene的词典FST深入剖析

二、Elasticsearch使用

增删改查

  • Get API
  • Delete API
  • Update API
  • Bulk API

搜索

  • Search API
  • Aggregrations
  • Query DSL
  • Elasticsearch SQL

Analyzer分词

  • Standard Analyzer:默认的分词器,按照词切分,并作大写转小写处理
  • Simple Analyzer:按照非字母切分(符号被过滤),并作大写转小写处理
  • Stop Anayzer:停用词(the、is)切分,并作大写转小写处理
  • Whitespace Anayzer:空格切分,不做大写转小写处理
  • IK:中文分词器,需要插件安装
  • ICU:国际化的分词器,需要插件安装
  • jieba分词:时下流行的一个中文分词器

索引管理

alias别名

  • 给多个索引分组

    按照月来建立索引的时候, 可以考虑先按天建立索引,使用 索引模板(indices templates)可以让日志类数据按天自动创建索引,然后使用月度的索引别名完成按天索引的分类

  • 灵活变更索引, 对索引进行修改的时候无需修改代码, 零停机迁移索引数据

    比如需要变更索引(修改分片、mapping映射、 重命名…)的时候只需要将新建的索引绑定对应的别名, 等数据迁移完毕后再将旧索引与别名绑定关系删除即可

    零停机迁移参考官方文档: Changing Mapping with Zero Downtime

  • 可以用于创建相同索引的不同 “视图”

    适用于多租户的场景, 比如需要针对不同用户看到的某个索引下不同数据, 就可以通过创建 filtered alias(筛选别名)来进行筛选

  • 多个物理索引并且需要通过 alias 写入的场景

    针对多个物理索引并且需要通过 alias 写入的时候可以通过指定 write index, 然后针对指向多个索引的别名的所有索引和更新请求将尝试解析为一个索引,即写索引. 每个别名只能将一个索引分配为一次write索引。 如果未指定write索引且别名引用了多个索引,则不允许写入

示例操作:

1
2
3
4
5
6
7
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "remove" : { "index" : "test1", "alias" : "alias1" } },
{ "add" : { "index" : "test2", "alias" : "alias1" } }
]
}
1
2
3
4
5
6
7
8
9
10
11
12
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{
"add" : {
"index" : "test1",
"alias" : "alias2",
"filter" : { "term" : { "user" : "kimchy" } }
}
}
]
}

参考链接 : Index Aliases

rollover API

根据日期滚动创建索引

参考链接 : Rollover Index

索引映射

  • Mapping设置
  • Index Template
  • Dynamic Template: 根据Elasticsearch识别的数据类型,结合字段名称,来动态设定字段类型

参考链接:

routing路由

当索引一个文档的时候,文档会被存储到一个主分片中。 Elasticsearch 如何知道一个文档应该存放到哪个分片中呢?当我们创建文档时,它如何决定这个文档应当被存储在分片 1 还是分片 2 中呢?

实际上,这个过程是根据下面这个公式决定的:

shard = hash(routing) % number_of_primary_shards

routing 是一个可变值,默认是文档的 _id ,也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字,然后这个数字再除以 number_of_primary_shards (主分片的数量)后得到 余数 。这个分布在 0number_of_primary_shards-1 之间的余数,就是我们所寻求的文档所在分片的位置

这也是我们为什么要在创建索引的时候就确定好主分片的数量并且永远不会改变这个数量:因为如果数量变化了,那么所有之前路由的值都会无效,文档也再也找不到了. (新版本里Es可以支持在一定条件限制下, 对某个索引的主分片进行Split拆分或 Shrink缩小, 只能拆分成 n 倍或缩小至 主分片数/ n 个, 而不能从 8 =》9 或者 9 =》8这种)

三、集群架构

集群角色

  • Master Node 主节点: 全局唯一, 集群选举. 负责集群层面的相关操作、管理集群变更. 主节点也可以作为数据节点, 但不推荐
  • Data Node数据节点: 负责保存数据、执行数据相关操作. 一般数据读写流程也只与数据节点交互
  • Ingest Node预处理节点: Es 5.0版本引入的概念. 预处理操作允许在索引文档之前, 即数据写入之前通过定义processors处理器和pipeline管道对数据进行某种转换. 参考链接:Ingest Node
  • Coordinating node协调节点: 协调节点将客户端请求转发给保存数据的Data Node, 每个数据节点在本地执行请求, 并将结果返回协调节点, 协调节点收集这些数据结果然后合并为单个全局结果
  • Tribe Node部落节点, 被废除, 被 Cross-cluster search取代

集群健康状态

  • Green: 所有主分片和副本分片正常运行
  • Yelloew: 主分片正常, 但不是所有的副本分片都正常运行, 这意味着可能存在单点故障风险
  • Red: 有主分片不能正常运行

每个索引也有以上三个状态, 假设其中某个副本分片不正常即为 Yellow状态

可以通过 curl -X GET "localhost:9200/_cluster/health?pretty" 查询集群状态, 具体参考:Cluster health API

集群扩容

当扩容集群、 添加节点时, 分片会均衡地分配到集群的各个节点, 从而对索引和搜索过程进行负载均衡, 这些都是系统自动完成的

参考: 扩容设计

主要内部模块

####Cluster

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

  • 管理集群状态,将新生成的集群状态发布到集群所有节点。
  • 调用allocation模块执行分片分配,决策哪些分片应该分配到哪个节点
  • 在集群各节点中直接迁移分片,保持数据平衡。

allocation

封装了分片分配相关的功能和策略,包括主分片的分配和副分片的分配,本模块由主节点调用。创建新索引、集群完全重启都需要分片分配的过程。

Discovery 发现模块

负责发现集群中的节点,以及选举主节点。当节点加入或退出集群时,主节点会采取相应的行动。从某种角度来说,发现模块起到类似ZooKeep町的作用,选主并管理集群拓扑。

gateway

负责对收到Master广播下来的集群状态(clusterstate)数据的持久化存储,并在集群完全重启时恢复它们。

Indices 索引模块

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

HTTP

HTTP模块允许通过 JSON over HTTP 的方式访问ES的API. HTTP模块本质上是完全异步的,这意味着没有阻塞线程等待响应。使用异步通信进行HTTP的好处是解决了C10k问题。10k量级的并发连接)。在部分场景下,可考虑使用HTTP keepalive以提升性能。注意:不要在客户端使用HTTP chunking

Transport传输模块

用于集群内节点之间的内部通信。从一个节点到另-个节点的每个请求都使用传输模块。如同HTTP模块,传输模块本质上也是完全异步的。传输模块使用TCP通信,每个节点都与其他节点维持若干TCP长连接。内部节点间的所有通信都是本模块承载的。

Engine

Engine模块封装了对Lucene的操作及translog的调用,它是对一个分片读写操作的最终提供者。

ES使用Guice框架进行模块化管理。Guice是Google开发的轻量级依赖注入框架CioC)。软件设计中经常说要依赖于抽象而不是具象,IoC就是这种理念的实现方式,并且在内部实现了对象的创建和管理