ElasticSearch系列 - ElasticSearch基础总结

一、基本概念

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 概念

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

倒排索引

传统的我们的检索是通过文章，逐个遍历找到对应关键词的位置。而倒排索引，是通过分词策略，形成了词和文章的映射关系表，从而能够在 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 索引且别名引用了多个索引，则不允许写入

示例操作:

Copy
curl -X POST "localhost:9200/_aliases?pretty" -H 'Content-Type: application/json' -d'
{
"actions" : [
{ "remove" : { "index" : "test1", "alias" : "alias1" } },
{ "add" : { "index" : "test2", "alias" : "alias1" } }
]
}

Copy
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 识别的数据类型，结合字段名称，来动态设定字段类型

参考链接:

Elasticsearch 的 Index Template 和 Dynamic Template

Mapping

routing 路由

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

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

shard = hash(routing) % number_of_primary_shards

routing 是一个可变值，默认是文档的 _id ，也可以设置成一个自定义的值。 routing 通过 hash 函数生成一个数字，然后这个数字再除以 number_of_primary_shards （主分片的数量）后得到 余数 。这个分布在 0 到 number_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 就是这种理念的实现方式，并且在内部实现了对象的创建和管理