写点什么

ElasticSearch 笔记

用户头像
石刻掌纹
关注
发布于: 2020 年 08 月 03 日

CRUD


Bulk Api

批量查询

数据迁移

创建新的索引,reindex 数据

POST _reindex

{

"source": {

"index": "旧索引"

},

"dest": {

"index": "新索引"

}

}


倒排索引

  • 倒排索引包含两个核心组成部分

  • 单词词典,记录所有文档的单词,记录单词到倒排列表的关联关系

  • 单词词典一般比较大,可以通过 B+或者哈希拉链法实现,以满足高性能查询

  • 倒排列表(Posting List),记录了单词对应的文档组合,由倒排索引项组成

  • 倒排索引项

  • 文档 ID

  • 词频 TF - 该单词在文档中出现的次数,用于相关性评分

  • 位置(Position)- 单词在文档中出现的位置,用于语句搜索

  • 偏移(Offset)- 记录单词的开始结束位置,实现高亮显示

Analyzer

  • 文本分析是把全文本转换成一系列单词的过程,也叫分词

  • Analyis 是将文本转换成倒排索引中 Terms 的过程

  • 除了在数据写入时转换词条,匹配 Query 语句时也需要用相同分析器对查询语句进行分析

  • 分词器是专门处理分词的组件,Analyzer 由三部分组成

  • character filters - 针对原始文本进行处理,例如去除 Html

  • tokenizer - 按照规则切分单词,例如空格

  • token filter - 将切分的单词进行加工,小写,删除 stop words(in/on/is/the),增加同义词

ElasticSearch 内置分词器

URI Search






能否更新 Mapping 的字段类型

Index Options

Null Value

Copy_to

这种情况 source 中并没有 fullName,但可以指定该字段进行查询:


Index Templates

用于指定自动生成的 mapping 规则

工作方式

Term 精确查询

  • 包含前缀查询、范围查询、通配符查询

  • ES 中,term 查询不管是 keyword 还是 text 对输入都不做分词,会将输入作为一个整体,在倒排索引中查找准确词项,并且使用相关度算分公示为每个包含该词项的文档进行相关度算分

  • ES 在插入数据默认会将,大写英文转成小写,如需完全匹配:"字段.keyword" : "查询值"

  • 大部分情况 term 查询需要忽略 TF-IDF 计算,避免相关性算分的开销,可以将 query 转成 filter,并且 filter 可以有效利用缓存

  • 日期区间

  • term 查询其实是包含关系,而不是精确匹配。比如,["A", "B"] 此时搜索 "A",依然可以匹配到

Match 全文本查询

  • 索引和搜索时都会分词,查询字符串先传递到一个合适的分词器,然后生成共查询的词项列表

  • 查询的时候会对输入的查询进行分词,然后每个词项逐个进行底层查询,最终将结果进行合并,并为每个文档进行算分。 - 例如查询"Matrix reloaded",会查到 matrix 或 reload 的所有结果

  • 如果需要结果两者都存在,则需要添加 AND 条件

相关性算分

TF-IDF

  • 相关性 - relevance

  • 搜索的相关性算分,描述了一个文档和查询语句的匹配程度。ES 会对每个匹配查询条件的结果进行算分_score

  • 打分的本质是排序,需要把最符合用户需求的文档排在前面。ES 5 之前默认的相关性算分采用 TF-IDF,现在采用 BM25

  • 词频

  • Term Frequency:检索词在一篇文档中的出现频率(检索词出现的次数 / 文档总字数)

  • 度量一条查询和结果文档相关性的简单方法:简单将搜索中每个词的 TF 进行相加,如"区块链的应用":TF(区块链) + TF(的) + TF(应用)

  • stop word:"的"在文档中出现了很多次,但对相关度没有贡献,不应该考虑它的 TF



BM25

  • 从 ES 5 开始,默认打分算法改为 BM25

  • 和经典的 TF-IDF 相比,当 TF 无限增加时,BM25 算分会趋于一个数值

Boosting Relevance

Bool Query 组合查询



  • filter,must_not 属于 Filter Context 不会影响算分,should,must 属于 Query Context 查询会影响算分

  • 通过指定 boost 字段,可以影响算分

  • 通过指定 negative 字段,可以减少算分,将不相关值排到末尾

should 算分过程

  • 查询满足 should 条件的所有 doc

  • 加和满足条件查询的评分

  • 乘以匹配语句总数

  • 除以所有语句总数

这种简单将分数加和的算分机制,有可能导致最佳匹配的 doc 分数反而偏低

Disjunction max query

  • 将任何与任一查询匹配的文档作为结果返回。采用字段上最匹配的评分作为最终评分返回

Multi Match 单字符串多字段查询

三种场景

  • 最佳字段(Best Fields)

  • 当字段之间相互竞争,又相互关联。例如 title 和 body 这样的字段。评分来自最匹配字段

  • Best Fields 是默认类型,可以不用指定

  • 多数字段(Most Fields)

  • 处理英文内容时:一种常见的手段是,在主字段(English Analyzer),抽取词干,加入同义词,以匹配更多的文档。相同文本,加入子字段(Standard Analyzer),以提供更加精确的匹配。其他字段作为匹配文档提高相关度信号。匹配字段越多分数越高

  • 用 English 分词器分词,用 Standard 分词器保证搜索精度

  • 跨字段搜索(Cross Field)

  • 对于某些实体,例如图书,人名,地址信息等,需要在多个字段中确定信息,单个字段只能作为整体的一部分,希望在列出的的字段中找到尽可能多的词

  • 支持使用 Operator

  • 可以在搜索时为单个字段提升权重

Function Score Query

按照受欢迎度提升权重

  • 希望将 skuId 大的数据,放在搜索列表相对靠前的位置。同时搜索的评分还是要作为主要依据

  • 新的算分 = 老的算分 * skuId

  • 使用 modifier 平滑曲线,新的算分 = 老的算分 * log(1 + skuId)

  • 使用 factor,新的算分 = 老的算分 * log(1 + factor * skuId)


  • seed 不变,随机返回的数据排序就不会改变

搜索建议

Suggester API


  • 搜索"pple"

  • 每个建议都包含了一个算分,相似性是通过 Levenshtein Edit Distance 算法实现的。核心思想就是一个词改动多少字符可以和另外一个词一致。提供了很多可选参数来控制相似性的模糊程度。例如:"max_edits"

  • 几种 suggest_mode

  • Missing - 如果索引中已经存在就不提供建议

  • Popular - 推荐出现频率更加高的词

  • Always - 无论是否存在都提供建议

  • 默认首字母不一致就不会推荐,但是如果将 prefix_length 设置为 0,就会为 pple 建议 Apple

自动补全和基于上下文提示

Auto Complete

  • Completion Suggester 提供了自动完成(Auto Complete)的功能。用户每输入一个字符,就需要即时发送一个查询请求到后端查找匹配项

  • 对性能要求比较严苛。ES 采用了不同的数据结构,并非倒排索引。而是将 Analyze 的数据编码成 FST 和索引一起存放。FST 会被 ES 整个加载进内存,速度很快

  • FST 只能用于前缀查找

  • 设置字段 mapping 为 completion


Context Suggester

  • 可以定义两种类型的 Context

  • category - 任意字符串

  • geo - 地理位置信息

  • 实现 context suggester 的具体步骤

  • 定制一个 mapping

  • 索引数据,并且为每个文档加入 context 信息

  • 结合 context 进行 suggestion 查询,根据不同上下文,自动提示

精准度和召回率

跨集群搜索 - Cross Cluster Search

  • ES 5.3 引入了跨集群搜索功能,推荐使用

  • 允许任何节点扮演 federated 节点,以轻量的方式将搜索请求代理

  • 不需要以 client node 形式加入其他集群

  • 配置及查询

集群分布式模型及选主与脑裂问题

分布式特性

节点

Coordinating Node

Data Node


  • ES 中主分片和副本分片一定不会分配在一个节点上

Master Node

Master Eligible Node & 选主过程


集群状态

脑裂问题

如何避免脑裂

配置参数建议

分片与集群故障转移

主分片 - 提升系统存储容量

副本分片 - 提高数据可用性

分片数的设定

  • Es 7.0 之后默认主分片数量是 1,副本分片数量是 0

  • 单节点集群,副本无法分片,集群状态为黄色

  • 增加一个数据节点,集群状态转为绿色,集群具备故障转移能力

  • master 会决定分片分配到哪个节点

  • 通过增加节点,提高集群计算能力

故障转移

集群健康状态

文档分布式存储

  • 文档会存储在具体某个主分片和副本分片上:例如 文档 1,会存储在 R0 和 R1 分片上

  • 文档到分片的路由算法:shard = hash(_routing) % number_of_primary_shards

  • Hash 算法确保⽂文档均匀分散到分⽚片中

  • 默认的 _routing 值是⽂档 id

  • 可以⾃行制定 routing 数值,例如⽤相同国家的商品,都分配到指定的 shard

  • 设置 Index Settings 后, Primary 数不能随意修改的根本原因

  • 文档更新过程

  • 用户发出更新请求到一个节点

  • 该节点扮演 Coordinating 角色,通过 hash 算法算出这个数据应该被路由到哪个分片,将该请求发送到对应分片

  • Es 的更新过程是先 delete,后 index

  • 更新完成后通知 Coordinating 节点更新成功

  • Coordinating 节点返回用户响应结果

  • 删除文档过程

  • 用户发出更新请求到一个节点

  • 该节点扮演 Coordinating 角色,通过 hash 算法算出这个数据应该被路由到哪个分片,将该请求发送到对应分片

  • 对应节点将数据删除,并且通过 cluster state 找到备份节点,将删除请求发送给备份节点

  • 备份分片节点删除后,通知主分片节点

  • 主分片节点删除完成后通知 Coordinating 节点删除成功

  • Coordinating 节点返回用户响应结果

分片及其生命周期

  • 分片内部原理

  • 什么是分片

  • ES 中最⼩的⼯作单元 / 是一个 Lucene 的 Index

  • 一些问题:

  • 为什么 ES 的搜索是近实时的(1 秒后被搜到)

  • ES 如何保证在断电时数据也不会丢失

  • 为什么删除⽂档,并不会⽴刻释放空间

  • 倒排索引不可变性

  • 倒排索引采⽤ Immutable Design,一旦生成,不可更改

  • 不可变性,带来了的好处如下:

  • 无需考虑并发写文件问题,避免锁机制带来的性能问题

  • 一旦读入内核的⽂件系统缓存,便留在缓存中。只要文件系统存有⾜够的空间,大部分请求就会直接请求内存,不会命中磁盘,提升了很⼤的性能

  • 缓存容易生成和维护 / 数据可以被压缩

  • 不可变更性,带来了的挑战:如果需要让一个新的文档可以被搜索,需要重建整个索引。

Lucene Index

  • 在 Lucene 中,单个倒排索引文件被称为 Segment。Segment 是自包含的,不可变更的。多个 Segments 汇总在一起,称为 Lucene 的 Index,其对应的就是 ES 中的 Shard

  • 当有新文档写入时,会生成新 Segment,查询时会同时查询所有 Segments,并且对结果汇总。Lucene 中有⼀个文件,用来记录所有 Segments 信息,叫做 Commit Point

  • 删除的文档信息,并不会被立刻删除,而是保存在“.del”文件中

ES Refresh

  • Es 在写入数据时会将数据先写入 Index Buffer 的内存空间

  • 一定时间(默认一秒)后,Index Buffer 会将其中的所有数据写入 segment 中,这个过程就叫 Refresh

  • 当文档被 Refresh 到 segment 中后才可以被搜索到

  • Refresh 频率:默认 1 秒发生一次,可通过 index.refresh_interval 配置。Refresh 后,数据就可以被搜索到了。这也是为什么 ElasticSearch 被称为近实时搜索

  • Index Buffer 被占满时,会触发 Refresh,默认值是 JVM 的 10%

Transaction Log

  • 如果有大量写入,就会产生大量 segment,将 segment 写入磁盘的过程相对也是比较耗时的。所以 ES 会借助文件系统缓存,Refresh 时首先将 segment 写入缓存以开放查询

  • 为了保证数据不会丢失,所以在 Index 文档时,同时会写 Transaction Log,现在版本默认落盘(顺序写),每个分片有一个 Transaction Log

  • 在 Es Refresh 时,Index Buffer 会被清空, Transaction Log 不会清空

  • 所以就算断电 Es 中的数据也不会丢失

Es Flush

  • 调用 Refresh,清空 Index Buffer

  • 调用 fsync,将缓存中的 segment 写入磁盘

  • 清空 Transaction Log

  • 因为这个过程比较重,默认 30 分钟一次

  • Transaction Log 写满(默认 512M)后也会触发

Es Merge

  • 当 Flush 完成后 segment 会从内存被写入磁盘,随着时间流逝,磁盘上的 segment 文件也会越来越多,所以需要定期对其进行处理

  • Merge 可以将多个 segment 合并成一个,同时会将.del 文件中的数据真正删除

  • Es 和 lucene 会自动进行 merge 操作

剖析分布式查询及相关性算分

分布式搜索的运行机制

  • Es 的搜索会分两阶段进行(举例:目前有三个主分片,各有一个备份分片)

  • 第一阶段 Query

  • ⽤用户发出搜索请求到 ES 节点。节点收到请求后, 会以 Coordinating 节点的身份,在 6 个主副分⽚片中随机选择 3 个分片,发送查询请求

  • 被选中的分片执行查询,进行排序。然后每个分片都会返回 From + Size 个排序后的文档 Id 和排序值 给 Coordinating 节点

  • 第二阶段 Fetch

  • Coordinating Node 会将 Query 阶段,从每个分片获取的排序后的文档 Id 列表,重新进行排序。选取 From 到 From + Size 个⽂档的 Id

  • 以 multi get 请求的方式,到相应的分⽚获取详细的⽂档数据

  • Query then Fetch 潜在问题

  • 性能问题

  • 每个分片需要查询的文档个数 = from + size

  • 如果系统中主分片数设置比较大,协调节点就需要处理很多数据:number_of_shard * (from + size)

  • 所以如果在处理深度分页时,性能会有很大影响

  • 相关度算分

  • 每个分片都是基于自己分片上的数据进行相关度计算。如果数据量较少,就会导致算分偏离。相关性算分在分片之间是相互独立。当文档总数很少时,如果主分片大于 1,主分片数越多,相关性算分会越不准

  • 解决算分不准的方法

  • 数据量不大时,可以将主分片数设置为 1

  • 当数据量足够大时,只要保证数据均匀分散在各个分片上,结果一般也就不会出现偏差

排序

  • Elasticsearch 默认采⽤用相关性算分对结果进行降序排序

  • 可以通过设定 sort 参数,⾃行设定排序

  • 如果不指定_score,算分为 Null

排序过程

  • 排序是针对字段原始内容进行的,倒排索引无法发挥作用

  • 需要用到正排索引,通过文档 id 和字段快速得到字段原始内容

  • Es 中有两种实现方式

  • Fielddata

  • Doc Values(列式存储,对 Text 类型无效)

  • Doc Values vs Fielddata

  • 关闭 Doc Values

  • 默认启用,可以通过 mapping 设置关闭

  • 可以增加写入数据的速度 / 减少磁盘空间

  • 如果重新打开,需要重建索引

  • 什么时候需要关闭

  • 明确该数据不需要做排序和聚合分析

分页

  • 默认情况下,查询按照相关度算分排序,返回前 10 条记录

  • from:开始位置,size:期望获取文档总数

  • 深度分页问题

  • 当一个查询:From = 990,size = 10

  • 会在每个分片上都获取 from + size 个文档,这个例子也就是 1000。然后通过 Coordinating Node 聚合所有结果。最后通过排序取前 1000 个文档

  • 页数越深,占用内存越多。为了避免深度分页带来的内存开销,Es 默认限定到 10000 个文档

Search After 避免深度分页

  • 避免深度分页的性能问题,可以实时获取下一页文档信息

  • 限制

  • 不支持指定页数(from)

  • 只能往下翻

  • 第一步搜索必须指定 sort,并且保证值是唯一的(可以通过加入_id 保证唯一性)

  • 然后使用上一次最后一个文档的 sort 值进行查询

  • 解决深度分页原理

  • 假定 size 是 10,查询 990 - 1000

  • 通过唯一排序值定位,将每个分片要处理的文档数都控制在 10

Scroll API

  • 调用第一次,指定 scroll 存活时间,基于该请求创建一个快照,但是如果有新的数据写入,无法被查到

  • 每次查询后,输入上一次的 scroll_id

  • scorll 查询适合作用于归档数据,例如导出全部数据

Es 并发问题

  • Es 采用的是乐观锁并发控制

  • Es 中文档是不可变更的。如果更新一个文档,会将旧文档标记为删除,同时增加一个全新的文档,并将文档的 version 字段 + 1

  • 内部版本控制

  • If_seq_no + If_primary_term

  • 外部版本控制

  • version + version type = external

Bucket & Metric

  • Metric Aggregation

  • Aggregation 属于 search 的一部分

  • 单值分析:只输出一个分析结果

  • min,max,avg,sum

  • Cardinality(类似 distinct,count)

  • 多值分析:输出多个分析结果

  • stats,extended stats

  • percentile,percentile rank

  • top hits

  • Bucket Aggregation

  • 按照一定的的规则,将文档分配到不同桶中,从而达到分类目的

  • 常见的 Bucket Aggregation

  • Terms

  • 数字类型

  • Range / Data Range

  • Histogram / Data Histogram

  • 支持嵌套,也就是桶中依然可以分桶

  • Text 字段默认不支持 Terms Aggregation 分析,需要打开 fielddata

  • keyword 字段默认支持 fielddata


优化 Term 聚合性能

  • 可以在 keyword 字段上,将该字段打开,每当有新的数据写入,他的 term 就会被加载到 cache 中,此时做 Term Aggregation 性能就会提升

  • 适合场景:频繁做聚合查询并且索引在不断写入

Range & Histogram 聚合直方图分桶

  • 按照数字范围进行分桶

  • 在 Range Aggregation 中可以自定义 key

  • Histogram Aggregation

  • Bucket 聚合分析允许通过子聚合进一步分析,子聚合分析可以是 Bucket 或者 Metric

聚合的作用范围和排序

  • ES 聚合分析的默认作⽤用范围是 query 的查询结果集

  • 同时 ES 还⽀支持以下⽅方式改变聚合的作⽤用范围

  • Filter 可以在具体的 Aggregation 中做限定

  • Post_Filter 对指定字段分桶,并显示该字段指定值的信息

  • Global 指定 global 的聚合统计,会忽略掉 query 中 range 的限定

排序

  • 指定 order,按照 count 和 key 进行排序

  • 按照聚合结果进行排序


  • 默认情况,按照 count 降序排序

  • 指定 size,就能返回相应的桶

聚合精准度的问题

  • Es 对海量数据的分析,会损失精准度,满足实时性的需求

  • 近似统计法

  • 如果数据量很大,并且精准度要求很高,应该使用 Hadoop

  • 如果精度要求很高,实施性要求也很高,数据量不是很大,Es 可以满足需求

  • 当数据量越来越大,Es 将数据分散到不同分片,此时做统计 Es 会采用近似计算,牺牲精准度换取实时性

  • Min 的聚合过程

  • 假设数据有三个分片

  • Coordinating Node 会在三个节点上拿到各自的最小值

  • Coordinating Node 在三个最小值中找到最小值,返回

  • Term 的聚合结果可能存在偏差

  • 在 Terms Aggregation 的返回中有两个特殊的数值

  • doc_count_error_upper_bound : 被遗漏的 term 分桶,包含的⽂档,有可能的最⼤值

  • sum_other_doc_count: 除了了返回结果 bucket 的 terms 以外,其他 terms 的文档总数(总数-返回的总数)

  • 举例:数据是两个分片,需要在两个分片上找到各自 bucket 中 count 最多的 top3

  • 第一个分片 count 最多的 top3 是 ABC bucket,舍弃掉了 D bucket

  • 第二个分片 count 最多的 top3 是 ABD bucket,舍弃掉 C bucket

  • 两个分片结果汇总,count 为 A(12)、B(6)、C(4)、D(3),

  • 所以最终结果会舍弃掉 D bucket,返回的 top3 是 ABC bucket,但其实 D bucket 的 count 数是 6,大于 C bucket 的 4,结果存在误差

  • 如何解决 Term 结果存在的偏差

  • Terms 聚合分析不准的原因:数据分散在多个分⽚上, Coordinating Node 无法获取数据全貌

  • 解决⽅方案 1:当数据量量不大时,设置 PrimaryShard 为 1;实现准确性

  • 解决方案 2:在分布式数据上,设置 shard_size 参数,提高精确度

  • 原理:每次从 Shard 上额外多获取数据(bucket)提升准确率,也就是之前每个分片只取 3 个,如果每个分片都拿 4 个,结果自然就会准确

  • 打开 show_term_doc_count_error,帮助我们了解聚合分析的结果是否精确

  • shard_size 设定

  • 调整 shard size ⼤小,降低 doc_count_error_upper_bound 来提升准确度

  • 增加整体计算量,提高了准确度,但会降低响应时间

  • Shard Size 默认⼤小设定:shard size = size * 1.5 +10

Es 对象及 Nested

  • Elasticsearch 并不擅长处理关联关系。我们一般采用以下四种方法处理关联

  • 对象类型

  • 嵌套对象(Nested Object)

  • ⽗子关联关系(Parent / Child )

  • 应⽤端关联

  • 对象查询带来的问题

  • 插入一部电影的数据,其中包含两个演员名字

  • 查询的演员名称是没有的,但是返回了搜索结果

  • 为什么会搜索到不需要的结果

  • 存储时,Es 对于内部对象的边界并没有考虑在内,JSON 格式被处理成扁平式键值对的结构

  • 可以⽤ Nested Data Type 解决这个问题

  • Nested 数据类型:允许对象数组中的对象被独⽴索引

  • 使⽤用 nested 和 properties 关键字,将所有 actors 索引到多个分隔的文档

  • 在内部, Nested ⽂档会被保存在两个 Lucene 文档中,在查询时做 Join 处理

  • Nested Aggregation,需要添加 nested 关键字,如果没有添加普通 aggs 是不工作的

Update By Query & Reindex API

  • 一般在以下⼏种情况时,我们需要重建索引

  • 索引的 Mappings 发⽣变更:字段类型更改,分词器及字典更新

  • 索引的 Settings 发生变更:索引的主分⽚数发生改变

  • 集群内,集群间需要做数据迁移

  • Update By Query:在现有索引上重建

  • 为索引增加子字段

  • 增加子字段后,对于新写入的数据可以查询到,但是对于原有数据,通过子字段无法查询到

  • 此时需要执行 update_by_query,之后即可返回数据

  • Reindex:在其他索引上重建索引

  • Reindex API ⽀持把文档从⼀个索引拷贝到另外⼀个索引

  • 使⽤ Reindex API 的一些场景

  • 修改索引的主分片数

  • 改变字段的 Mapping 中的字段类型

  • 集群内数据迁移 / 跨集群的数据迁移

  • 注意事项

  • reindex 的索引 source 字段必须是 enable 的

  • reindex 只会在目标索引中创建不存在的文档,如果文档已经存在,会导致版本冲突

  • 可以指定 op_type 字段为 create,表示只有没有的数据才会被创建

  • 如果需要跨集群 reindex,需要修改 elasticsearch.yml 指定白名单,并且重启

数据建模

  • 如何对字段进行建模

  • 字段类型

  • 是否需要搜索及分词

  • 是否需要聚合及排序

  • 是否需要额外存储

  • 字段类型

  • Text

  • 用于全文本字段,文本会被 Analyzer 分词

  • 默认不支持聚合分析和排序。需要设置 fielddata 为 true

  • Keyword

  • 用于 id,枚举,及不需要分词的文本。如:电话号码,手机号码,邮政编码,性别等

  • 适用于 Filter(精确匹配),Sort 和 Aggragations

  • 设置多字段类型

  • Es 默认会为文本类型字段设置为 text,并且设置一个 keyword 子字段

  • 在处理人类语言时,通过增加各种分词起,提高搜索结构

  • 数值类型

  • 尽量选择贴近的类型。例如,可以用 byte 就不要用 long

  • 枚举类型:设置为 keyword。即便是数字类型也应该设置为 keyword,获取更好的性能

  • 其他:日期 / 布尔 / 地理信息

  • 检索

  • 如果是不需要搜索的字段,可以将该字段的 index 设置为 false

  • 如果不需要排序或聚合分析,即使是 keyword 字段,也应该将 Doc_values / fielddata 设置为 false

  • 更新频繁,聚合查询频繁的 keyword 字段,应该将 eager_global_ordinals 设置为 true,这个设置可以更好利用缓存特性

  • 一个文档中最好避免大量字段

  • 字段数过多不易维护

  • Mapping 信息保存在 cluster state 中,数据量过大,对集群性能会有影响(Cluster state 信息需要和所有节点同步)

  • 删除或修改数据需要 reindex

  • 默认最大字段是 1000,可以设置 index.mapping.total_fields.limt 限定最⼤大字段数

  • 什么情况会导致文档中有成百上千字段?

  • Dynamic(生产环境中尽量不要打开 Dynamic)

  • true - 未知字段会被自动加入

  • false - 新字段不会被索引,但会保存在 _source 字段中

  • strict - 新增字段不会被索引,文档写入失败,可以控制到字段级别

  • 尽量避免空值 - 在该字段增加 null_value 的设置

Ingest Node

  • Elasticsearch 5.0 后,引入的一种新的节点类型。默认配置下,每个节点都是 Ingest Node

  • 具有预处理理数据的能力,可拦截 Index 或 Bulk API 的请求

  • 对数据进⾏转换,并重新返回给 Index 或 Bulk API

  • ⽆无需 Logstash,就可以进行数据的预处理,例如

  • 为某个字段设置默认值;重命名某个字段的字段名;对字段值进行 Split 操作

  • 支持设置 Painless 脚本,对数据进行更加复杂的加⼯

集群部署

  • 一个节点在默认情况会下同时扮演:master eligible,data node 和 ingest node

  • 生产环境中建议设置单一角色的节点

  • master 节点:负责集群状态(cluster state)管理

  • 通常可以使用低配置的机器

  • 一般生产环境配置 3 台,一个集群只有一个活跃的主节点

  • 负责分片管理,索引创建,集群管理等操作

  • data 节点:负责数据存储和处理客户端请求

  • 应该是用高配置服务器,SSD 提升磁盘吞吐量

  • ingest 节点:负责数据处理

  • 使用高 CPU 配置,低配置磁盘即可

  • coordinating 节点:负责 load balance,降低 master、data 节点负担

  • 数据节点相对有比较大的内存占用

  • coordinating 节点有时会开销很高的查询,导致 OOM

  • 如果和 master 混合部署,这些都有可能影响 master 节点,导致集群的不稳定

  • 当磁盘容量无法满足需求时,读写压力大时,可以增加数据节点。

  • 如果有大量复杂或聚合查询,增加 coordinating node 可以提升查询性能,coordinating 节点之前最好还有一层 LB 来负载均衡,应用只需要和 LB 交互即可完成读写

  • 有些集群需要单独设置 Ingest Node,通过 Pipline 的方式对数据写入做预先处理,可以为其增加写 LB

Hot & Warm Architecture

  • Hot 节点

  • 对于比较新的,热门的数据放在 Hot 节点上

  • 用于数据写入

  • Index 操作对 CPU 和 IO 都有比较高的要求,需要使用高配置服务器,通常使用 SSD

  • Warm 节点

  • 旧的,不常使用的数据放在 Warm 节点上

  • 用于保存只读索引,通常使用大容量磁盘

  • 如何配置

  • 使用 Shard Filtering

  • 标记节点(Tagging)

  • 需要通过 “node.attr” 来标记一个节点

  • 查看节点信息

  • 配置索引到 Hot 节点

  • 创建索引时,将其创建在 Hot 节点上

  • 配置索引到 Warm 节点

  • Index.routing.allocation 是一个索引级的 dynamic setting,可以通过 API 在后期进行设定

  • 当数据很少使用时,将其移动到 Warm 节点

Rack Awareness

  • Es 节点可能分配在多个机架

  • 当一个机架断电,可能会丢失几个节点

  • 如果一个索引相同的主分片和副本分片,同时在这个机架上,就有可能导致数据丢失

  • 通过 Rack Awareness 的机制,就可以尽可能避免将同一个索引的主副分片同时分配在一个机架上


分片设计和管理

  • 单个分片

  • 7.0 开始,新创建一个索引时,默认只有一个主分片

  • 单个分片,查询算分,聚合不准的问题都得以解决

  • 单个索引,单个分片,集群无法实现水平扩展

  • 即使增加新的节点,也无法实现水平扩展

  • 两个分片

  • 随着数据越来越大,集群增加一个节点后,Es 会自动进行分片移动,也叫 Shard Rebalancing

  • 如何设计分片数

  • 当分片数 > 节点数

  • 一旦集群中有新的数据节点加入,分片就可以自动分配

  • 分片在重新分配时,系统不会有 downtime

  • 多分片的好处:一个索引如果分布在不同节点,多个节点可以并行执行

  • 查询可以并行执行

  • 写入可以分散到多个机器

  • 分片过多的副作用

  • Shard 是 Es 实现集群水平扩展的最小单位

  • 过多设置分片数会带来一些潜在问题

  • 每个分片是一个 Lucene 索引,会使用机器资源。过多分片会导致机器额外性能开销

  • 每次搜索请求,需要从每个分片上获取数据

  • 分片的 Meta 信息由 Master 节点维护。过多会增加管理负担。经验值,分片总数控制在 10w 以内

  • 如何确定分片总数

  • 存储角度

  • 日志类应用,单个分片不要超过 50G

  • 搜索类应用,单个分片不要超过 20G

  • 为什么要控制分片大小

  • 提高 Update 性能

  • Merge 时,减少所需资源

  • 丢失节点后,具备更快的恢复速度 / 便于分片在集群内 Rebalancing

  • 如何确定副本分片数

  • 副本是主分片的拷贝

  • 提高系统可用性,相应查询请求,防止数据丢失

  • 需要占用和主分片一样的资源

  • 对性能的影响

  • 副本会降低数据的写入速度:因为需要拷贝,有几分副本就会有几倍的 CPU 资源消耗在索引上

  • 会减缓主分片的查询压力,但是会消耗同样的内存资源

  • 如果机器充足,提高副本数可以提高整体查询的 QPS

容量规划

  • 一个集群需要多少节点,一个索引设置几个分片

  • 规划上需要保持一定余量,当出现负载波动节点丢失,还能正常运行

  • 硬件配置

  • 数据节点尽量使用 SSD

  • 搜索等性能要求高的场景,建议 SSD,按照 1:10 的比例配置内存和硬盘

  • 日志类和查询并发低的场景,可以考虑使用机械硬盘存储,按照 1:50 配置内存和硬盘

  • 单节点数据建议控制在 2TB 以内,最大不超过 5TB

  • JVM 配置机器内存的一半,JVM 最大内存配置不建议超过 32G

  • 拆分索引

  • 读多场景

  • 如果业务上有大量查询是基于一个字段进行 filter,该字段又是一个数量不变的枚举值,例如用户所在地区

  • 如果在单个索引有大量数据,可以考虑拆分成多个索引

  • 查询性能提高

  • 如果要对多个索引查询,也可以在查询中指定多个索引得以实现

  • 写多场景(日志归档)

  • 用户更多会查询近期数据,对旧数据查询相对较少

  • 对数据写入性能要求较高

  • 可以创建基于时间序列的索引

  • 在索引名字中增加时间信息

  • 按照每天 / 每周 / 每月进行划分

  • 基于 Index Alias

  • 好处

  • 随着时间推移,便于对索引做老化处理

  • 可以利用 Hot & Warm Architecture


发布于: 2020 年 08 月 03 日阅读数: 268
用户头像

石刻掌纹

关注

还未添加个人签名 2018.11.22 加入

还未添加个人简介

评论

发布
暂无评论
ElasticSearch笔记