带你读《Elastic Stack 实战手册》之83:——4.3.2.Elasticsearch 开发人员最佳实践指南(1)
4.3.2.Elasticsearch 开发人员最佳实践指南创作人:铭毅天下几个月以来,我一直在记录自己开发 Elasticsearch 应用程序的最佳实践。本文梳理的内容试图传达 Java 的某些思想,我相信其同样适用于其他编程语言。我尝试尽量避免重复教程和Elasticsearch 官方文档中已经介绍的内容。本文梳理的内容都是从线上实践问题和个人总结的经验汇总得来的。文章从以下几个维度展开讲解:· 映射(Mapping)· 设置(Setting)· 查询方式(Querying)· 实战技巧(Strategy)映射(Mapping)避免使用 Nested 类型每个 Elasticsearch 文档都对应一个 Lucene 文档。Nested 类型是个例外,对于 nested 类型,每个字段都作为单独的文档存储与父 Lucene 的关联。其影响是:· Nested 与父文档中的字段相比,查询字段的速度较慢。· 检索匹配 Nested 字段会降低检索速度。1595 · 一旦更新了包含 Nested 字段的文档的任何字段(与是否更新嵌套字段无关,则所有基础Lucene 文档(父级及其所有 Nested 子级)都需要标记为已删除并重写)。除了降低更新速度外,此类操作还会产生大量垃圾文件,直到通过段合才能进行清理。在某些情况下,你可以将 Nested 字段展平。例如,给定以下文档:{ "attributes": [
{"key": "color", "val": "green"}, {"key": "color", "val": "blue"}, {"key": "size", "val": "medium"}
]
}展平如下:{ "attributes": { "color": ["green", "blue"], "size": "medium"
}
}Mapping 设置 strict实际业务中,如果不明确设定字段类型,Elasticsearch 有动态映射机制,会根据插入数据自动匹配对应的类型。假定本来准备插入浮点型数据,但由于第一个插入数据为整形,Elasticsearch 自定会判定为long 类型,虽然后续数据也能写入,但很明显“浮点类型”只阉割保留了整形部分。铭毅给个 Demo 一探究竟:POST my_index03/_doc/1
{ "tvalue":35
}
POST my_index03/_doc/2
{ "tvalue":3.1415
}
GET my_index03/_mapping
GET my_index03/_search
{ "query": { "term": { "tvalue": { "value": 3.1415
}
}
}
}注意:term 查询是不会返回结果的。所以,实战环境中,Mapping 设定要注意如下节点:· 显示的指定字段类型· 尽量避免使用动态模板(dynamic-templates)· 禁用日期检测 (date_detection),默认情况下处于启用状态。“strict” 实践举例:PUT my_index
{ "mappings": { "dynamic": "strict", "properties": { "user": { "properties": { "name": { "type": "text"
},"social_networks": { "dynamic": "strict", "properties": { "network_id": { "type": "keyword"
},"network_name": { "type": "keyword"
}
}
}
}
}
}
}
}合理的设置 string 类型Elasticsearch 5.X 之后,String 被分成两种类型,text 和 keyword。两者的区别:· text:适用分词全文检索场景· keyword:适用字符串的精准匹配场景默认,如果不显示指定字段类型,字符串类型自定映射后的 Mapping 如下所示:"cont" : { "type" : "text",
"fields" : { "keyword" : { "type" : "keyword", "ignore_above" : 256
}
}而公司实战的业务场景,通常会面临:· 需不需要分词,不需要的话仅保留 keyword 即可。· 需要用什么分词?英文分词还是中文分词?· 分词后是否还需要排序和聚合,即 fielddata 是否需要开启。· 是否需要精准匹配,即是否需要保留 keyword。所以,回答了如上几个问题,再有针对的显示设定 string 类型的 Mapping 方为上策设置(Setting)在这里我分享了 Elasticsearch 集群设置相关的技巧。避免过度分片分片是 Elasticsearch 的最大优势之一,即将数据分散到多个节点以实施并行化。关于这个主题有过很多讨论。但请注意,索引的主分片一旦设置便无法更改(除非重建索引或者 reindex)。对于新来者来说,过度分片是一个非常普遍的陷阱。在做出任何决定之前,请确保先通读官方的这篇博文:我在 Elasticsearch 集群内应该设置多少个分片?https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster铭毅提示:· 主分片数过多: ○ 批量写入或者查询请求被分割成过多的子写入、子查询,导致索引的写入、查询拒绝率上升。· 主分片数过少:○ 尤其对于数据量非常庞大的索引,若分片数过少或者就 1 个分片,会导致无法利用集群多节点资源(也就是分布式特性),造成资源利用率不高或者不均衡,影响写入或者查询效率。○ 并且,一旦该大的主分片出现问题,恢复起来耗时会非常长。 取消学习任何段合并的技巧从本质上讲,Elasticsearch 是另一种分布式 Lucene 产品,就像 Solr 一样 。在底层,大多数时候,每个 Elasticsearch 文档都对应一个 Lucene 文档(nested 除外)。在 Lucene 中,文档存储在 segment 中。后台的 Elasticsearch 通过以下两种模式连续维护这些 Lucene 段:· 在 Lucene 中,当你删除或更新文档时,旧文档被标记为已删除,而新文档被创建。 Elasticsearch 会跟踪这些标记为 deleted 的文档,适时对其段合并。· 新添加的文档可能会产生大小不平衡的段。Elasticsearch 可能会出于优化目的而决定将它们合并为更大的段。实战中一定要注意:段合并是高度受磁盘 I/O 和 CPU 约束的操作。作为用户,我们不想让段合并破坏 Elasticsearch 的查询性能。事实上,在某些情况下可以完全避免使用它们:一次构建索引,不再更改它。尽管在许多应用场景中可能很难满足此条件。一旦开始插入新文档或更新现有文档,段合并就成为不可避免的一部分。正在进行的段合并可能会严重破坏集群的总体查询性能。在 Google 上进行随机搜索,你会发现许多人发帖求助求助:“在段合并中减少对性能的影响的配置“,还有许多人共享某些适用于他们的配置。但很多配置都是早期 1.X,2.X 版本的设置,新版本已经废弃。综上我进行段合并的经验法则如下:· 取消学习任何段合并的技巧。早期版本的段合并配置是与 Elasticsearch 的内部紧密耦合的操作,新版本一般不再兼容。几乎没有“神秘”的底层配置修改可以使它运行得更快。· 找到 translog flush 的最优配置 。尝试调整 index.translog.sync_interval 和 index.translog.flush_threshold_size 设置。详见:https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-translog.html · 动态调整 index.refresh_interval 以满足业务需求。如果实时性要求不高,可以调大刷新频率(默认是 1s,可以调到 30s 甚至更大)。PUT /twitter/_settings
{ "index" : { "refresh_interval" : "30s"
}
}注意 JVM 内存设置Elasticsearch 可以根据两个主要内存设置产生引人注目的性能特征:· JVM 堆空间——主要用途:缓存(节点缓存、分片请求缓存、Field data 缓存以及索引缓存)· 堆外内存空间—— Lucene 段文件缓存提醒你不要根据过去的非 Elasticsearch JVM 应用程序经验来盲目设置 Elasticsearch JVM堆大小。详见官方文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html《Elastic Stack 实战手册》——四、应用实践——4.3 性能优化场景——4.3.2.Elasticsearch 开发人员最佳实践指南(2): /article/1225222spm=a2c6h.13148508.setting.15.438f4f0e18NXNE
带你读《Elastic Stack 实战手册》之76:——4.2.2.Elasticsearch智能巡检开发设计实践(3)
《Elastic Stack 实战手册》——四、应用实践——4.2 可观测性应用场景 ——4.2.2.Elasticsearch智能巡检开发设计实践(2) /article/1226094Index 层面指标分析?replica?所有集群的 index 都应该有副本分片,没有副本分片的 index 在节点 crash 时会丢失数据。?GET index_name/_settings当 number_of_replicas 为 0 时候异常情况。?动态 mapping?dynamic mapping 设置为 true 会使得 mapping 变得不可维护,且 mapping 源数据由master 维护、分发,大量变更可能导致 master 压力过大,在高峰情况下,可能会使得积压大量task,引发集群不响应、踢出节点等问题。?GET /*/_mapping?format=json当 number_of_replicas 为 0 时候异常情况。?动态 mapping?dynamic mapping 设置为 true 会使得 mapping 变得不可维护,且 mapping 源数据由master 维护、分发,大量变更可能导致 master 压力过大,在高峰情况下,可能会使得积压大量task,引发集群不响应、踢出节点等问题。GET /*/_mapping?format=json巡检需要检查出 "dynamic=true(或默认)"的索引的集群,标记为异常。refresh_interval?索引 refresh 频率是影响性能的一个因素,受到 refresh_interval 参数与 buffer 大小的影响,由于业务场景的差异,对 refresh 的设置可能大不相同,可将集群类型大致分为搜索类型与数据分析类型,根据类型的不同设置差异化的阈值,且集群不应该出现?refresh_interval = -1?的设置。GET /*/_settings?include_defaults=trueindices.refresh.total?refresh 的频率影响着 segment 的生成速度与大小,而 segment 过多往往影响查询性能,并且需要消耗更多的内存和磁盘空间。由于默认值为?refresh_interval = 1s,不考虑 buffer 的影响可以认为 refresh 频率为 60/min,故巡检阈值可以设置到比默认值稍高,例如:count(refresh) = 80/min。GET /_nodes/stats/indices,ingest/refreshmax_result_window?max_result_window 为单次请求返回 doc 的最大值,默认为 10000,该默认值的限制可以覆盖到所有正常的业务场景。一般是深度分页、全量查询、job 查询可能导致返回 doc 数大于10000,触发异常,而这些场景可以由 scroll、search after 来完成。故该指标阈值可以设置成该参数默认值。GET /*/_settings?include_defaults=truejvm 层面指标分析?jvm heap 使用率?jvm 堆的使用率过高有着 OutOfMemory 的风险,并使得 GC 频率过高,影响请求响应时间。由于使用的 G1 收集器,首次 GC 收集会在预估 GC 时间达到预定值的时候开始触发,则heap 使用率的稳定值也随着参数设置而产生较大差异。而该参数主要是为了预防 OutOfMemory 异常,所以该指标阈值可以设置一个较大值,例如?heap > 90。?GET /_nodes/stats/indices,jvmjdk version 一致性由于 Elasticsearch 的分布式属性,集群存在多节点,每个节点一个单独的实例,需要保证jdk 版本一致。jvm heap segment memory?segment memory 常驻 heap 内存,所以 segment memory 的增长会压缩其他对象的内存空间。segment memory 是每个 segment 倒排词典上层的一个前缀索引,即 FST 结构,该前缀索引会在 segment 不断的累积下逐渐增多。?为了防止其对 heap 内存过多的占用,需要对该值继续检查限制,由于 FST 结构对前缀索引进行大量压缩,正常状态下对 heap 占用较低,巡检阈值也可以设置较低,例如 20% heap_size。GET /_nodes/stats/indices,ingest/segmentsfull gc?Elasticsearch 7.x 默认使用的 G1 垃圾收集器,所以一般会是 Young GC 或 Mixed GC,如果mixed GC 无法跟上新对象分配内存的速度,导致老年代填满无法继续进行 Mixed GC,于是使用 full GC 来收集整个 heap。G1 不提供 full GC,使用的是 serial old GC。所以该full GC 是单线程串行的,且 stop the world,这对业务来说是致命的。所以该巡检的阈值为"count(full gc) > 0"。GET /_nodes/stats/indices,jvmthreadpool 层面指标分析?bulk reject 数量?bulk 出现 reject 意味着线程池中线程被完全占用,且队列也已经占满。该指标阈值可设置为 "count(bulk rejected)>0"。GET _cat/thread_poolsearch reject 数量?search 出现 reject 意味着线程池中线程被完全占用,且队列也已经占满。该指标阈值可设置为 count(search rejected)>0,查询 API 同上。《Elastic Stack 实战手册》——四、应用实践——4.2 可观测性应用场景 ——4.2.2.Elasticsearch智能巡检开发设计实践(4) /article/1226091
带你读《Elastic Stack 实战手册》之70:——4.1.2.实现主流搜索引擎广告置顶显示效果(下)
《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.2.实现主流搜索引擎广告置顶显示效果(上) /article/1226383Pinned query 实战实现?基础数据 Demo 如下,直接拿文章开头的截图示例模拟一下,假设 id为 1、2、3 的3条数据是需要特意置顶显示的数据?PUT?index_001
{
??"mappings":?{
????"properties":?{
??????"title":{
????????"type":?"text",
????????"analyzer":?"ik_max_word",
????????"fields":?{
??????????"keyword":{
????????????"type":"keyword"
??????????}
????????}
??????}
????}
??}
}
PUT?index_001/_bulk
{"index":{"_id":1}}
{"title":"大众汽车首款纯电动ID.4_预售进行时_先订先享"}
{"index":{"_id":2}}
{"title":"保时捷首款纯电动跑车Taycan?-?现已到店?-?电驰神往"}
{"index":{"_id":3}}
{"title":"纯电动电动汽车?英国国际贸易部_邀您来投资英国汽车工业"}
{"index":{"_id":4}}
{"title":"四轮电动车_?电动汽车报价_阿里巴巴采购批发_超多品类低价批发"}
{"index":{"_id":5}}
{"title":"电动汽车之家,为新能源汽车而生?-?第一电动网"}
{"index":{"_id":6}}
{"title":"中国电动汽车网_新能源汽车_电动汽车网"}
{"index":{"_id":7}}
{"title":"电车之家_领先的电动汽车及新能源汽车行业门户网站"}如果要召回既包含:“电动汽车” 完全匹配,又包含“电动”或“汽车” 分词匹配的全量数据。大致的检索语句如下:POST?index_001/_search
{
??"query":?{
????"bool":?{
??????"should":?[
????????{
??????????"match_phrase":?{
????????????"title":?{
??????????????"query":?"电动汽车",
??????????????"boost":?5
????????????}
??????????}
????????},
????????{
??????????"bool":?{
????????????"should":?[
??????????????{
????????????????"match":?{
??????????????????"title":?"电动"
????????????????}
??????????????},
??????????????{
????????????????"match_phrase":?{
??????????????????"title":?"汽车"
????}
??????????????}
????????????],
????????????"minimum_should_match":?2
??????}
????????}
??????]
????}
??}
}如上检索部分:完全匹配加了 boost 提升权重。?返回结果如下:?返回结果按照评分由高到低顺序排列,_id 序列为:5、7、3、6、4 ......?置顶显示_id 为 1、2、3 的数据,pinned query 实现如下:GET?index_001/_search
{
??"query":?{
????"pinned":?{
??????"ids":?[
????????"1",
??"2",
????????"3"
??????],
??????"organic":?{
????????"bool":?{
??????????"should":?[
????????????{
??????????????"match_phrase":?{
????????????????"title":?{
??????????????????"query":?"电动汽车",
??????????????????"boost":?5
????????????????}
??????????????}
????????????},
????????????{
??????????????"bool":?{
????????????????"should":?[
??????????????????{
????????????????????"match":?{
??????????????????????"title":?"电动"
????????????????????}
??????????????????},
??????????????????{
????????????????????"match_phrase":?{
??????????????????????"title":?"汽车"
????????????????????}
??????????????????}
????????????????],
????????????????"minimum_should_match":?2
??????????????}
????????????}
??????????]
????????}
??????}
????}
??}
}本质是在原来检索语句的基础上,添加了如下部分代码:"pinned": {
"ids": [
"1",
"2",
"3"
],
"organic": {第一:置顶显示的 id ,写法如下:?第二:除了置顶数据之外的其余正常检索语句块内容。只是加了“organic” 包裹一层。其中的检索语句还是原来的写法 ,拷贝过来即可。?返回结果如下:返回结果已 pinned(类似做了“广告位”定制),_id 序列为:1、2、3、5 ....... 实现了类百度置顶显示广告的效果。?Pinned query 源码解读?认知前提:源码中最大评分计算方法float MAX_ORGANIC_SCORE = Float.intBitsToFloat((0xfe << 23)) - 1;本质上与下面代码等价:float max_rst = (float)Math.pow(2,127);//1.7014118E38也就是说:MAX_ORGANIC_SCORE 大小为:2 的 127 次幂,是 Elasticsearch float 最大值。?最大评分作用?正常查询的评分得分不会超过 MAX_ORGANIC_SCORE, 将固定查询(pinned query)的评分设定为:MAX_ORGANIC_SCORE。?pinned query 保证置顶显示解密?原理:将置顶显示的数据通过 bool 组合查询 + boost 提升权重的方式给设置了 float 最大值评分,这样就能保证置顶显示了。?核心源码实现如下:注意细节没有深究,比如:置顶返回的结果显示的是原始评分。?小结?读者可能会问:这并没有实现基于特定关键词返回特定数据的需求?其实有了pinned query 再将特定关键词与待置顶显示文章 _id ,建立个一对多的映射关系就可以实现。映射关系可以自己内存维护或者借助 redis 实现都可以。?你我发现的新需求,很可能别人早就发现,且已经提交 Git了。更可怕的是:官方新版本已经实现了!?要注重基础夯实的同时,多关注一下技术动态。两手抓、两手都要硬! ?参考?l?https://www.elastic.co/guide/en/elasticsearch/reference/7.4/release-notes-7.4.0.html?创作人简介: 铭毅天下,Elastic 认证工程师、Elastic 官方合作培训讲师、便宜云服务器 MVP、CSDN 博客专家、铭毅天下 Elasticsearch 公众号作者、死磕 Elasticsearch 知识星球星主。近 10年工作经验,关注 Elastic Stack 技术栈、大数据技术领域。博客:https://elastic.blog.csdn.net/
带你读《Elastic Stack 实战手册》之69:——4.1.1.Elasticsearch 在舆情搜索中的实践(下)
《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.1.Elasticsearch 在舆情搜索中的实践(中) /article/1226421典型业务场景?情绪走势?通过“情绪走势”图,可以看出一个舆情事件,在一段时间,不同情感表达上的数据分布情况,为了方便使用 ES 的聚合统计功能,对每篇文档的发布时间,设置了news_posthour(文档发布时间所在的小时)冗余字段,文档的情绪news_mood_pri字段,通过在 DSL 中嵌套使用 aggregation ,就可以在一次查询中获得对应的图表数据。 比如查询在微博上与疫情相关的博文在某一天每小时的情绪走势,查询语法如下:GET weibo_2021-04-08/_search
{
"query": {"bool": {"must": [
{"match_phrase": {
"news_title": "疫情"
}}
]}},
"size": 0,
"aggs": {
"NAME": {
"terms": {
"field": "news_mood_pri",
"size": 10
},
"aggs": {
"NAME": {
"terms": {
"field": "news_posthour",
"size": 24
}
}
}
}
}
}通过上面的嵌套查询 DSL 语句,就可以快速获取到不同情绪分类下,每个小时段的数据量。?热门主题词通过一个事件的热门主题词,可以直观的了解到一个事件的大概内容。这里也是通过 ES 的聚合功能实时获取主题词的统计数据。为了能够实时获取主题词的统计数据,这里用一个事件中提到每个主题词的文档数量,来当作主题词的数量(相当于默认每个主题词在文档中只出现一次),并没有用每篇文档的主题词的绝对量。这样做有一个好处,可以使用 ES 的 aggregation 功能实时聚合获取统计数据,再配合 TF-IDF 算法,计算每个词的相对权重。 ?在设置索引 schema 时,定义了news_keywords_list字段, 用于保存单篇文档的分词结果列表,然后使用如下的语法,就可以快速统计每个词对应的文档数量:?GET weibo_2021-04-08/_search
{
"query": {"bool": {"must": [
{"match_phrase": {
"news_title": "疫情"
}}
]}},
"size": 0,
"aggs": {
"NAME": {
"terms": {
"field": "news_keywords_list",
"size": 10
}
}
}
}通过上面的 DSL 语句,可以快速统计出高频词以及与其相关的文档数量。?创作人简介:王欢,近 10 年内容大数据领域从业经验,安徽云计算产业促进会开发者工作委员会发起人之一,便宜云服务器 MVP。擅长高并发系统设计、数据中台构建等,目前在一家人工智能企业担任技术VP,主要关注 AI 算法平台构建、AI 算法在内容分析领域落地等。
带你读《Elastic Stack 实战手册》之69:——4.1.1.Elasticsearch 在舆情搜索中的实践(中)
《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.1.Elasticsearch 在舆情搜索中的实践(上) /article/1226425分词器设计?索引膨胀对比?不同于英文分词器,大部分使用空格作为分隔符。针对不同的检索场景,中文有更多的分词器可供选择,不同中文分词器的选择,会有索引大小,检索性能,以及检索数据的召回率与准确率上的不同。不同分词器的磁盘占用对比在同样的数据条件下,通过对不同分词器下索引占用磁盘空间对比,我们发现:?l?IK(ik_max_word)分词器,占用磁盘空间最小l?Standard分词器,与 ik_max_word 相差不大,比 ik_max_word 分词方式只增加了 5% 左右l?N-gram 占用空间比较大,相对于 ik_max_word, 当 n=3,4,5 时,占用空间,分别是ik_max_word 的 2倍、3倍、4倍 左右。?检索性能对比不同分词器的检索耗时对比在同样的数据条件下,通过对不同的分词下检索性能的对比,我们发现:?l?IK(ik_max_word)分词器,检索性能是 standard 分词器的 2-3倍 左右l?N-gram(n=5)分词器,检索性能是 standard 分词器的 7倍 左右,是 ik_max_word 分词器的 3倍 左右?通过综合对比,虽然 n-gram(n=5)分词器具备更高的检索性能,但是占用更多磁盘空间,在舆情业务上,索引是百亿级别(保留近3个月),基于成本考虑,这里选择了 ik_max_word 分词器。?基于 ES 的数据中台系统架构图整个数据中台也是分层的架构体系,分为:?l?数据接入层l?消息总线l?数据处理与存储层l?数据索引层(ES 集群)l?智能网关层l?业务接入层?这里重点介绍数据索引层与智能网关层。数据索引层在数据索引层,按照业务特点,以及成本综合考虑。分为近 2年数据的冷数据集群,以及近 3个月的热数据集群。?l?冷数据集群,选择价格相对低廉的 SAS 盘作为索引的存储介质, 提供离线的数据下载,以及对响应时间不敏感,且时间周期跨度较长的检索、聚合统计等。l?热数据集群,选择 SSD 盘作为索引的存储介质,每个节点 16C、64G 内存, 为了降低运维成本,以及动态扩缩容,我们选择了“便宜云服务器Elasticsearch 服务”。?在索引设计方面,根据业务特点,经常要检索特定平台的数据,对索引按照文章发布平台,以及发布日期做了拆分,使每个索引不至于过大,以及导致每个节点上的数据分布不均匀,提高磁盘的利用率与检索性能。?智能网关层智能网关层避免了业务端直连 ES,无法做到访问并发限制,以及细粒度的权限限制。智能网关主要解决了以下几个痛点:?1、并发控制网关为每个业务分配对应的独立的 TOKEN,并且设置相应的 QPS,防止某个业务的高频访问,影响了其他业务的访问,最终因为级联效应, 导致整个 ES 集群无法提供服务。另外, 网关提供了熔断限流的功能,在ES集群负载比较高的况下,对低优先级的 TOKEN 进行限流。?2、权限控制网关为业务分配对应的权限,比如,读写权限、访问特定索引权限、查询时间跨度权限等,对查询语句进行解析,禁止访问超出权限的数据。?3、SQL 查询网关提供了 SQL 转换成 DSL 的功能,访问业务端通过标准的 SQL 进行快速的查询对应的数据,提高业务开发效率,降低使用ES的门槛。??4、动态路由在索引设计阶段,把索引按天、按照发文平台进行了拆分。为了方便业务端查询,根据业务端查询的时间段、发文平台,自动定位到对应的索引,业务端不用关心具体的索引名称,提高业务端的开发效率, 同时,根据查询时间范围,动态查找对应的索引,并在查询时指定到具体的索引,提高查询的速度,避免无效的索引扫描。?5、降低升级成本由于业务不是直连 ES,后端切换 ES 集群,可做到业务端无感知,降低了 ES 集群的升级切换对业务端的影响, 降低了升级带来的开发成本。《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.1.Elasticsearch 在舆情搜索中的实践(下) /article/1226418
带你读《Elastic Stack 实战手册》之69:——4.1.1.Elasticsearch 在舆情搜索中的实践(上)
4.1 企业搜索应用场景4.1.1.Elasticsearch 在舆情搜索中的实践业务背景?网络舆情监测,主要是利用互联网信息采集技术,以及自然语言处理等智能信息处理技术,通过对互联网公开数据进行自动化抓取,然后对信息进行结构化、自动分类、文本聚类、主题发现与跟踪等,提供信息检索、多维度统计、敏感信息预警、信息简报、自动化报告等功能,帮助用户及时发现危害品牌形象的观点,并为用户分析关注对象在网络中的形象提供依据。?在舆情 SAAS 系统(以下简称:舆情系统)中,用户设置关注的关键词,就可以快速检索对应的舆情数据,以及对提及关键词的数据提供统计图表,包括舆情走势、词云图、情感分布、情绪走势等。?这里的信息检索与统计,都离不开 Elasticsearch(以下简称:ES)的 Query 以及 aggregation功能,下面详细介绍如何使用 Elasticsearch 实现这些功能,以及在实践过程中遇到的一些问题及解决方案。舆情系统多维度检索索引设计?采集的数据源包括微博、微信、新闻网页、论坛、自媒体平台、短视频等平台的数据,每天新增去重数据量在 1亿+,每条数据在经过结构化,以及经过 NLP(自然语言处理)之后,超过150 个字段,比如,文章标题、发布时间、发布作者、发布平台、新闻分类、新闻提及地域、新闻情绪等。由于业务端需要对这些数据进行实时检索,对不同平台的数据实时聚合,各平台的数据量分布也有很大的差异,所以按照平台进行拆分,而不是把所有的数据放到一个大的索引里面。 ?由于不同平台的数据量差异很大,一般地,微博占每日总采集量的 80%,而新闻网页、微信、自媒体平台的占比相对较少。为了避免由于索引的大小不一样,导致每个shard的差异过大,最终导致落在不同节点上 shard 占用空间分布不均匀而出现数据倾斜。?所以,在实现上对微博的索引按照日期做了进一步拆分,微博每日一个索引,而自媒体平台每月一个索引。?索引划分示意图为了方便业务检索,对按天分索引的微博设置别名,比如 alias weibo_202101对weibo_20210101,weibo_20210102,...,weibo_20210131?《Elastic Stack 实战手册》——四、应用实践——4.1 企业搜索应用场景 ——4.1.1.Elasticsearch 在舆情搜索中的实践(中) /article/1226421
带你读《Elastic Stack 实战手册》之59:——3.5.16.3.Anomaly detection(5)
《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.16. Machine learning ——3.5.16.3.Anomaly detection(4) /article/1227220五、异常检测限制与注意事项?l?机器学习使用流式 SIMD 扩展(SSE)4.2指令,因此仅适用于 CPU 支持 SSE4.2 的机器。如果在旧硬件上运行 Elasticsearch,则必须通过设置 xpack.ml.enabled 为 false,禁用机器学习。l?分类非结构化数据使用的词典仅支持默认英文单词词典。l?如果使用 Kibana,则需要设置浏览器中启用弹出窗口,保证 Kibana 正常使用。l?某些场景下 Kibana 对异常检测结果绘图会出现异常。由于分类、time_of_day 函数或time_of_week 函数获取的结果不能很好的展示为时序图表,对于使用脚本字段的检测器无法快速搜索原始源数据,因为被脚本进行了某种程度的转换,所以 Kibana 不会显示 Anomaly Explorer 图表。l?设置 datafeed 时如果指定结束日期,则在 dataFeed 结束后关闭 job(避免大量打开一次性 job),否则 job 将处于 open 状态(避免对 job 的反复重新打开)。l?如果在 Kibana 中创建 job 则必须使用 dataFeed,如果需要分析的数据不在 ES 中,则无法使用 dataFeed,可以通过机器学习 API 创建 job 并将批量数据直接发送到 job,如果使用 Post data API 将数据发送到 job,该数据必须是 JSON 格式。l?使用 missing_field_count 计数时,可能会产生一定的误差。l?terms 聚合默认返回前十个 buckets(可以通过 size 参数修改),如果是将预聚合的数据发送到 job,则需要保证 size 配置正确。l?索引中字段名若为 by、coun、over,则不能在 job 的 by_field_name 或 over_field_name 配置中使用。l?在使用 Single metric job 或 Multi metric job 时,ES 会在某些场景中使用预聚合,此时会因为精度问题和不使用预聚合的分析同一数据的脚本产生不完全一样的结果。l?使用 Single metric job 时会默认启用 model_plot_config 配置项(开启模型绘图),此配置会给 ES 增加大量开销,如果分析数据过大,需要手动禁用该配置。l?ES 开启安全功能后,datafeed 会受到权限影响。在 datafeed 创建、task 进行中修改角色,则 datafeed 会以角色关联的新权限运行。如果在 datafeed 创建、更新完成后调整用户角色,则 datafeed 将以与原始角色关联的权限运行。l?ES 集群升级时必须关闭机器学习 job。l?Rollup 控制的 index 以及 Frozen indices,无法被使用在机器学习 job 和 datafeed 中。l?机器学习相关 object 不属于 Kibana 的 spaces。如果在 Kibana 中创建了 space,隔离了 index_patterns, dashboards, visualizations 等 object,当机器学习 job 使用了这些 object 时,在后续更改 space 时可能出现异常。l?获取 Job、datafeed 的 API 具有一定的限制,均最多返回 10000 条。l?不支持时间设置为纳秒级(date_nanos)的字段作为机器学习 job 的 time_field 属性。l?机器学习的预测能力存在一定限制:○?1.正常情况下预测和机器学习分析会同时进行,机器学习分析不会在生成预测时停止。如果内存不充足则预测会对异常检测 job 产生影响,所以在内存不充足情况下不会进行预测。○?2.创建预测时,异常检测 job 必须处于 open 状态。○?3.没有足够的数据,生成的预测通常准确度不高。六、优化?l?集群节点需要足够的内存,异常检测 job 会在单个节点上运行,需要足够的资源将其模型保存在内存中。当一个 job 打开时,会被放置在当时可用内存最多的节点上。如果是大量小内存。l?job 的场景,可以考虑拓展节点数。l?单个大型 job,考虑使用单独的结果索引。默认情况下 job 生成的结果会存入同一个 index 中。l?在 Kibana 中会默认启用模型图,单个大型 job 场景下考虑禁用模型图。POST _ml/anomaly_detectors/population_v2_detector/_update
{
"model_plot_config": {
? "enabled": true
}
}
?l?Bucket Span 调优l?时间窗口设置的越大,bucket span 越大,单次输送 ML job 的数据越多,精度越小(曲线越平滑),可能会忽略某些异常点。丢失很多细节信息,可能导致给 ML 输送的数据量不足。l?时间窗口设置的越小,bucket span 越小,单次输送 ML job 的数据越少,精度越大,噪声越多,可能会放大某些小的异常点。桶大小过小,会导致小时间段内数据频繁变化,影响模型的学习。l?所以,桶的大小要根据业务场景,不断地调整大小,找到合适的值。l?ES ML 提供了一键预估(Estimate bucket span) Bucket 大小,这个功能并不是很智能,仍需要自己不断调试。l?给每个 job 设置模型内存限制,通过 model_memory_limit 参数设定,通过 Kibana 创建时会默认自动生成一个预估值,如果需要修改,则需要关闭 job 修改该限制。?l?优化归一化窗口设置,当新异常的得分远高于过去的任何异常时,异常得分会根据新数据在 0 到 100 的范围内进行调整。这意味着在结果索引中重写大量文档。默认情况下,对过去 30 天或 100 个存储桶跨度的结果进行重新归一化。l?当大规模 job 启用时,设置 renormalization_window_days 一个较低的值,可以减少性能压力。?创作人简介:张妙成,ES PAAS 平台开发,云计算技术爱好者。博客:https://blog.csdn.net/qq_33999844?spm=1001.2014.3001.5343?
带你读《Elastic Stack 实战手册》之43:——3.5.2.Kibana的Alert (4)
《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.2.Kibana的Alert(3) /article/1228796count 为统计文档数,不需要填写字段,其余方法会自动匹配出可计算类型的字段,比如long类型。?OVER 条件可以配置聚合全部文档或者分组。如果使用了分组,即 top,那么当每个组超过阈值时,将为每个组创建一个 Alert Instance。在配置中,top 会设定分组数量,限制高基数字段上的实例数量。比如上图中只检查 user.id.keyword 数量最多的3组。PUT _ingest/pipeline/add-timestamp
{
"processors": [
{
"set": {
"field": "@timestamp",
"value": "{{_ingest.timestamp}}"
}
}
]
}
POST _bulk?pipeline=add-timestamp
{ "create" : { "_index" : "test-es", "_id" : "1" } }
{ "user.id":"may","fail_num":"1"}
{ "create" : { "_index" : "test-es", "_id" : "2" } }
{ "user.id":"may","fail_num":"4"}
{ "create" : { "_index" : "test-es", "_id" : "4" } }
{ "user.id":"jack","fail_num":"1"}
{ "create" : { "_index" : "test-es", "_id" : "5" } }
{ "user.id":"may","fail_num":"6"}
{ "create" : { "_index" : "test-es", "_id" : "6" } }
{ "user.id":"jack","fail_num":"3"}
{ "create" : { "_index" : "test-es", "_id" : "7" } }
{ "user.id":"jack","fail_num":"3"}
{ "create" : { "_index" : "test-es", "_id" : "8" } }
{ "user.id":"bill","fail_num":"9"}
{ "create" : { "_index" : "test-es", "_id" : "9" } }
{ "user.id":"jack","fail_num":"1"}
{ "create" : { "_index" : "test-es", "_id" : "10" } }
{ "user.id":"jack","fail_num":"1"}相关的查询结果会有一张时序图来体现,如下图:从图中看出前三个是 may 6次、jack 9次、bill 9次。?再设置一个 Action,此处是写进索引 alert-record保存后,过段时间 Alert 的详情里就有了 Alert Instance 的相关信息。其中 Status 为 Active 的是待执行 Action 操作的。不管 Alert Instance 在被监控判断的期间是否被静音( Mute ),最终状态都为 OK。?关于 Actions?预设置 Connector 和 Action?可以在 kibana.yml 中预设值 Connector 或者 Action Type。?预设 Connector 需要将配置和相关证书设置清楚,不可以数组对象的形式设置,且配置完以后不可修改。相比之下在 Kibana 启动后,再设置 Connector 相对灵活许多。?Connector 和 Action 的具体配置可参考官方网站。?Action 的变量传入?在设置 Action 时,根据 Action Type 的不同,会设置不同的 properties。比如,email 配置Subject 和 Message;Index 提供 document。具体细节参考每个类型的具体 action 配置。?虽然各个 Properties 不一样,但是在配置时都可以把 Alert 中监控到的数据,作为变量传入报警信息中。使用 Mustache 模板语法 {{variable name}},可以在检测到一个 Condition 时将警报值传递给一个 Action。?可用的变量因 Alert Type 不同而不同,可以使用 "Add variable" 按钮来访问列表。?下图是 email 的可传入参数:?创作人简介:扈臣聪 ,擅长应用设计与开发,关注Elasticsearch、Mysql、NLP等技术。博客:/profile/etlld5ckrmzfc?
带你读《Elastic Stack 实战手册》之42:——3.5.1.跨集群操作(2)
《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.1.跨集群操作(1) /article/1228852跨集群搜索?跨集群搜索可以针对一个或多个远程集群,运行单个搜索请求。例如,我们可以使用跨集群搜索,来过滤和分析存储,在不同数据中心的集群中的日志数据。?在5.3.0之前的版本,Elastic 官方提供了 Tribe Node 实现多集群访问的解决方案。?Tribe Node是以 Client Node 的角色添加到集群中,但是由于不保留集群的 meta 信息,每次重启需要重新加载初始化。因此,在5.3版本中 Elastic 官方提供了 CCS 的功能,允许集群中的任何节点可以联合查询。?快速入门?下面以两个集群的跨集群搜索为例。我们预先启动了两个集群:cluster1、cluster2,当前集群是 cluster1。现在的任务是联合远程访问的集群 cluter2 进行跨集群搜索。?我们分别在两个集群上动态配置 remote cluster。?注意:在 seeds 列表中填写的是集群节点间通信的 TCP 端口而不是 HTTP 端口。PUT _cluster/settings
{
"persistent": {
"cluster": {
"remote": {
"cluster_one": {
"seeds": [
"192.168.2.2:9300"
]
},
"cluster_two": {
"seeds": [
"192.168.2.2:9500"
]
}
}
}
}
}在 cluster1 中插入数据?PUT esfighttogether/_doc/1
{
"teamname":"team 10"
}在 cluster2 中插入数据PUT esfighttogether/_doc/1
{
"teamname":"team 1"
}
PUT esfighttogether/_doc/2
{
"teamname":"team 7"
}在两个集群上分别验证数据。因为写入时 Elasticsearch 自带的默认分词器会对数据进行分词,我们通过 team 就可以查询到所有数据。?查询语句如下:GET esfighttogether/_search
{
"query": {
"match": {
"teamname": "team"
}
}
}执行跨集群搜索GET cluster_one:esfighttogether,cluster_two:esfighttogether/_search?timeout=5m
{
"query": {
"match": {
"teamname": "team"
}
}
}如上图所示,通过 CCS 查询到 3 条数据:cluster_one 的一条数据 team 10 以及 cluster_two 的两条数据 team 1 和 team 7,和之前写入的数据一致。?《Elastic Stack 实战手册》——三、产品能力——3.5 进阶篇——3.5.1.跨集群操作(3) /article/1228850?
带你读《Elastic Stack 实战手册》之40:——3.4.2.21.Aggregations(14)
《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.21.Aggregations(13) /article/12292251、直方图聚合?比如按照产品价格间隔为1000进行分组聚合:GET order/_search
{
"aggs": {
"histogram_test": {
"histogram": { //聚合类型
"field": "price",
"interval": 1000 //分组间隔
}
}
}
}?例如我们需要根据价格进行直方图聚合:指标(Metrics)选择计数(Count)->存储桶选择按列拆分(Split chart)->聚合类型选择直方图(histogram)->选择聚合字段,并配置 interval 间隔->点击更新即可得到我们想要的图表。2、管道聚合-父类聚合?例如根据产品价格做直方图统计,在每个统计桶中做平均值得累加和统计:GET order/_search
{
"aggs": {
"my_histogram": {
"histogram": {
"field": "price",
"interval": 1000
},
"aggs": {
"avg_bucket": {
"avg": {
"field": "price"
}
},
"my_cumulative_sum":{
"cumulative_sum": {
"buckets_path": "avg_bucket"
}
}
}
}
}
}?上述查询配置步骤:指标(Metrics)选择累计和(Cumulative Sum)->定制指标(Custom metric)中选择按照价格进行平均值聚合->存储桶选择 X 轴->聚合类型选择直方图->选择聚合字段,并配置 interval 间隔->点击更新即可得到我们想要的图表。创作人简介:扈臣聪,擅长应用设计与开发,关注Elasticsearch、Mysql、NLP等技术。 博客:/profile/etlld5ckrmzfc?