ElasticSearch
2021-04-23 11:42:37 8 举报
AI智能生成
ElasticSearch
作者其他创作
大纲/内容
简介
基于Lucene的分布式, 实时文档存储, 实时分析搜索引擎
特点
分布式
全文检索
ES架构
Gateway
Distributed Lucene Directory
ES的模块
索引模块(Index Module)
搜索模块(Search Module)
映射解析模块(Mapping)
Discovery、Scripting和第三方插件
Discovery是ES的节点发现模块,不同机器上的ES节点要组成集群需要进行消息通信,集群内部需要选举master节点,这些工作都是由Discovery模块完成。支持多种发现机制,如 Zen 、EC2、gce、Azure。
Scripting用来支持在查询语句中插入javascript、python等脚本语言,scripting模块负责解析这些脚本,使用脚本语句性能稍低。ES也支持多种第三方插件
ES的传输模块和JMX
Restful API
基础
可视化界面工具
elasticsearch-head
kibana
基本概念
集群
节点
索引
类型
文档
分片
副本
索引定义
索引(名词) 一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地
索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。
倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。
映射Mapping
核心数据类型
字符串类型
text 用于索引全文值的字段
keyword 用于索引结构化内容的字段, 只能按其确切值进行搜索
数字类型
long, integer, short, byte, double, float, half_float, scaled_float
布尔型: boolean + 日期: date
动态映射
当索引一个包含新域的文档—之前未曾出现-- Elasticsearch 会使用 动态映射 ,通过JSON中基本数据类型,尝试猜测域类型,使用如下规则
别名的使用
由于es的数据不可变性, es允许在索引中新增映射类型, 但是不允许修改已有的映射类型
无缝的从一个索引切换到另一个索引
API使用
分布式工作原理
集群
一个Elasticsearch服务实例就是一个节点(Node)
主节点
1. 不需要涉及到文档级别的变更和搜索等操作
2. 维护并更新Cluster 状态
所有节点信息
所有索引和其相关的Mapping和setting信息
分片路由信息
3. 节点的加入集群及移除
4. 分片的平衡
协调节点
1. 所有节点默认都是 协调节点
2. 请求可以发送到集群中的任一节点, 每个节点都有能力处理任意请求
3. 每个节点都知道集群中任一文档位置,所以可以直接将请求转发到需要的节点上
4. 负责数据的存储和读写的,写入索引,搜索数据,data node
默认一旦master宕机, 所有的写操作都会被拒绝,但是读操作是被允许的
发现机制+选主
Zen Discovery
发现机制
单播列表
discovery.zen.ping.unicast.hosts -- 单播列表
1. 节点启动后, 先ping单播列表中的host(建议配置3个Master-eligible-Node节点的host)
2. 假如单播列表的host参数不存在则ping localhost, 这样默认就是开发集群
3. 当节点联系到单播列表中的成员时,它就会得到整个集群所有节点的状态
4. 联系master节点, 请求加入集群
文件配置发现
通过外部文件提供主机列表
discovery.zen.hosts_provider:file
discovery.zen.hosts_provider:file
如果要让多个node组成一个es集群,首先第一个要设置的参数,就是cluster.name,多个node的cluster.name如果一样,才满足组成一个集群的基本条件。
选主
选主机制
activeMasters / masterCandidates 说明
activeMasters 启用中列表 -- 其他节点认为的master节点
masterCandidates 候选列表 -- 具有候选资格的节点, master.node配置为true旳节点
优先从activeMasters列表中选取
选主流程
1. ping所有节点, 获得并合并所有的 [activeMasters 启用中列表] 和 [masterCandidates 候选列表]
2. 从activeMasters列表选举Master节点
3. 如果activeMaster列表为空, 则从masterCandidates列表选举Master节点
4. 判断临时Master是否是本节点
是: 等待其他节点选我
否: 不接受其他节点的join请求,并向Master节点发送加入请求
选主算法
Bully算法
假定所有节点都有一个唯一的ID,使用该ID对节点进行排序,选择最小的节点作为Master
节点失效检测
主节点
定时ping所有节点
当有节点连不上时, 会执行removeNode.
当在线节点低于法定数量, 会放弃master身份执行rejoin以避免脑裂
当有节点连不上时, 会执行removeNode.
当在线节点低于法定数量, 会放弃master身份执行rejoin以避免脑裂
非主节点
定期pingMaster节点是否活跃,Master下线则触发rejoin重新选举
选主最少节点限制
discovery.zen.minimum_master_nodes
(master_eligible_nodes)/2+1
(master_eligible_nodes)/2+1
1. 参选主节点数达不到最小值的限制,则等待,直到节点数足够可以开始选举
2. 如果只有一个 local 节点那就选出的是自己
最新版本ES 7已经移除minimum_master_nodes配置,让Elasticsearch自己选择可以形成仲裁的节点。
脑裂
原因
网络
节点响应超时
节点负载过大
es实例无响应, 例如较大规模的内存回收操作(STW)
避免方法
1. discovery.zen.ping_timeout(默认值是3秒)
适当增大timeout时间
适当增大timeout时间
2. node.master: true node.data: false
master Node 和 Data Node 分离 -- 默认情况下这两个属性的值都是true
master Node 和 Data Node 分离 -- 默认情况下这两个属性的值都是true
处理方法
1. 重启集群
2. 注意节点的启动顺序, 因为2份数据不一致, 先启动的节点会成为主节点
集群故障检测
通过Ping的方式互检查
主节点负责Ping所有其他节点,判断是否有节点已经挂掉
其他节点也通过Ping的方式判断主节点是否处于可用状态
discovery.zen.fd.ping_interval 节点多久ping一次,默认1s
discovery.zen.fd.ping_timeout 等待响应时间,默认30s
discovery.zen.fd.ping_retries 失败或超时后重试的次数,默认3
处理并发冲突
乐观锁
版本号
Elasticsearch 中对文档的 索引 ,GET 和 delete 请求时,我们指出每个文档都有一个 _version (版本)号,当文档被修改时版本号递增
如果旧版本的文档在新版本之后到达,它可以被简单的忽略
检测变更的版本号是否等于ES中的当前文档版本号
外部系统
可以借助外部系统使用版本控制
PUT /website/blog/2?version=5&version_type=external
检测变更的版本号是否大于ES中的当前文档版本号
No master block 集群缺失主节点
discovery.zen.no_master_block
all 所有操作均不可做,读写、包括集群状态的读写api
write 默认为write,写操作被拒绝执行,基于最后一次已知的正常的集群状态可读
文档分片路由
shard = hash(routing) % number_of_primary_shards
分片数创建索引时便确定, 并且不能修改, 因为分片数涉及到分片路由
交互原理
文档 新建/索引/删除 交互
1. 客户端向 Node 1 发送新建、索引或者删除请求。
2. 节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3,因为分片 0 的主分片目前被分配在 Node 3 上
3. Node 3 在主分片上面执行请求。如果成功了,它将请求并行转发到 Node 1 和 Node 2 的副本分片上。一旦所有的副本分片都报告成功, Node 3 将向协调节点报告成功,协调节点向客户端报告成功
在客户端收到成功响应时,文档变更已经在主分片和所有副本分片执行完成,变更是安全的
文档修改
1. 客户端向 Node 1 发送更新请求。
2. 它将请求转发到主分片所在的 Node 3 。
3. Node 3 从主分片检索文档,修改 _source 字段中的 JSON ,并且尝试重新索引主分片的文档。 如果文档已经被另一个进程修改,它会重试步骤 3 ,超过 retry_on_conflict 次后放弃。
4. 如果 Node 3 成功地更新文档,它将新版本的文档并行转发到 Node 1 和 Node 2 上的副本分片,重新建立索引。 一旦所有副本分片都返回成功, Node 3 向协调节点也返回成功,协调节点向客户端返回成功。
在处理读取请求时,协调结点在每次请求的时候都会通过轮询所有的副本分片来达到负载均衡
优化
ES内部压缩方案
倒排列表 - postings list 的POR压缩
Filter cache 的RBM压缩
分片策略
分片数设置
1. 每个分片占用的硬盘容量不超过ES的最大JVM的堆空间设置(一般设置不超过32G)
预估索引的容量设置分片数, 例如320G的索引容量则至少需要10个分片
预估索引的容量设置分片数, 例如320G的索引容量则至少需要10个分片
2. 分片数不超过节点数的3倍
推迟副本分片分配
默认情况,集群会等待一分钟来查看节点是否会重新加入
通过修改参数 delayed_timeout ,可以延长再均衡的时间
通过修改参数 delayed_timeout ,可以延长再均衡的时间
索引优化
1. 尽量避免使用nested或 parent/child Mapping类型
2. 如果一定要使用nested fields,保证nested fields字段不能过多,目前ES默认限制是50
3. 控制索引的字段数量、mapping深度、索引字段的类型
4. 不需要做模糊检索的字段使用 keyword类型代替 text 类型,这样可以避免在建立索引前对这些文本进行分词
5. 对于那些不需要聚合和排序的索引字段禁用Doc values
查询效率
1. 使用批量请求,批量索引的效率肯定比单条索引的效率要高
2. query_string 或 multi_match 的查询字段越多, 查询越慢。
可以在 mapping 阶段,利用 copy_to 属性将多字段的值索引到一个新字段,multi_match时,用新的字段查询。
可以在 mapping 阶段,利用 copy_to 属性将多字段的值索引到一个新字段,multi_match时,用新的字段查询。
3. 日期字段的查询, 尤其是用now 的查询实际上是不存在缓存的,因此, 可以从业务的角度来考虑是否一定要用now, 毕竟利用 query cache 是能够大大提高查询效率的。
4. 查询结果集的大小不能随意设置成大得离谱的值, 如query.setSize不能设置成 Integer.MAX_VALUE, 因为ES内部需要建立一个数据结构来放指定大小的结果集数据。
5. 避免层级过深的聚合查询, 层级过深的group by , 会导致内存、CPU消耗,建议在服务层通过程序来组装业务,也可以通过pipeline 的方式来优化。
6. cache的设置+使用
filter查询(QueryCache)
聚类或排序(FieldDataCache)
ShardRequestCache
倒排索引-数据结构
单词 - 文档矩阵
单词 - 文档矩阵是表达两者之间所具有的一种包含关系的概念模型
倒排索引基本概念
文档(Document)
文档集合(Document Collection)
文档编号(Document ID)
单词编号(Word ID)
倒排索引(Inverted Index)
单词词典
单词词典是由文档集合中出现过的所有单词构成的字符串集合,单词词典内每条索引项记载单词本身的一些信息以及指向“倒排列表”的指针
倒排列表
倒排列表记载了出现过某个单词的所有文档的文档列表及单词在该文档中出现的位置信息,每条记录称为一个倒排项(Posting)。根据倒排列表,即可获知哪些文档包含某个单词
文档频率信息(有多少个文档包含这个单词)
文档编号(哪个文档包含这个单词)
单词频率信息(这个单词在文档中的出现次数)
单词在文档出现的位置信息
倒排文件
倒排文件是存储倒排索引的物理文件
索引原理
倒排索引被写入磁盘后是不可改变 的:它永远不会修改
不需要锁
一旦索引被读入内核的文件系统缓存,便会留在哪里
其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
写入单个大的倒排索引允许数据被压缩,减少磁盘 I/O 和 需要被缓存到内存的索引的使用量。
存储原理
概念
Lucene 中 按段搜索
段 segment
一个索引文件拆分为多个子文件,则每个子文件叫作段
提交点
当段被批量的写入磁盘, 则会生成一个提交点
事务日志 translog
新建索引 -(1)-> 内存缓存 -(2)-> 文件系统缓存 -(3)-> 磁盘
步骤
1. 用户创建了一个新文档,新文档被写入[内存缓存]中 --未可读
2. 每间隔1秒, [内存缓存]中数据会以段的形式写入[文件系统缓存] --可读
3. [文件系统缓存]触发flush刷新, 段被全量提交, 且一个提交点被写入[磁盘] --持久化
分片每30分钟自动刷新(flush)
translog 太大的时候触发刷新
持久化
由于创建新文档时, 数据并不直接落到磁盘, 因此在断电时会存在数据丢失的风险
1. 用户创建一个文档时, 新文档写入[内存缓存]的同时, 也会被追加到[tanslog]
2. 随着 [translog] 变得越来越大,达到一定程度后索引被刷新,在刷新flush之后,段被全量提交,一个提交点被写入硬盘,并且[translog]被清空
段合并
由于自动刷新流程每秒会创建一个新的段 ,这样会导致短时间内的段数量暴增
每一个段都会消耗文件句柄、内存和cpu运行周期。
每个搜索请求都必须轮流检查每个段;所以段越多,搜索也就越慢
1. 小的段被合并到大的段,然后这些大的段再被合并到更大的段
2. 段合并的时候会将那些旧的已删除文档从文件系统中清除
搜索API
查询/搜索
分页
from, size
分页原理
1. 假设查询请求from=10, size=5
2. 协调节点把查询请求分发到各分片中, 分片查询返回其排序前15的数据到协调节点
3. 假设索引分片数=4, 则协调节点最多会收到60条数据, 并重新进行排序返回5条给客户端
在分布式系统中,对结果排序的成本随分页的深度成指数上升
_all
当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引
除非设置特定字段,否则查询字符串就使用 _all 字段进行搜索
请求体查询
filltter与query的区别: fillter不影响评分
query
fillter
1. 通过倒排索引获取包含该 term 的所有文档
2. 创建 bitset, 一个段对应一个bitset
3. 迭代 bitset(s)
一旦为每个查询生成了 bitsets ,Elasticsearch 就会循环迭代 bitsets 从而找到满足所有过滤条件的匹配文档的集合
一旦为每个查询生成了 bitsets ,Elasticsearch 就会循环迭代 bitsets 从而找到满足所有过滤条件的匹配文档的集合
4. 增量使用计数
查询在最近的 256 次查询中会被用到,那么它就会被缓存到内存中
当 bitset 被缓存后,缓存会在那些低于 10,000 个文档(或少于 3% 的总索引数)的段(segment)中被忽略
query叶子体
match 标准查询
如果在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串
如果在一个精确值的字段上使用它,那么它将会精确匹配给定的值
multi_match 多个字段上执行相同的 match 查询
range 查询找出那些落在指定区间内的数字或者时间
term 查询被用于精确值匹配
terms 查询和 term 查询一样,但它允许你指定多值进行匹配
exists 查询和 missing 查询被用于查找那些指定字段中有值 (exists) 或无值 (missing) 的文档
bool组合查询
must 文档 必须 匹配这些条件才能被包含进来
must_not 文档 必须不 匹配这些条件才能被包含进来
should 如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分
filter 必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
constant_score查询
以非评分模式来执行 term 查询并以1作为统一评分
分布式检索
查询阶段
1. 客户端发送搜索请求到协调节点
2. 查询请求被转发到索引的每个主分片或副本分片中, 分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中
3. 分片返回各自优先队列中所有文档的 ID 和排序值给协调节点
取回阶段
1. 协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求
2. 每个分片加载并 丰富 文档(如果有需要的话),接着返回文档给协调节点
3. 一旦所有的文档都被取回了,协调节点返回结果给客户端。
scroll 游标查询
0 条评论
下一页