ES避坑指南
Posted @&¥(Y)咖色
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES避坑指南相关的知识,希望对你有一定的参考价值。
基础篇
一.分片
分片是一个功能完整的搜索引擎,它拥有使用一个节点上的所有资源的能力。 索引一旦创建分片数量就已经确定,且不可更改,默认为5个分片,每个分片有1个副本
二.文档元数据;
_index:
索引名,这个名字必须小写,不能以下划线开头,不能包含逗号
_type:
索引下的逻辑分区
一个 _type 命名可以是大写或者小写,但是不能以下划线或者句号开头,不应该包含逗号, 并且长度限制为256个字符
_id:
_id+ _index + _type 组合确定ES中的一个文档
三:更新和冲突
1 为了避免数据丢失, update API 在 检索 步骤时检索得到文档当前的 _version 号,并传递版本号到 重建索引 步骤的 index 请求。 如果另一个进程修改了处于检索和重新索引步骤之间的文档,那么 _version 号将不匹配,更新请求将会失败。
2 这可以通过设置参数 retry_on_conflict 来自动完成, 这个参数规定了失败之前 update 应该重试的次数,它的默认值为 0 。
POST /website/pageviews/1/_update?retry_on_conflict=5
四:查询
1:空搜索:
GET /_search 返回集群中所有索引下的所有文档
2:/_search
在所有的索引中搜索所有的类型
/gb/_search
在 gb 索引中搜索所有的类型
/gb,us/_search
在 gb 和 us 索引中搜索所有的文档
/g*,u*/_search
在任何以 g 或者 u 开头的索引中搜索所有的类型
/gb/user/_search
在 gb 索引中搜索 user 类型
/gb,us/user,tweet/_search
在 gb 和 us 索引中搜索 user 和 tweet 类型
/_all/user,tweet/_search
在所有的索引中搜索 user 和 tweet 类型
3:分页
size
显示应该返回的结果数量,默认是 10
from
显示应该跳过的初始结果数量,默认是 0
在分布式系统中深度分页:
1>现在假设我们请求第 1000 页— 结果从 10001 到 10010 。所有都以相同的方式工作除了每个分片不得不产生前10010个结果以外。 然后协调节点对全部 50050 个结果排序最后丢弃掉这些结果中的 50040 个结果。
可以看到,在分布式系统中,对结果排序的成本随分页的深度成指数上升。这就是 web 搜索引擎对任何查询都不要返回超过 1000 个结果的原因。
4:_all字段
1)当索引一个文档的时候,Elasticsearch 取出所有字段的值拼接成一个大的字符串,作为 _all 字段进行索引 _all字段:String类型
2)除非设置特定字段,否则查询字符串就使用 _all 字段进行搜索。
五:分词
1)分词过程
字符过滤器
首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉html,或者将 & 转化成 and。
分词器
其次,字符串被 分词器 分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
Token 过滤器
最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化 Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像 jump 和 leap 这种同义词)。
2)测试分词分析
GET /_analyze
“analyzer”: “standard”,
“text”: “Text to analyze”
六:映射
1)Elasticsearch 支持如下简单域类型:
- 字符串:
-
ElasticSearch 5.0以后,string类型有重大变更,移除了string类型,string字段被拆分成两种新的数据类型: text用于全文搜索的,而keyword用于关键词搜索。ElasticSearch字符串将默认被同时映射成text和keyword类型,将会自动创建下面的动态映射(dynamic mappings)
“foo”:
“type”: “text”,
“fields”:
“keyword”:
“type”: “keyword”,
“ignore_above”: 256
- 整数 : byte, short, integer, long
- 浮点数: float, double
- 布尔型: boolean
- 日期: date
查询映射:GET /index/_mapping/type ES7去除了type
类型一旦指定,不可更改,可以增加
2)自定义域映射:
“tag”:
“type”: “string”, //类型
“index”: “analyzed” ,
“analyzer:”english”
- index 属性控制怎样索引字符串,可选三个值
analyzed
首先分析字符串,然后索引它。换句话说,以全文索引这个域。
not_analyzed
索引这个域,所以它能够被搜索,但索引的是精确值。不会对它进行分析。
no
不索引这个域。这个域不会被搜索到。
其他简单类型(例如 long , double , date 等)也接受 index 参数,但有意义的值只有 no 和 not_analyzed , 因为它们永远不会被分析。 - analyzer
对于 analyzed 字符串域,用 analyzer 属性指定在搜索和索引时使用的分析器。默认, Elasticsearch 使用 standard 分析器, 但你可以指定一个内置的分析器替代它,例如 whitespace 、 simple 和 english:
2)复杂核心域类型 - 多值域:如果你通过索引数组来创建新的域,Elasticsearch 会用数组中第一个值的数据类型作为这个域的 类型 。
- 空域:null,[]等
- 多层级对象与内部对象的映射:Lucene 不理解内部对象。 Lucene 文档是由一组键值对列表组成的
七、查询表达式
典型结构:
QUERY_NAME:
ARGUMENT: VALUE,
ARGUMENT: VALUE,…
针对某个字段:
QUERY_NAME:
FIELD_NAME:
ARGUMENT: VALUE,
ARGUMENT: VALUE,…
1)分类:
-
叶子语句(Leaf clauses) (就像 match 语句) 被用于将查询字符串和一个字段(或者多个字段)对比。
-
复合(Compound) 语句 主要用于 合并其它查询语句。 比如,一个 bool 语句 允许在你需要的时候组合其它语句,无论是 must 匹配、 must_not 匹配还是 should 匹配,同时它可以包含不评分的过滤器(filters)
2)过滤查询(filter)与评分查询(query): -
过滤(filtering)的目标是减少那些需要通过评分查询(scoring queries)进行检查的文档。
-
通常的规则是,使用查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。
3)最重要的查询 -
match_all
-
match(标准查询):
- 如果你在一个全文字段上使用 match 查询,在执行查询前,它将用正确的分析器去分析查询字符串
- 如果在一个精确值的字段上使用它,例如数字、日期、布尔或者一个 not_analyzed 字符串字段,那么它将会精确匹配给定的值
- 格式:“match”:字段名:字段值
- 返回部分数据:
GET /_search
“query”: “match_all”: ,
“_source”: [ “title”, “created” ]
-
multi_match
“multi_match”:
“query”: “full text search”,
“fields”: [ “title”, “body” ]
-
Range::查询落在指定区间内的时间或数字
“range”:
“age”:
“gte”: 20,
“lt”: 30
-
term查询:
term 查询被用于精确值匹配,这些精确值可能是数字、时间、布尔或者那些 not_analyzed 的字符串:
格式:
“Term”:
“字段名”:”字段值“
-
terms查询:
terms 查询和 term 查询一样,但它允许你指定多值进行匹配。如果这个字段包含了指定值中的任何一个值,那么这个文档满足条件
格式:
“terms”:
“字段名”:[”字段值1“,”字段值2”]
-
exists 查询和 missing 查询
用户查询指定的字段有值或者没有值的情况
格式:
“exists”:
“field”: “字段名”
4)组合多查询
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。
“bool”:
“must”: “match”: “title”: “how to make millions” ,
“must_not”: “match”: “tag”: “spam” ,
“should”: [
“match”: “tag”: “starred” ,
“range”: “date”: “gte”: “2014-01-01”
]
如果没有 must 语句,那么至少需要能够匹配其中的一条 should 语句。但,如果存在至少一条 must 语句,则对 should 语句的匹配没有要求。
五)验证查询
GET /gb/tweet/_validate/query?explain
“query”:
“tweet” :
“match” : “really powerful”
八:排序
1:排序规则
默认按照_score字段排序,可以指定排序的字段
- 单字段排序:
“sort”:
“字段名”:“desc”
- 多级排序:
“sort”:[
“字段名”:desc…
] - 多值字段的排序:
“sort”:
“dates”:
“order”: “asc”,
“mode”: “min”
2: 相关性的计算规则
检索词频率
检索词在该字段出现的频率?出现频率越高,相关性也越高。 字段中出现过 5 次要比只出现过 1 次的相关性高。
反向文档频率
每个检索词在索引中出现的频率?频率越高,相关性越低。检索词出现在多数文档中会比出现在少数文档中的权重更低。
字段长度准则
字段的长度是多少?长度越长,相关性越低。 检索词出现在一个短的 title 要比同样的词出现在一个长的 content 字段权重更大。
链接:
(152条消息) TF-IDF(词频-逆文档频率)介绍_逆文档频率为什么要用对数_北京小辉的博客-CSDN博客
原理篇
一:执行分布式检索
查询阶段
-
客户端发送一个 search 请求到 Node 3 , Node 3 会创建一个大小为 from + size 的空优先队列。
-
Node 3 将查询请求转发到索引的每个主分片或副本分片(轮训发送)中。每个分片在本地执行查询并添加结果到大小为 from + size 的本地有序优先队列中。
-
每个分片返回各自优先队列中所有文档的 ID 和排序值(比如_score)给协调节点,也就是 Node 3 ,它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。
取回阶段
-
协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。
-
每个分片加载并 丰富 文档,如果有需要的话,接着返回文档给协调节点。
-
一旦所有的文档都被取回了,协调节点返回结果给客户端。
备注:深度分页
- 先查后取的过程支持用 from 和 size 参数分页,但是这是 有限制的 。 要记住需要传递信息给协调节点的每个分片必须先创建一个 from + size 长度的队列,协调节点需要根据 number_of_shards * (from + size) 排序文档,来找到被包含在 size 里的文档。
- 但是使用足够大的 from 值,排序过程可能会变得非常沉重,使用大量的CPU、内存和带宽。因为这个原因,我们强烈建议你不要使用深分页
搜索选项
- 偏好preference
bouncing results 问题: 每次用户刷新页面,搜索结果表现是不同的顺序。 让同一个用户始终使用同一个分片,这样可以避免这种问题, 可以设置 preference 参数为一个特定的任意值比如用户会话ID来解决
选项设置:
Preference | Elasticsearch Reference [5.6] | Elastic - 超时问题
Timeout告诉 分片允许处理数据的最大时间
游标查询:
- scroll 查询 可以用来对 Elasticsearch 有效地执行大批量的文档查询,而又不用付出深度分页那种代价。
- 游标查询允许我们 先做查询初始化,然后再批量地拉取结果,会取某个时间点的快照数据
- 深度分页的代价根源是结果集全局排序,如果去掉全局排序的特性的话查询结果的成本就会很低。 游标查询用字段 _doc 来排序
游标查询 Scroll | Elasticsearch: 权威指南 | Elastic
注释:_doc 其实就是按照Lucene 文件结构的当时索引时的先后顺序,那么按道理它就是最快的,_doc值能把保证在每个分片是唯一的,但是不能保证全局唯一
二.索引管理
1)索引设置
- number_of_shards
每个索引的主分片数,默认值是 5 。这个配置在索引创建后不能修改。 - number_of_replicas
每个主分片的副本数,默认值是 1 。对于活动的索引库,这个配置可以随时修改 - 创建时设置:
PUT /my_temp_index
“settings”:
“number_of_shards” : 1,
“number_of_replicas” : 0
- 创建后修改副本数:
PUT /my_temp_index/_settings
“number_of_replicas”: 1
2)类型和映射 - Lucene 没有文档类型的概念,每个文档的类型名被存储在一个叫 _type 的元数据字段上。 当我们要检索某个类型的文档时, Elasticsearch 通过在 _type 字段上使用过滤器限制只返回这个类型的文档。
- 每个 Lucene 索引中的所有字段都包含一个单一的、扁平的模式。一个特定字段可以映射成 string 类型也可以是 number 类型,但是不能两者兼具(不同的类型在一个索引上)。因为类型是 Elasticsearch 添加的 优于 Lucene 的额外机制(以元数据 _type 字段的形式),在 Elasticsearch 中的所有类型最终都共享相同的映射。
- 所以高版本中移除了类型的概念
3)动态映射
1)动态映射设置
true
动态添加新的字段— 缺省
false
忽略新的字段,新的字段不会被加到映射中也不可搜索
strict
如果遇到新字段抛出异常
如: - PUT /my_index
“mappings”:
“my_type”: - “dynamic”: “strict”,
“properties”:
“title”: “type”: “string”,
“stash”:
“type”: “object”, - “dynamic”: true
- 配置参数 dynamic 可以用在根 object 或任何 object 类型的字段上
如果遇到新字段,对象 my_type 就会抛出异常。 而内部对象 stash 遇到新字段就会动态创建新字段。
2)自定义动态映射
- 日期检测
默认检测格式:yyyy/MM/dd HH:mm:ss Z||yyyy/MM/dd Z
修改日期检测格式(需在创建索引时指定):
修改映射格式
或者在创建映射时指定: - “create_date” :
“type” : “date”,
“format” : “MM/dd/yyyy”
3)重新索引数据
重新索引数据
干货 | 中小企业选型 Elasticsearch 避坑指南
1、线上常见问题
在我线下对接企业或线上交流的时候,经常会遇到各种业务场景不同的问题。
比如,常见问题归类如下:
常见问题1:ES 适合场景及架构选型问题。
公司的核心业务是做企业员工健康管理,数据来自电子化后的员工体检报告以及各种健康数据采集设备,均存储在关系型数据库中。
先计划搞健康大数据分析,比如某企业内按部门,年龄段等对现有数据对比分析等。请问ES适合这个场景使用吗?如果适合,大致的架构是怎样的?
常见问题2:节点偶然下线问题。
运输数据场景,批量写入导致 ES 宕机,集群偶然下线后导致无法上线,怎么解决?
常见问题3:数据不一致问题。
在原有的集群规模的数据非常大的基础上,要删除接近2/3的数据。这时候,两个集群出现了数据不一致的情况,如何排查?
常见问题4:集群重启时间超过20小时以上。
超过8小时的时候,没有引起重视,后面起不起来了,才发现是大问题。
实地环境排查及大量沟通发现,这些后期出现的问题或者“坑”,前期规避的话,成本会更低。
2、发现的潜在的“坑”
如下的坑,都是中小型企业现场环境排查、腾讯会议交流等发现的。
提前声明:对于一些大型企业、大厂不见得适用,毕竟场景不同,得具体问题具体分析。
(1)没有选择相对新的8.X
版本,而是选择了 6.X
版本。
原因:对接 API 方便。
(2)一台高配物理机(如:256GB内存,64核CPU)部署一个节点,资源利用率非常低。
(3)不熟悉 Linux
,集群部署依然基于 Windows
服务器。
(4)数据同步工具自己开发“另起炉灶”,关键功能和性能尚不如 Logstash
等成熟工具。
(5)主分片设定未考虑集群未来的可横向扩展性。
(6)批量写入不考虑集群性能上限,直至节点宕机脱离集群。
(7)不借助可视化工具:Kibana monitoring
监控集群,甚至 head
插件也没有用起来,出现问题不知道如何排查。
(8)命令行 DSL
仍然借助 Postman
等工具实现。
(9)Wildcard
模糊匹配召回结果符合预期,就大量不计后果的使用。
(10)查询细节参数不了解,能用起来就不关心其他。
3、Elasticsearch 常见认知“误区”
认知误区1:Elasticsearch 是关系型数据库。
实际上,Elasticsearch是非关系型数据库,不支持严格的关系数据模型,而是采用文档型存储。
认知误区2:Elasticsearch 只适用于搜索。
Elasticsearch不仅适用于搜索,还支持聚合、分析等功能。
认知误区3:Elasticsearch 无需预处理数据。
Elasticsearch需要预处理数据,并对数据结构有严格的要求,否则可能导致检索效果不佳。
认知误区4:Elasticsearch 可以无限扩展。
(1)纵向扩展得看机器是否支持动态扩内存、CPU等资源,取决于硬件。
(2)横向扩展得看多节点集群规模能否适配性能指标,不见得是机器越多越好。
认知误区5:Elasticsearch 安全性很高。
Elasticsearch 本身 7.1 之前不提供严格的安全性,需要通过相关的插件或配置来实现安全性。7.1(含)之后 xpack 基础功能免费,8.X 之后安全成为必选项!
认知误区6:Elasticsearch 无需维护。
不止要维护,Elasticsearch 需要定期维护,包括数据备份(借助快照和恢复功能)、性能优化、安全更新等。
4、避坑方案探讨
4.1 Elasticsearch 版本及架构选型避坑
关于版本选型,Elastic 官方工程师如是说:“我完全理解稳定性是最重要的问题。在那种情况下,我们不应该选择最新版本的 Elasticsearch。作为参考,所有当前和过去的版本都可以在此页面上找到......作为一种模式,我建议比最新版本早发布 4 到 6 个月的版本”。——来自阮一鸣老师和ES官方的讨论帖。
关于版本选型,张超老师说“对稳定性要求比较高的生产,不要用最新的版本,谁不也知道有没有严重 bug,往前推一些,看看社区反馈没有大问题的版本,修正版本号用最高的”。
如下几点要谨慎考虑:
考虑功能要求:选择支持我们需要的功能的版本,比如:xpack 功能7.1之后才免费,ilm功能 6.7 版本才推出。
考虑兼容性:确保您选择的版本与正在使用的其他软件和工具兼容,比如:java、python客户端的选择。
考虑数据量: Elasticsearch是否能够满足数据存储和处理的要求?
考虑硬件资源:使用Elasticsearch需要充足的硬件资源,包括内存,硬盘,带宽等。
考虑集群架构:要根据业务需求选择合适的集群架构,并考虑到集群的可用性和扩展性。
历史版本下载地址:
https://www.elastic.co/cn/downloads/past-releases#ela...
https://blog.csdn.net/u013613428/article/details/103317806
4.2 Elasticsearch 常用工具避坑
“工欲善其事必先利其器”,没有工具,效率无从谈起。
推荐优先级:Kibana > Head / cerebro > Postman。
学会使用:Kibana Dev Tool,并用好 ctrl + i 快捷键。
学会使用:Kibana monitor 监控可视化工具。
更多推荐:
4.3 Elasticsearch 集群避坑
结合集群能承载的总数据量、每日的增量,在有预留的前提下,给出集群规模的评估。避免“拍脑袋”,要理性计算给出实际参考依据。
布局好节点角色,早期版本叫节点类型。要知道节点角色更为便捷。
确定是否需要冷热集群架构,区分:热节点、温节点、冷节点。冷热集群架构是 ILM 的前提,没有它,ILM无从谈起。
更多推荐:
探究 | Elasticsearch集群规模和容量规划的底层逻辑
干货 | Elasticsearch 8.X 节点角色划分深入详解
4.4 Elasticsearch 索引避坑
确定是否需要 ILM 索引生命周期管理,而不是仅适用 rollover + 脚本自己维护方式或借助 curator 实现。用好 Kibana 可视化管理好 ILM。
考虑索引承载数据上限和大索引可能带来的风险,提前做好业务层面的布局,不同业务使用不同索引,不要混用。
能用模板 template 的就不要单独使用 index。
能支持 datastream 数据流(智能别名)就大胆使用。
定期备份集群索引数据,尤其业务索引,并准备恢复方案,以防数据丢失。
数据迁移需要认真计划,以防迁移不当可能导致数据丢失或损坏问题。
更多推荐:
4.5 Elasticsearch 分片避坑
由于路由机制原因,不同于副本分片支持 update 动态更新,Elasticsearch 主分片数一旦设定就不能动态更新,除非 reindex。
分片设置要不仅满足当下集群的需求,也要考虑集群的未来可扩展性。
单分片大小参见官方的 30GB-50GB的优化建议(因场景而异,可能微调)。
更多推荐:
4.6 Elasticsearch 同步工具避坑
能借助 Ingest 预处理功能解决的,就不要使用 logstash。
能使用 logstash 解决基于时间递增和基于id递增同步的,就不要自己开发。
衡量好 Kafka_connector 和 logstash 的性能和适用场景。
阿里的 canal 工具在同步删除和更新操作时,要优先选择,因为 logstash 不支持同步更新和删除操作。
更多推荐:
4.7 Elasticsearch 检索选型避坑
如果查询语句不正确,可能导致查询性能下降,例如查询条件过于复杂、数据量过大等。
首先,建立起 ES 支持的检索类型的全局认知。
其次:
区分好:什么是召回率?什么是精准率?
区分好:什么是精准匹配,什么是全文检索?
区分好:哪些需要评分?哪些不需要评分?
区分好:什么叫 query?什么叫 filter?
最后:选型成功后,做充分的验证,再部署到线上环境。
涉及性能相关的,要做足检索并发性能测试。
PS:如果所有的已经存在的检索都无法达到业务指标,得考虑分词处下功夫,得考虑空间换时间。
推荐阅读:
4.8 Elasticsearch 数据建模避坑
Elasticsearch要求数据结构符合其特定 Mapping 格式,如果数据结构不合适,可能导致数据存储不完整,后续检索可能会非常复杂。
建模问题的核心在于,前期不会发现,往往项目的中后期才会发现。但,一旦发现,返工的概率就会极大,带来了整体工期的延长和效率的降低。
所以,建议设计初期做足准备。
做什么准备呢?
(1)业务层面:不同索引可能跨索引检索,字段的一致性必要性尤为凸显。
(2)能“宽表”就不要或少用 Nested 嵌套字段、Join 多表关联数据类型。
(3)避免字段爆炸,设置 strict 最为严谨,设置dynamic:false相对谨慎,设置默认的 dynamic:true 要慎之又慎,评估好风险。
更多推荐:
4.9 Elasticsearch 运维避坑
不要等出了问题采取看监控,而是动态更新监控指标数据,考虑将集群各节点的健康状态,以定时任务的形式发送到邮箱等。
定期监控集群健康状态,并及时解决任何问题,以保证集群稳定运行。
用好运维监控工具。Kibana monitor、grafana 均可。
日志建议再归集到一个独立的小ES集群,通过 kibana 可视化展示,并对于 Warn 及以上级别日志及时预警。
推荐如下:
(1)使用Elasticsearch的内置监控工具:如Node Stats API和Cluster Stats API,可用于监控节点和集群的性能。
(2)使用 Kibana Monitoring:提供了全面的监控功能,包括集群监控、节点监控、索引监控等。
(3)定期评估集群健康:使用Elasticsearch的Cluster Health API评估集群的健康状况,以检测性能问题。
(4)记录并分析日志:记录并分析Elasticsearch的日志,以诊断性能问题。
(5)设置告警:设置告警,以提醒您有关性能问题的变化。建议和监控工具(如:Zabbix)结合。
更多推荐:
4.10 Elasticsearch 安全避坑
安全无小事,早期版本(1.X、2.X、5.X、6.X、7.X)“luo奔”导致的安全事故依然屡见不鲜。8.X 的版本已经全线支持默认安全机制,用起来是王道。
如果非要早期版本(5.X、6.X、7.X),建议一定至少加上 xpack 安全机制,至少设置好密码。如果更早版本(1.X、2.X),建议不要开放外网权限,切记!
更多推荐:
5、小结
“坑”是成长过程中的财富,提前关注“坑”能提高开发效率。
欢迎大家就使用 Elasticsearch 过程中遇到的坑留言交流。
5、参考
https://articles.zsxq.com/id_oo0h8a5b6b8a.html
https://wx.zsxq.com/dweb2/index/search/%E4%BC%81%E4%B8%9A/alltopics?groupId=225224548581
https://t.zsxq.com/0bUYswMJn
推荐阅读
更短时间更快习得更多干货!
和全球 1800+ Elastic 爱好者一起精进!
比同事抢先一步学习进阶干货!
以上是关于ES避坑指南的主要内容,如果未能解决你的问题,请参考以下文章
干货 | 中小企业选型 Elasticsearch 避坑指南