Elasticsearch Suggester详解
如果自己亲手去试一下,可以看到Google在用户刚开始输入的时候是自动补全的,而当输入到一定长度,如果因为单词拼写错误无法补全,就开始尝试提示相似的词。
那么类似的功能在Elasticsearch里如何实现呢? 答案就在Suggesters API。 Suggesters基本的运作原理是将输入的文本分解为token,然后在索引的字典里查找相似的term并返回。 根据使用场景的不同,Elasticsearch里设计了4种类别的Suggester,分别是:
- Term Suggester
- Phrase Suggester
- Completion Suggester
- Context Suggester
在官方的参考文档里,对这4种Suggester API都有比较详细的介绍,但苦于只有英文版,部分国内开发者看完文档后仍然难以理解其运作机制。 本文将在Elasticsearch 5.x上通过示例讲解Suggester的基础用法,希望能帮助部分国内开发者快速用于实际项目开发。限于篇幅,更为高级的Context Suggester会被略过。
首先来看一个Term Suggester的示例:
准备一个叫做blogs的索引,配置一个text字段。
PUT /blogs/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "text"
}
}
}
}
}
通过bulk api写入几条文档
POST _bulk/?refresh=true
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Lucene is cool"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elasticsearch rocks"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elastic is the company behind ELK stack"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "elk rocks"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "elasticsearch is rock solid"}
此时blogs索引里已经有一些文档了,可以进行下一步的探索。为帮助理解,我们先看看哪些term会存在于词典里。
将输入的文本分析一下:
POST _analyze
{
"text": [
"Lucene is cool",
"Elasticsearch builds on top of lucene",
"Elasticsearch rocks",
"Elastic is the company behind ELK stack",
"elk rocks",
"elasticsearch is rock solid"
]
}
(由于结果太长,此处略去)
这些分出来的token都会成为词典里一个term,注意有些token会出现多次,因此在倒排索引里记录的词频会比较高,同时记录的还有这些token在原文档里的偏移量和相对位置信息。
执行一次suggester搜索看看效果:
POST /blogs/_search
{
"suggest": {
"my-suggestion": {
"text": "lucne rock",
"term": {
"suggest_mode": "missing",
"field": "body"
}
}
}
}
suggest就是一种特殊类型的搜索,DSL内部的"text"指的是api调用方提供的文本,也就是通常用户界面上用户输入的内容。这里的lucne是错误的拼写,模拟用户输入错误。 "term"表示这是一个term suggester。 "field"指定suggester针对的字段,另外有一个可选的"suggest_mode"。 范例里的"missing"实际上就是缺省值,它是什么意思?有点挠头... 还是先看看返回结果吧:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 0,
"max_score": 0,
"hits":
},
"suggest": {
"my-suggestion": [
{
"text": "lucne",
"offset": 0,
"length": 5,
"options": [
{
"text": "lucene",
"score": 0.8,
"freq": 2
}
]
},
{
"text": "rock",
"offset": 6,
"length": 4,
"options":
}
]
}
}
在返回结果里"suggest" -> "my-suggestion"部分包含了一个数组,每个数组项对应从输入文本分解出来的token(存放在"text"这个key里)以及为该token提供的建议词项(存放在options数组里)。 示例里返回了"lucne","rock"这2个词的建议项(options),其中"rock"的options是空的,表示没有可以建议的选项,为什么? 上面提到了,我们为查询提供的suggest mode是"missing",由于"rock"在索引的词典里已经存在了,够精准,就不建议啦。 只有词典里找不到词,才会为其提供相似的选项。
如果将"suggest_mode"换成"popular"会是什么效果?
尝试一下,重新执行查询,返回结果里"rock"这个词的option不再是空的,而是建议为rocks。
"suggest": {
"my-suggestion": [
{
"text": "lucne",
"offset": 0,
"length": 5,
"options": [
{
"text": "lucene",
"score": 0.8,
"freq": 2
}
]
},
{
"text": "rock",
"offset": 6,
"length": 4,
"options": [
{
"text": "rocks",
"score": 0.75,
"freq": 2
}
]
}
]
}
回想一下,rock和rocks在索引词典里都是有的。 不难看出即使用户输入的token在索引的词典里已经有了,但是因为存在一个词频更高的相似项,这个相似项可能是更合适的,就被挑选到options里了。 最后还有一个"always" mode,其含义是不管token是否存在于索引词典里都要给出相似项。
有人可能会问,两个term的相似性是如何判断的? ES使用了一种叫做Levenstein edit distance的算法,其核心思想就是一个词改动多少个字符就可以和另外一个词一致。 Term suggester还有其他很多可选参数来控制这个相似性的模糊程度,这里就不一一赘述了。
Term suggester正如其名,只基于analyze过的单个term去提供建议,并不会考虑多个term之间的关系。API调用方只需为每个token挑选options里的词,组合在一起返回给用户前端即可。 那么有无更直接办法,API直接给出和用户输入文本相似的内容? 答案是有,这就要求助Phrase Suggester了。
Phrase suggester在Term suggester的基础上,会考量多个term之间的关系,比如是否同时出现在索引的原文里,相邻程度,以及词频等等。看个范例就比较容易明白了:
POST /blogs/_search
{
"suggest": {
"my-suggestion": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "body",
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
}
}
}
}
}
返回结果:
"suggest": {
"my-suggestion": [
{
"text": "lucne and elasticsear rock",
"offset": 0,
"length": 26,
"options": [
{
"text": "lucene and elasticsearch rock",
"highlighted": "<em>lucene</em> and <em>elasticsearch</em> rock",
"score": 0.004993905
},
{
"text": "lucne and elasticsearch rock",
"highlighted": "lucne and <em>elasticsearch</em> rock",
"score": 0.0033391973
},
{
"text": "lucene and elasticsear rock",
"highlighted": "<em>lucene</em> and elasticsear rock",
"score": 0.0029183894
}
]
}
]
}
options直接返回一个phrase列表,由于加了highlight选项,被替换的term会被高亮。因为lucene和elasticsearch曾经在同一条原文里出现过,同时替换2个term的可信度更高,所以打分较高,排在第一位返回。Phrase suggester有相当多的参数用于控制匹配的模糊程度,需要根据实际应用情况去挑选和调试。
最后来谈一下Completion Suggester,它主要针对的应用场景就是"Auto Completion"。 此场景下用户每输入一个字符的时候,就需要即时发送一次查询请求到后端查找匹配项,在用户输入速度较高的情况下对后端响应速度要求比较苛刻。因此实现上它和前面两个Suggester采用了不同的数据结构,索引并非通过倒排来完成,而是将analyze过的数据编码成FST和索引一起存放。对于一个open状态的索引,FST会被ES整个装载到内存里的,进行前缀查找速度极快。但是FST只能用于前缀查找,这也是Completion Suggester的局限所在。
为了使用Completion Suggester,字段的类型需要专门定义如下:
PUT /blogs_completion/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "completion"
}
}
}
}
}
用bulk API索引点数据:
POST _bulk/?refresh=true
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Lucene is cool"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elasticsearch rocks"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elastic is the company behind ELK stack"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "the elk stack rocks"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "elasticsearch is rock solid"}
查找:
POST blogs_completion/_search?pretty
{ "size": 0,
"suggest": {
"blog-suggest": {
"prefix": "elastic i",
"completion": {
"field": "body"
}
}
}
}
结果:
"suggest": {
"blog-suggest": [
{
"text": "elastic i",
"offset": 0,
"length": 9,
"options": [
{
"text": "Elastic is the company behind ELK stack",
"_index": "blogs_completion",
"_type": "tech",
"_id": "AVrXFyn-cpYmMpGqDdcd",
"_score": 1,
"_source": {
"body": "Elastic is the company behind ELK stack"
}
}
]
}
]
}
值得注意的一点是Completion Suggester在索引原始数据的时候也要经过analyze阶段,取决于选用的analyzer不同,某些词可能会被转换,某些词可能被去除,这些会影响FST编码结果,也会影响查找匹配的效果。
比如我们删除上面的索引,重新设置索引的mapping,将analyzer更改为"english":
PUT /blogs_completion/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "completion",
"analyzer": "english"
}
}
}
}
}
bulk api索引同样的数据后,执行下面的查询:
POST blogs_completion/_search?pretty
{ "size": 0,
"suggest": {
"blog-suggest": {
"prefix": "elastic i",
"completion": {
"field": "body"
}
}
}
}
居然没有匹配结果了,多么费解! 原来我们用的english analyzer会剥离掉stop word,而is就是其中一个,被剥离掉了!
用analyze api测试一下:
POST _analyze?analyzer=english
{
"text": "elasticsearch is rock solid"
}
会发现只有3个token:
{
"tokens": [
{
"token": "elasticsearch",
"start_offset": 0,
"end_offset": 13,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "rock",
"start_offset": 17,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "solid",
"start_offset": 22,
"end_offset": 27,
"type": "<ALPHANUM>",
"position": 3
}
]
}
FST只编码了这3个token,并且默认的还会记录他们在文档中的位置和分隔符。 用户输入"elastic i"进行查找的时候,输入被分解成"elastic"和"i",FST没有编码这个“i” , 匹配失败。
好吧,如果你现在还足够清醒的话,试一下搜索"elastic is",会发现又有结果,why? 因为这次输入的text经过english analyzer的时候is也被剥离了,只需在FST里查询"elastic"这个前缀,自然就可以匹配到了。
其他能影响completion suggester结果的,还有诸如"preserve_separators","preserve_position_increments"等等mapping参数来控制匹配的模糊程度。以及搜索时可以选用Fuzzy Queries,使得上面例子里的"elastic i"在使用english analyzer的情况下依然可以匹配到结果。
因此用好Completion Sugester并不是一件容易的事,实际应用开发过程中,需要根据数据特性和业务需要,灵活搭配analyzer和mapping参数,反复调试才可能获得理想的补全效果。
回到篇首Google搜索框的补全/纠错功能,如果用ES怎么实现呢?我能想到的一个的实现方式:
- 在用户刚开始输入的过程中,使用Completion Suggester进行关键词前缀匹配,刚开始匹配项会比较多,随着用户输入字符增多,匹配项越来越少。如果用户输入比较精准,可能Completion Suggester的结果已经够好,用户已经可以看到理想的备选项了。
- 如果Completion Suggester已经到了零匹配,那么可以猜测是否用户有输入错误,这时候可以尝试一下Phrase Suggester。
- 如果Phrase Suggester没有找到任何option,开始尝试term Suggester。
精准程度上(Precision)看: Completion > Phrase > term, 而召回率上(Recall)则反之。从性能上看,Completion Suggester是最快的,如果能满足业务需求,只用Completion Suggester做前缀匹配是最理想的。 Phrase和Term由于是做倒排索引的搜索,相比较而言性能应该要低不少,应尽量控制suggester用到的索引的数据量,最理想的状况是经过一定时间预热后,索引可以全量map到内存。
如果自己亲手去试一下,可以看到Google在用户刚开始输入的时候是自动补全的,而当输入到一定长度,如果因为单词拼写错误无法补全,就开始尝试提示相似的词。
那么类似的功能在Elasticsearch里如何实现呢? 答案就在Suggesters API。 Suggesters基本的运作原理是将输入的文本分解为token,然后在索引的字典里查找相似的term并返回。 根据使用场景的不同,Elasticsearch里设计了4种类别的Suggester,分别是:
- Term Suggester
- Phrase Suggester
- Completion Suggester
- Context Suggester
在官方的参考文档里,对这4种Suggester API都有比较详细的介绍,但苦于只有英文版,部分国内开发者看完文档后仍然难以理解其运作机制。 本文将在Elasticsearch 5.x上通过示例讲解Suggester的基础用法,希望能帮助部分国内开发者快速用于实际项目开发。限于篇幅,更为高级的Context Suggester会被略过。
首先来看一个Term Suggester的示例:
准备一个叫做blogs的索引,配置一个text字段。
PUT /blogs/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "text"
}
}
}
}
}
通过bulk api写入几条文档
POST _bulk/?refresh=true
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Lucene is cool"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elasticsearch rocks"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "Elastic is the company behind ELK stack"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "elk rocks"}
{ "index" : { "_index" : "blogs", "_type" : "tech" } }
{ "body": "elasticsearch is rock solid"}
此时blogs索引里已经有一些文档了,可以进行下一步的探索。为帮助理解,我们先看看哪些term会存在于词典里。
将输入的文本分析一下:
POST _analyze
{
"text": [
"Lucene is cool",
"Elasticsearch builds on top of lucene",
"Elasticsearch rocks",
"Elastic is the company behind ELK stack",
"elk rocks",
"elasticsearch is rock solid"
]
}
(由于结果太长,此处略去)
这些分出来的token都会成为词典里一个term,注意有些token会出现多次,因此在倒排索引里记录的词频会比较高,同时记录的还有这些token在原文档里的偏移量和相对位置信息。
执行一次suggester搜索看看效果:
POST /blogs/_search
{
"suggest": {
"my-suggestion": {
"text": "lucne rock",
"term": {
"suggest_mode": "missing",
"field": "body"
}
}
}
}
suggest就是一种特殊类型的搜索,DSL内部的"text"指的是api调用方提供的文本,也就是通常用户界面上用户输入的内容。这里的lucne是错误的拼写,模拟用户输入错误。 "term"表示这是一个term suggester。 "field"指定suggester针对的字段,另外有一个可选的"suggest_mode"。 范例里的"missing"实际上就是缺省值,它是什么意思?有点挠头... 还是先看看返回结果吧:
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 0,
"max_score": 0,
"hits":
},
"suggest": {
"my-suggestion": [
{
"text": "lucne",
"offset": 0,
"length": 5,
"options": [
{
"text": "lucene",
"score": 0.8,
"freq": 2
}
]
},
{
"text": "rock",
"offset": 6,
"length": 4,
"options":
}
]
}
}
在返回结果里"suggest" -> "my-suggestion"部分包含了一个数组,每个数组项对应从输入文本分解出来的token(存放在"text"这个key里)以及为该token提供的建议词项(存放在options数组里)。 示例里返回了"lucne","rock"这2个词的建议项(options),其中"rock"的options是空的,表示没有可以建议的选项,为什么? 上面提到了,我们为查询提供的suggest mode是"missing",由于"rock"在索引的词典里已经存在了,够精准,就不建议啦。 只有词典里找不到词,才会为其提供相似的选项。
如果将"suggest_mode"换成"popular"会是什么效果?
尝试一下,重新执行查询,返回结果里"rock"这个词的option不再是空的,而是建议为rocks。
"suggest": {
"my-suggestion": [
{
"text": "lucne",
"offset": 0,
"length": 5,
"options": [
{
"text": "lucene",
"score": 0.8,
"freq": 2
}
]
},
{
"text": "rock",
"offset": 6,
"length": 4,
"options": [
{
"text": "rocks",
"score": 0.75,
"freq": 2
}
]
}
]
}
回想一下,rock和rocks在索引词典里都是有的。 不难看出即使用户输入的token在索引的词典里已经有了,但是因为存在一个词频更高的相似项,这个相似项可能是更合适的,就被挑选到options里了。 最后还有一个"always" mode,其含义是不管token是否存在于索引词典里都要给出相似项。
有人可能会问,两个term的相似性是如何判断的? ES使用了一种叫做Levenstein edit distance的算法,其核心思想就是一个词改动多少个字符就可以和另外一个词一致。 Term suggester还有其他很多可选参数来控制这个相似性的模糊程度,这里就不一一赘述了。
Term suggester正如其名,只基于analyze过的单个term去提供建议,并不会考虑多个term之间的关系。API调用方只需为每个token挑选options里的词,组合在一起返回给用户前端即可。 那么有无更直接办法,API直接给出和用户输入文本相似的内容? 答案是有,这就要求助Phrase Suggester了。
Phrase suggester在Term suggester的基础上,会考量多个term之间的关系,比如是否同时出现在索引的原文里,相邻程度,以及词频等等。看个范例就比较容易明白了:
POST /blogs/_search
{
"suggest": {
"my-suggestion": {
"text": "lucne and elasticsear rock",
"phrase": {
"field": "body",
"highlight": {
"pre_tag": "<em>",
"post_tag": "</em>"
}
}
}
}
}
返回结果:
"suggest": {
"my-suggestion": [
{
"text": "lucne and elasticsear rock",
"offset": 0,
"length": 26,
"options": [
{
"text": "lucene and elasticsearch rock",
"highlighted": "<em>lucene</em> and <em>elasticsearch</em> rock",
"score": 0.004993905
},
{
"text": "lucne and elasticsearch rock",
"highlighted": "lucne and <em>elasticsearch</em> rock",
"score": 0.0033391973
},
{
"text": "lucene and elasticsear rock",
"highlighted": "<em>lucene</em> and elasticsear rock",
"score": 0.0029183894
}
]
}
]
}
options直接返回一个phrase列表,由于加了highlight选项,被替换的term会被高亮。因为lucene和elasticsearch曾经在同一条原文里出现过,同时替换2个term的可信度更高,所以打分较高,排在第一位返回。Phrase suggester有相当多的参数用于控制匹配的模糊程度,需要根据实际应用情况去挑选和调试。
最后来谈一下Completion Suggester,它主要针对的应用场景就是"Auto Completion"。 此场景下用户每输入一个字符的时候,就需要即时发送一次查询请求到后端查找匹配项,在用户输入速度较高的情况下对后端响应速度要求比较苛刻。因此实现上它和前面两个Suggester采用了不同的数据结构,索引并非通过倒排来完成,而是将analyze过的数据编码成FST和索引一起存放。对于一个open状态的索引,FST会被ES整个装载到内存里的,进行前缀查找速度极快。但是FST只能用于前缀查找,这也是Completion Suggester的局限所在。
为了使用Completion Suggester,字段的类型需要专门定义如下:
PUT /blogs_completion/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "completion"
}
}
}
}
}
用bulk API索引点数据:
POST _bulk/?refresh=true
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Lucene is cool"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elasticsearch builds on top of lucene"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elasticsearch rocks"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "Elastic is the company behind ELK stack"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "the elk stack rocks"}
{ "index" : { "_index" : "blogs_completion", "_type" : "tech" } }
{ "body": "elasticsearch is rock solid"}
查找:
POST blogs_completion/_search?pretty
{ "size": 0,
"suggest": {
"blog-suggest": {
"prefix": "elastic i",
"completion": {
"field": "body"
}
}
}
}
结果:
"suggest": {
"blog-suggest": [
{
"text": "elastic i",
"offset": 0,
"length": 9,
"options": [
{
"text": "Elastic is the company behind ELK stack",
"_index": "blogs_completion",
"_type": "tech",
"_id": "AVrXFyn-cpYmMpGqDdcd",
"_score": 1,
"_source": {
"body": "Elastic is the company behind ELK stack"
}
}
]
}
]
}
值得注意的一点是Completion Suggester在索引原始数据的时候也要经过analyze阶段,取决于选用的analyzer不同,某些词可能会被转换,某些词可能被去除,这些会影响FST编码结果,也会影响查找匹配的效果。
比如我们删除上面的索引,重新设置索引的mapping,将analyzer更改为"english":
PUT /blogs_completion/
{
"mappings": {
"tech": {
"properties": {
"body": {
"type": "completion",
"analyzer": "english"
}
}
}
}
}
bulk api索引同样的数据后,执行下面的查询:
POST blogs_completion/_search?pretty
{ "size": 0,
"suggest": {
"blog-suggest": {
"prefix": "elastic i",
"completion": {
"field": "body"
}
}
}
}
居然没有匹配结果了,多么费解! 原来我们用的english analyzer会剥离掉stop word,而is就是其中一个,被剥离掉了!
用analyze api测试一下:
POST _analyze?analyzer=english
{
"text": "elasticsearch is rock solid"
}
会发现只有3个token:
{
"tokens": [
{
"token": "elasticsearch",
"start_offset": 0,
"end_offset": 13,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "rock",
"start_offset": 17,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "solid",
"start_offset": 22,
"end_offset": 27,
"type": "<ALPHANUM>",
"position": 3
}
]
}
FST只编码了这3个token,并且默认的还会记录他们在文档中的位置和分隔符。 用户输入"elastic i"进行查找的时候,输入被分解成"elastic"和"i",FST没有编码这个“i” , 匹配失败。
好吧,如果你现在还足够清醒的话,试一下搜索"elastic is",会发现又有结果,why? 因为这次输入的text经过english analyzer的时候is也被剥离了,只需在FST里查询"elastic"这个前缀,自然就可以匹配到了。
其他能影响completion suggester结果的,还有诸如"preserve_separators","preserve_position_increments"等等mapping参数来控制匹配的模糊程度。以及搜索时可以选用Fuzzy Queries,使得上面例子里的"elastic i"在使用english analyzer的情况下依然可以匹配到结果。
因此用好Completion Sugester并不是一件容易的事,实际应用开发过程中,需要根据数据特性和业务需要,灵活搭配analyzer和mapping参数,反复调试才可能获得理想的补全效果。
回到篇首Google搜索框的补全/纠错功能,如果用ES怎么实现呢?我能想到的一个的实现方式:
- 在用户刚开始输入的过程中,使用Completion Suggester进行关键词前缀匹配,刚开始匹配项会比较多,随着用户输入字符增多,匹配项越来越少。如果用户输入比较精准,可能Completion Suggester的结果已经够好,用户已经可以看到理想的备选项了。
- 如果Completion Suggester已经到了零匹配,那么可以猜测是否用户有输入错误,这时候可以尝试一下Phrase Suggester。
- 如果Phrase Suggester没有找到任何option,开始尝试term Suggester。
精准程度上(Precision)看: Completion > Phrase > term, 而召回率上(Recall)则反之。从性能上看,Completion Suggester是最快的,如果能满足业务需求,只用Completion Suggester做前缀匹配是最理想的。 Phrase和Term由于是做倒排索引的搜索,相比较而言性能应该要低不少,应尽量控制suggester用到的索引的数据量,最理想的状况是经过一定时间预热后,索引可以全量map到内存。 收起阅读 »
2017 Elastic 官方及社区国内活动日程安排
【线上活动】
在线直播
直播工具Zoom:https://elastic.zoom.us/j/522710614,房间号:522710614(密码进群索取)
- 《Elastic{ON}17 Keynote 回顾》,QQ 群(190605846),2017-3-17 21:00 PM,回放
- 《What's new in Elasticsearch 5》,QQ 群(190605846),2017-3-20 21:00 PM,回放
- 《What's new in Logstash 5》,QQ 群(190605846),2017-3-21 21:00 PM,回放
【线下活动】
Workshop
- Elastic Workshop,北京,2017-04-10【报名结束】
- Elastic Workshop,上海,2017-04-17【报名结束】
- Elastic Workshop,深圳,2017-04-20【报名结束】
- Elastic Workshop,上海,2017-06-29【报名结束】
- Elastic Workshop,广州,2017-07-04【报名结束】
- Elastic Workshop,深圳,2017-07-06【报名结束】
Meetup
- Elastic Meetup Shanghai ,上海,2017.5.14, 【报名结束】【日程】
- Elastic Meetup Beijing ,北京,2017.5.21 【报名结束】【日程】【直播回放】
- Elastic Meetup Nanjing,南京,2017.6.10 【报名结束】【日程】【直播回放】
- Elastic Meetup Hangzhou,杭州,2017.6.25 【报名结束】【日程】【直播回放】
- Elastic Meetup Changsha,长沙,2017.10.28 【报名结束】【日程】
- Elastic Meetup Wuhan,武汉,2017.11.4 【报名结束】【日程】
- Elastic Meetup Guangzhou,广州,2017.11.25 【报名结束】【日程】
- Elastic Meetup Shenzhen,深圳,2017.12.16 【报名结束】【日程】
【会议参展】
Elastic 今年继续赞助和支持各种开发者会议,欢迎届时来展台交流。
- Gopher China,上海,2017.04.15-2017.04.16
- OSC Shanghai ,上海,2017.5.13
- The China-R Conf,北京,2017.5.19-2017.5.21
- OSC Hangzhou,杭州,2017.6.24
- ArchSummit Shenzhen,深圳,2017.7.7-2017.7.8
- OSC Jinan,济南,2017.7.22
- OSC Zhuhai,珠海,2017.8.27
- RubyConf China,杭州,2017.9.16-17
- OSC Chengdu, 成都,2017.9.23
- OSC Chongqing,重庆,2017.9.24
- ArchSummit Beijing,2017.12.8-2017.12.9
上面是暂时确定的活动,部分活动报名链接晚点放出来,请关注本页面。
欢迎各个不同的城市的同学一起帮忙举办线下活动。
各个城市的线下活动欢迎报名分享,大家多交流,话题无论大小。
【线上活动】
在线直播
直播工具Zoom:https://elastic.zoom.us/j/522710614,房间号:522710614(密码进群索取)
- 《Elastic{ON}17 Keynote 回顾》,QQ 群(190605846),2017-3-17 21:00 PM,回放
- 《What's new in Elasticsearch 5》,QQ 群(190605846),2017-3-20 21:00 PM,回放
- 《What's new in Logstash 5》,QQ 群(190605846),2017-3-21 21:00 PM,回放
【线下活动】
Workshop
- Elastic Workshop,北京,2017-04-10【报名结束】
- Elastic Workshop,上海,2017-04-17【报名结束】
- Elastic Workshop,深圳,2017-04-20【报名结束】
- Elastic Workshop,上海,2017-06-29【报名结束】
- Elastic Workshop,广州,2017-07-04【报名结束】
- Elastic Workshop,深圳,2017-07-06【报名结束】
Meetup
- Elastic Meetup Shanghai ,上海,2017.5.14, 【报名结束】【日程】
- Elastic Meetup Beijing ,北京,2017.5.21 【报名结束】【日程】【直播回放】
- Elastic Meetup Nanjing,南京,2017.6.10 【报名结束】【日程】【直播回放】
- Elastic Meetup Hangzhou,杭州,2017.6.25 【报名结束】【日程】【直播回放】
- Elastic Meetup Changsha,长沙,2017.10.28 【报名结束】【日程】
- Elastic Meetup Wuhan,武汉,2017.11.4 【报名结束】【日程】
- Elastic Meetup Guangzhou,广州,2017.11.25 【报名结束】【日程】
- Elastic Meetup Shenzhen,深圳,2017.12.16 【报名结束】【日程】
【会议参展】
Elastic 今年继续赞助和支持各种开发者会议,欢迎届时来展台交流。
- Gopher China,上海,2017.04.15-2017.04.16
- OSC Shanghai ,上海,2017.5.13
- The China-R Conf,北京,2017.5.19-2017.5.21
- OSC Hangzhou,杭州,2017.6.24
- ArchSummit Shenzhen,深圳,2017.7.7-2017.7.8
- OSC Jinan,济南,2017.7.22
- OSC Zhuhai,珠海,2017.8.27
- RubyConf China,杭州,2017.9.16-17
- OSC Chengdu, 成都,2017.9.23
- OSC Chongqing,重庆,2017.9.24
- ArchSummit Beijing,2017.12.8-2017.12.9
上面是暂时确定的活动,部分活动报名链接晚点放出来,请关注本页面。
欢迎各个不同的城市的同学一起帮忙举办线下活动。
各个城市的线下活动欢迎报名分享,大家多交流,话题无论大小。 收起阅读 »
用es的官方工具curator4来配置和管理和优化es索引
https://vastxiao.github.io/art ... ning/
https://vastxiao.github.io/art ... ning/
Elastic{ON}17 见闻
在进入主题之前,我们先参观看看会场及周边情况吧,这次会场是在旧金山的 48 号码头,和去年的场地很近,不过今年的场地为了容纳的更多的人数,比去年的场地要大很多,码头对面就是著名的AT&T棒球场,看图。
今年多了一个开场的舞蹈,跟别人不一样的事,芭蕾舞蹈演员身上佩戴着若干闪光的传感器,在大背景墙上面可以看到随着演员的舞蹈,有不断变化的各种传感器实时分析的 Kibana 界面,这一切都是实时的哦。
然后就进入 Keynote 了,Elastic 公司 CEO Shay Banon 宣布 Elastic 的产品下载次数达到小目标,已经累计一个亿了。
Elastic 的产品的一个重要原则就是简单,为了让简单的事情变简单,比如采集日志文件,现在的 Filebeat 引入了模块的概念,相应模块直接提供对应成套的配置文件,包括 Mapping、Ingest pipeline、Kibana Dashboard, 启动 filebeat 收集数据进入 es 之后,直接就能可视化分析了。
现在使用 elasticsearch 来做 metric 分析的场景和用户越来越多,而 Kibana 的 Timeseries visual builder 就是为了 metric 场景而产生的一个新的特性,支持非常灵活的自定义可视化,还支持 elasticsearch 的 pipeline aggregation。
接下来就是,Elasticsearch 机器学习了,去年收购的 Perlert,目前已经和 Elasticsearch 无缝集成,现场 demo 演示了通过机器学习模块来分析日志的完整过程,实时的进行异常预测,从众多 service 的日志中找到 root cause。
然后就是 ECE,即 ElasticCloud 的私有云,企业可以很方便的借助它来实现搭建 Elastic 的私有云,集群管理,集群升级都很简单。
然后前 CEO Steven Schuurman 为大家揭晓了今年的第一届 Elastic Cause Awards 获奖的结果,你知道吗,Elasticsearch 正被用于预防埃博拉病毒、拯救人口贩卖以及防止校园暴力等很多有意义的项目中。
然后 Costin Leau 为大家演示了 Elasticsearch-sql,新的和 Elasticsearch 交互的方式,jdbc 兼容,大家期盼已久的功能终于来了,你可以使用现有的支持 jdbc 协议的各种工具来使用 elasticsearch 了,当然不会是完整的 SQL 标准协议,但满足大部分常见的简单的场景。
接下来,Rashid Khan 为大家介绍了与社区相关的一些统计,到最后才跳出来,这些炫酷的 infographic 和 presentation 居然就是在 Kibana 上面,并且这些数据是实时变化的,从而引入了 Kibana 新的功能:Kibana Canvas,借助它,你可以灵活布局设计报表或是 presentation,与后端Elasticsearch数据实时连接,另外与大家通常熟知的静态的 infographic 不同,所有的这些可视化图形都是可以交互操作的,比如过滤与搜索,从此,数据的探索与分析又有了一种新的方式了。
Keynote 的内容主要就到这里了,下午还有很多其他的具体的演讲,都是各个产品的具体的新的特性,回头再补充。
现场还有很多各种类型的 Demo。
想知道 Elastic{ON}17 后续几天还有什么新鲜事么,欢迎关注我的微博:@medcl 和 Elastic 中文社区公众号。
在进入主题之前,我们先参观看看会场及周边情况吧,这次会场是在旧金山的 48 号码头,和去年的场地很近,不过今年的场地为了容纳的更多的人数,比去年的场地要大很多,码头对面就是著名的AT&T棒球场,看图。
今年多了一个开场的舞蹈,跟别人不一样的事,芭蕾舞蹈演员身上佩戴着若干闪光的传感器,在大背景墙上面可以看到随着演员的舞蹈,有不断变化的各种传感器实时分析的 Kibana 界面,这一切都是实时的哦。
然后就进入 Keynote 了,Elastic 公司 CEO Shay Banon 宣布 Elastic 的产品下载次数达到小目标,已经累计一个亿了。
Elastic 的产品的一个重要原则就是简单,为了让简单的事情变简单,比如采集日志文件,现在的 Filebeat 引入了模块的概念,相应模块直接提供对应成套的配置文件,包括 Mapping、Ingest pipeline、Kibana Dashboard, 启动 filebeat 收集数据进入 es 之后,直接就能可视化分析了。
现在使用 elasticsearch 来做 metric 分析的场景和用户越来越多,而 Kibana 的 Timeseries visual builder 就是为了 metric 场景而产生的一个新的特性,支持非常灵活的自定义可视化,还支持 elasticsearch 的 pipeline aggregation。
接下来就是,Elasticsearch 机器学习了,去年收购的 Perlert,目前已经和 Elasticsearch 无缝集成,现场 demo 演示了通过机器学习模块来分析日志的完整过程,实时的进行异常预测,从众多 service 的日志中找到 root cause。
然后就是 ECE,即 ElasticCloud 的私有云,企业可以很方便的借助它来实现搭建 Elastic 的私有云,集群管理,集群升级都很简单。
然后前 CEO Steven Schuurman 为大家揭晓了今年的第一届 Elastic Cause Awards 获奖的结果,你知道吗,Elasticsearch 正被用于预防埃博拉病毒、拯救人口贩卖以及防止校园暴力等很多有意义的项目中。
然后 Costin Leau 为大家演示了 Elasticsearch-sql,新的和 Elasticsearch 交互的方式,jdbc 兼容,大家期盼已久的功能终于来了,你可以使用现有的支持 jdbc 协议的各种工具来使用 elasticsearch 了,当然不会是完整的 SQL 标准协议,但满足大部分常见的简单的场景。
接下来,Rashid Khan 为大家介绍了与社区相关的一些统计,到最后才跳出来,这些炫酷的 infographic 和 presentation 居然就是在 Kibana 上面,并且这些数据是实时变化的,从而引入了 Kibana 新的功能:Kibana Canvas,借助它,你可以灵活布局设计报表或是 presentation,与后端Elasticsearch数据实时连接,另外与大家通常熟知的静态的 infographic 不同,所有的这些可视化图形都是可以交互操作的,比如过滤与搜索,从此,数据的探索与分析又有了一种新的方式了。
Keynote 的内容主要就到这里了,下午还有很多其他的具体的演讲,都是各个产品的具体的新的特性,回头再补充。
现场还有很多各种类型的 Demo。
想知道 Elastic{ON}17 后续几天还有什么新鲜事么,欢迎关注我的微博:@medcl 和 Elastic 中文社区公众号。
收起阅读 »
使用 Elastic Stack 来监控和调优 Golang 应用程序
Elastic Stack 其实是一个集合,包含 Elasticsearch、Logstash 和 Beats 这几个开源软件,而 Beats 又包含 Filebeat、Packetbeat、Winlogbeat、Metricbeat 和新出的 Heartbeat,呵呵,有点多吧,恩,每个 beat 做的事情不一样,没关系,今天主要用到 Elasticsearch、Metricbeat 和 Kibana 就行了。
Metricbeat 是一个专门用来获取服务器或应用服务内部运行指标数据的收集程序,也是 Golang 写的,部署包比较小才10M 左右,对目标服务器的部署环境也没有依赖,内存资源占用和 CPU 开销也较小,目前除了可以监控服务器本身的资源使用情况外,还支持常见的应用服务器和服务,目前支持列表如下:
- Apache Module
- Couchbase Module
- Docker Module
- HAProxy Module
- kafka Module
- MongoDB Module
- MySQL Module
- Nginx Module
- PostgreSQL Module
- Prometheus Module
- Redis Module
- System Module
- ZooKeeper Module
当然,也有可能你的应用不在上述列表,没关系,Metricbeat 是可以扩展的,你可以很方便的实现一个模块,而本文接下来所用的 Golang Module 也就是我刚刚为 Metricbeat 添加的扩展模块,目前已经 merge 进入 Metricbeat 的 master 分支,预计会在 6.0 版本发布,想了解是如何扩展这个模块的可以查看 代码路径 和 PR地址。
上面的这些可能还不够吸引人,我们来看一下 Kibana 对 Metricbeat 使用 Golang 模块收集的数据进行的可视化分析吧:
上面的图简单解读一下:
最上面一栏是 Golang Heap 的摘要信息,可以大致了解 Golang 的内存使用和 GC 情况,System 表示 Golang 程序从操作系统申请的内存,可以理解为进程所占的内存(注意不是进程对应的虚拟内存),Bytes allocated 表示 Heap 目前分配的内存,也就是 Golang 里面直接可使用的内存,GC limit 表示当 Golang 的 Heap 内存分配达到这个 limit 值之后就会开始执行 GC,这个值会随着每次 GC 而变化, GC cycles 则代表监控周期内的 GC 次数;
中间的三列分别是堆内存、进程内存和对象的统计情况;Heap Allocated 表示正在用和没有用但还未被回收的对象的大小;Heap Inuse 显然就是活跃的对象大小了;Heap Idle 表示已分配但空闲的内存;
底下两列是 GC 时间和 GC 次数的监控统计,CPUFraction 这个代表该进程 CPU 占用时间花在 GC 上面的百分比,值越大说明 GC 越频繁,浪费更多的时间在 GC 上面,上图虽然趋势陡峭,但是看范围在0.41%~0.52%之间,看起来还算可以,如果GC 比率占到个位数甚至更多比例,那肯定需要进一步优化程序了。
有了这些信息我们就能够知道该 Golang 的内存使用和分配情况和 GC 的执行情况,假如要分析是否有内存泄露,看内存使用和堆内存分配的趋势是否平稳就可以了,另外 GC_Limit 和 Byte Allocation 一直上升,那肯定就是有内存泄露了,结合历史信息还能对不同版本/提交对 Golang 的内存使用和 GC 影响进行分析。
接下来就要给大家介绍如何具体使用了,首先需要启用 Golang 的 expvar 服务,expvar(https://golang.org/pkg/expvar/) 是 Golang 提供的一个暴露内部变量或统计信息的标准包。
使用的方法很简单,只需要在 Golang 的程序引入该包即可,它会自动注册现有的 http 服务上,如下:
import _ "expvar"
如果 Golang 没有启动 http 服务,使用下面的方式启动一个即可,这里端口是 6060,如下:func metricsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
first := true
report := func(key string, value interface{}) {
if !first {
fmt.Fprintf(w, ",\n")
}
first = false
if str, ok := value.(string); ok {
fmt.Fprintf(w, "%q: %q", key, str)
} else {
fmt.Fprintf(w, "%q: %v", key, value)
}
}
fmt.Fprintf(w, "{\n")
expvar.Do(func(kv expvar.KeyValue) {
report(kv.Key, kv.Value)
})
fmt.Fprintf(w, "\n}\n")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/debug/vars", metricsHandler)
endpoint := http.ListenAndServe("localhost:6060", mux)
}
默认注册的访问路径是/debug/vars, 编译启动之后,就可以通过 http://localhost:6060/debug/vars 来访问 expvar 以 JSON 格式暴露出来的这些内部变量,默认提供了 Golang 的 runtime.Memstats 信息,也就是上面分析的数据源,当然你还可以注册自己的变量,这里暂时不提。OK,现在我们的 Golang 程序已经启动了,并且通过 expvar 暴露出了运行时的内存使用情况,现在我们需要使用 Metricbeat 来获取这些信息并存进 Elasticsearch。
关于 Metricbeat 的安装其实很简单,下载对应平台的包解压(下载地址:https://www.elastic.co/downloads/beats/metricbeat ),启动 Metricbeat 前,修改配置文件:metricbeat.yml
metricbeat.modules:
- module: golang
metricsets: ["heap"]
enabled: true
period: 10s
hosts: ["localhost:6060"]
heap.path: "/debug/vars"
上面的参数启用了 Golang 监控模块,并且会10秒获取一次配置路径的返回内存数据,我们同样配置该配置文件,设置数据输出到本机的 Elasticsearch:output.elasticsearch:
hosts: ["localhost:9200"]
现在启动 Metricbeat:
./metricbeat -e -v
现在在 Elasticsearch 应该就有数据了,当然记得确保 Elasticsearch 和 Kibana 是可用状态,你可以在 Kibana 根据数据灵活自定义可视化,推荐使用 Timelion 来进行分析,当然为了方便也可以直接导入提供的样例仪表板,就是上面第一个图的效果。关于如何导入样例仪表板请参照这个文档:https://www.elastic.co/guide/e ... .html
除了监控已经有的内存信息之外,如果你还有一些内部的业务指标想要暴露出来,也是可以的,通过 expvar 来做同样可以。一个简单的例子如下:
var inerInt int64 = 1024
pubInt := expvar.NewInt("your_metric_key")
pubInt.Set(inerInt)
pubInt.Add(2)
在 Metricbeat 内部也同样暴露了很多内部运行的信息,所以 Metricbeat 可以自己监控自己了。。。首先,启动的时候带上参数设置pprof监控的地址,如下:
./metricbeat -httpprof="127.0.0.1:6060" -e -v
这样我们就能够通过 [url=http://127.0.0.1:6060/debug/vars]http://127.0.0.1:6060/debug/vars[/url] 访问到内部运行情况了,如下:{
"output.events.acked": 1088,
"output.write.bytes": 1027455,
"output.write.errors": 0,
"output.messages.dropped": 0,
"output.elasticsearch.publishEvents.call.count": 24,
"output.elasticsearch.read.bytes": 12215,
"output.elasticsearch.read.errors": 0,
"output.elasticsearch.write.bytes": 1027455,
"output.elasticsearch.write.errors": 0,
"output.elasticsearch.events.acked": 1088,
"output.elasticsearch.events.not_acked": 0,
"output.kafka.events.acked": 0,
"output.kafka.events.not_acked": 0,
"output.kafka.publishEvents.call.count": 0,
"output.logstash.write.errors": 0,
"output.logstash.write.bytes": 0,
"output.logstash.events.acked": 0,
"output.logstash.events.not_acked": 0,
"output.logstash.publishEvents.call.count": 0,
"output.logstash.read.bytes": 0,
"output.logstash.read.errors": 0,
"output.redis.events.acked": 0,
"output.redis.events.not_acked": 0,
"output.redis.read.bytes": 0,
"output.redis.read.errors": 0,
"output.redis.write.bytes": 0,
"output.redis.write.errors": 0,
"beat.memstats.memory_total": 155721720,
"beat.memstats.memory_alloc": 3632728,
"beat.memstats.gc_next": 6052800,
"cmdline": ["./metricbeat","-httpprof=127.0.0.1:6060","-e","-v"],
"fetches": {"system-cpu": {"events": 4, "failures": 0, "success": 4}, "system-filesystem": {"events": 20, "failures": 0, "success": 4}, "system-fsstat": {"events": 4, "failures": 0, "success": 4}, "system-load": {"events": 4, "failures": 0, "success": 4}, "system-memory": {"events": 4, "failures": 0, "success": 4}, "system-network": {"events": 44, "failures": 0, "success": 4}, "system-process": {"events": 1008, "failures": 0, "success": 4}},
"libbeat.config.module.running": 0,
"libbeat.config.module.starts": 0,
"libbeat.config.module.stops": 0,
"libbeat.config.reloads": 0,
"memstats": {"Alloc":3637704,"TotalAlloc":155
... ...
比如,上面就能看到output模块Elasticsearch的处理情况,如 output.elasticsearch.events.acked 参数表示发送到 Elasticsearch Ack返回之后的消息。现在我们要修改 Metricbeat 的配置文件,Golang 模块有两个 metricset,可以理解为两个监控的指标类型,我们现在需要加入一个新的 expvar 类型,这个即自定义的其他指标,相应配置文件修改如下:
- module: golang
metricsets: ["heap","expvar"]
enabled: true
period: 1s
hosts: ["localhost:6060"]
heap.path: "/debug/vars"
expvar:
namespace: "metricbeat"
path: "/debug/vars"
上面的一个参数 namespace 表示自定义指标的一个命令空间,主要是为了方便管理,这里是 Metricbeat 自身的信息,所以 namespace 就是 metricbeat。重启 Metricbeat 应该就能收到新的数据了,我们前往 Kibana。
这里假设关注 output.elasticsearch.events.acked和
output.elasticsearch.events.not_acked这两个指标,我们在Kibana里面简单定义一个曲线图就能看到 Metricbeat 发往 Elasticsearch 消息的成功和失败趋势。
Timelion 表达式:
.es("metricbeat*",metric="max:golang.metricbeat.output.elasticsearch.events.acked").derivative().label("Elasticsearch Success"),.es("metricbeat*",metric="max:golang.metricbeat.output.elasticsearch.events.not_acked").derivative().label("Elasticsearch Failed")
效果如下:从上图可以看到,发往 Elasticsearch 的消息很稳定,没有出现丢消息的情况,同时关于 Metricbeat 的内存情况,我们打开导入的 Dashboard 查看:
关于如何使用 Metricbeat 来监控 Golang 应用程序的内容基本上就差不多到这里了,上面介绍了如何基于 expvar 来监控 Golang 的内存情况和自定义业务监控指标,在结合 Elastic Stack 可以快速的进行分析,希望对大家有用。
最后,这个 Golang 模块目前还没 release,估计在 beats 6.0 发布,有兴趣尝鲜的可以自己下载源码打包。
Elastic Stack 其实是一个集合,包含 Elasticsearch、Logstash 和 Beats 这几个开源软件,而 Beats 又包含 Filebeat、Packetbeat、Winlogbeat、Metricbeat 和新出的 Heartbeat,呵呵,有点多吧,恩,每个 beat 做的事情不一样,没关系,今天主要用到 Elasticsearch、Metricbeat 和 Kibana 就行了。
Metricbeat 是一个专门用来获取服务器或应用服务内部运行指标数据的收集程序,也是 Golang 写的,部署包比较小才10M 左右,对目标服务器的部署环境也没有依赖,内存资源占用和 CPU 开销也较小,目前除了可以监控服务器本身的资源使用情况外,还支持常见的应用服务器和服务,目前支持列表如下:
- Apache Module
- Couchbase Module
- Docker Module
- HAProxy Module
- kafka Module
- MongoDB Module
- MySQL Module
- Nginx Module
- PostgreSQL Module
- Prometheus Module
- Redis Module
- System Module
- ZooKeeper Module
当然,也有可能你的应用不在上述列表,没关系,Metricbeat 是可以扩展的,你可以很方便的实现一个模块,而本文接下来所用的 Golang Module 也就是我刚刚为 Metricbeat 添加的扩展模块,目前已经 merge 进入 Metricbeat 的 master 分支,预计会在 6.0 版本发布,想了解是如何扩展这个模块的可以查看 代码路径 和 PR地址。
上面的这些可能还不够吸引人,我们来看一下 Kibana 对 Metricbeat 使用 Golang 模块收集的数据进行的可视化分析吧:
上面的图简单解读一下:
最上面一栏是 Golang Heap 的摘要信息,可以大致了解 Golang 的内存使用和 GC 情况,System 表示 Golang 程序从操作系统申请的内存,可以理解为进程所占的内存(注意不是进程对应的虚拟内存),Bytes allocated 表示 Heap 目前分配的内存,也就是 Golang 里面直接可使用的内存,GC limit 表示当 Golang 的 Heap 内存分配达到这个 limit 值之后就会开始执行 GC,这个值会随着每次 GC 而变化, GC cycles 则代表监控周期内的 GC 次数;
中间的三列分别是堆内存、进程内存和对象的统计情况;Heap Allocated 表示正在用和没有用但还未被回收的对象的大小;Heap Inuse 显然就是活跃的对象大小了;Heap Idle 表示已分配但空闲的内存;
底下两列是 GC 时间和 GC 次数的监控统计,CPUFraction 这个代表该进程 CPU 占用时间花在 GC 上面的百分比,值越大说明 GC 越频繁,浪费更多的时间在 GC 上面,上图虽然趋势陡峭,但是看范围在0.41%~0.52%之间,看起来还算可以,如果GC 比率占到个位数甚至更多比例,那肯定需要进一步优化程序了。
有了这些信息我们就能够知道该 Golang 的内存使用和分配情况和 GC 的执行情况,假如要分析是否有内存泄露,看内存使用和堆内存分配的趋势是否平稳就可以了,另外 GC_Limit 和 Byte Allocation 一直上升,那肯定就是有内存泄露了,结合历史信息还能对不同版本/提交对 Golang 的内存使用和 GC 影响进行分析。
接下来就要给大家介绍如何具体使用了,首先需要启用 Golang 的 expvar 服务,expvar(https://golang.org/pkg/expvar/) 是 Golang 提供的一个暴露内部变量或统计信息的标准包。
使用的方法很简单,只需要在 Golang 的程序引入该包即可,它会自动注册现有的 http 服务上,如下:
import _ "expvar"
如果 Golang 没有启动 http 服务,使用下面的方式启动一个即可,这里端口是 6060,如下:func metricsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
first := true
report := func(key string, value interface{}) {
if !first {
fmt.Fprintf(w, ",\n")
}
first = false
if str, ok := value.(string); ok {
fmt.Fprintf(w, "%q: %q", key, str)
} else {
fmt.Fprintf(w, "%q: %v", key, value)
}
}
fmt.Fprintf(w, "{\n")
expvar.Do(func(kv expvar.KeyValue) {
report(kv.Key, kv.Value)
})
fmt.Fprintf(w, "\n}\n")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/debug/vars", metricsHandler)
endpoint := http.ListenAndServe("localhost:6060", mux)
}
默认注册的访问路径是/debug/vars, 编译启动之后,就可以通过 http://localhost:6060/debug/vars 来访问 expvar 以 JSON 格式暴露出来的这些内部变量,默认提供了 Golang 的 runtime.Memstats 信息,也就是上面分析的数据源,当然你还可以注册自己的变量,这里暂时不提。OK,现在我们的 Golang 程序已经启动了,并且通过 expvar 暴露出了运行时的内存使用情况,现在我们需要使用 Metricbeat 来获取这些信息并存进 Elasticsearch。
关于 Metricbeat 的安装其实很简单,下载对应平台的包解压(下载地址:https://www.elastic.co/downloads/beats/metricbeat ),启动 Metricbeat 前,修改配置文件:metricbeat.yml
metricbeat.modules:
- module: golang
metricsets: ["heap"]
enabled: true
period: 10s
hosts: ["localhost:6060"]
heap.path: "/debug/vars"
上面的参数启用了 Golang 监控模块,并且会10秒获取一次配置路径的返回内存数据,我们同样配置该配置文件,设置数据输出到本机的 Elasticsearch:output.elasticsearch:
hosts: ["localhost:9200"]
现在启动 Metricbeat:
./metricbeat -e -v
现在在 Elasticsearch 应该就有数据了,当然记得确保 Elasticsearch 和 Kibana 是可用状态,你可以在 Kibana 根据数据灵活自定义可视化,推荐使用 Timelion 来进行分析,当然为了方便也可以直接导入提供的样例仪表板,就是上面第一个图的效果。关于如何导入样例仪表板请参照这个文档:https://www.elastic.co/guide/e ... .html
除了监控已经有的内存信息之外,如果你还有一些内部的业务指标想要暴露出来,也是可以的,通过 expvar 来做同样可以。一个简单的例子如下:
var inerInt int64 = 1024
pubInt := expvar.NewInt("your_metric_key")
pubInt.Set(inerInt)
pubInt.Add(2)
在 Metricbeat 内部也同样暴露了很多内部运行的信息,所以 Metricbeat 可以自己监控自己了。。。首先,启动的时候带上参数设置pprof监控的地址,如下:
./metricbeat -httpprof="127.0.0.1:6060" -e -v
这样我们就能够通过 [url=http://127.0.0.1:6060/debug/vars]http://127.0.0.1:6060/debug/vars[/url] 访问到内部运行情况了,如下:{
"output.events.acked": 1088,
"output.write.bytes": 1027455,
"output.write.errors": 0,
"output.messages.dropped": 0,
"output.elasticsearch.publishEvents.call.count": 24,
"output.elasticsearch.read.bytes": 12215,
"output.elasticsearch.read.errors": 0,
"output.elasticsearch.write.bytes": 1027455,
"output.elasticsearch.write.errors": 0,
"output.elasticsearch.events.acked": 1088,
"output.elasticsearch.events.not_acked": 0,
"output.kafka.events.acked": 0,
"output.kafka.events.not_acked": 0,
"output.kafka.publishEvents.call.count": 0,
"output.logstash.write.errors": 0,
"output.logstash.write.bytes": 0,
"output.logstash.events.acked": 0,
"output.logstash.events.not_acked": 0,
"output.logstash.publishEvents.call.count": 0,
"output.logstash.read.bytes": 0,
"output.logstash.read.errors": 0,
"output.redis.events.acked": 0,
"output.redis.events.not_acked": 0,
"output.redis.read.bytes": 0,
"output.redis.read.errors": 0,
"output.redis.write.bytes": 0,
"output.redis.write.errors": 0,
"beat.memstats.memory_total": 155721720,
"beat.memstats.memory_alloc": 3632728,
"beat.memstats.gc_next": 6052800,
"cmdline": ["./metricbeat","-httpprof=127.0.0.1:6060","-e","-v"],
"fetches": {"system-cpu": {"events": 4, "failures": 0, "success": 4}, "system-filesystem": {"events": 20, "failures": 0, "success": 4}, "system-fsstat": {"events": 4, "failures": 0, "success": 4}, "system-load": {"events": 4, "failures": 0, "success": 4}, "system-memory": {"events": 4, "failures": 0, "success": 4}, "system-network": {"events": 44, "failures": 0, "success": 4}, "system-process": {"events": 1008, "failures": 0, "success": 4}},
"libbeat.config.module.running": 0,
"libbeat.config.module.starts": 0,
"libbeat.config.module.stops": 0,
"libbeat.config.reloads": 0,
"memstats": {"Alloc":3637704,"TotalAlloc":155
... ...
比如,上面就能看到output模块Elasticsearch的处理情况,如 output.elasticsearch.events.acked 参数表示发送到 Elasticsearch Ack返回之后的消息。现在我们要修改 Metricbeat 的配置文件,Golang 模块有两个 metricset,可以理解为两个监控的指标类型,我们现在需要加入一个新的 expvar 类型,这个即自定义的其他指标,相应配置文件修改如下:
- module: golang
metricsets: ["heap","expvar"]
enabled: true
period: 1s
hosts: ["localhost:6060"]
heap.path: "/debug/vars"
expvar:
namespace: "metricbeat"
path: "/debug/vars"
上面的一个参数 namespace 表示自定义指标的一个命令空间,主要是为了方便管理,这里是 Metricbeat 自身的信息,所以 namespace 就是 metricbeat。重启 Metricbeat 应该就能收到新的数据了,我们前往 Kibana。
这里假设关注 output.elasticsearch.events.acked和
output.elasticsearch.events.not_acked这两个指标,我们在Kibana里面简单定义一个曲线图就能看到 Metricbeat 发往 Elasticsearch 消息的成功和失败趋势。
Timelion 表达式:
.es("metricbeat*",metric="max:golang.metricbeat.output.elasticsearch.events.acked").derivative().label("Elasticsearch Success"),.es("metricbeat*",metric="max:golang.metricbeat.output.elasticsearch.events.not_acked").derivative().label("Elasticsearch Failed")
效果如下:从上图可以看到,发往 Elasticsearch 的消息很稳定,没有出现丢消息的情况,同时关于 Metricbeat 的内存情况,我们打开导入的 Dashboard 查看:
关于如何使用 Metricbeat 来监控 Golang 应用程序的内容基本上就差不多到这里了,上面介绍了如何基于 expvar 来监控 Golang 的内存情况和自定义业务监控指标,在结合 Elastic Stack 可以快速的进行分析,希望对大家有用。
最后,这个 Golang 模块目前还没 release,估计在 beats 6.0 发布,有兴趣尝鲜的可以自己下载源码打包。 收起阅读 »
为什么用java重写logstash
写之前这里先打个广告,java版本的logstash已经开源。git地址:https://github.com/dtstack,下面进入正题。
jlogstash性能:
当时袋鼠云的云日志系统的日志接收端是ruby版本的logstash,存储使用的是elasticsearch,前端的展示没有使用原生的kibana,而是自己写了一套前端。
本人是负责日志接收端的logstash开发人员,主要负责基于ruby版本的logstash编写一些公司业务需要的插件。
当时为了提升性能做了各种优化,比如用java重写了一些模块,再用ruby调用这些模块,比如ip的解析模块,但是最终优化的结果只是单机4core、4g的虚拟机每小时最多能处理800万的数据而已(我们的场景跟大部分人一样都是订阅kafka的消息,再经过一些filter(瓶颈主要是这里比较耗cpu)写入elasticsearch)。
因为logstash的核心代码是用ruby语言开发,虽然运行在jruby上,但是由于中间涉及到数据结构的转化,性能跟原生的class运行在jvm上肯定是有所差距的。
所以当时抱着尽可能最大程度上提升性能,更好地满足用户需求的目的,用java重写了logstash,并把需要用到的插件也进行了重写。在同样的4core、4g虚拟机环境下,每小时能处理4000万数据,性能有了近5倍的提升。
下面是java logstash 和 ruby logstash(2.3.2版本)按照logstash官方测试方案做的性能对比:
git 地址(https://github.com/DTStack/jlo ... sting)
以上三种场景的处理效率
java版本logstash性能分别是ruby版本logstash的2.99倍、4.15倍、3.49倍。
jlogstash尽可能保证数据不丢失:
ruby 版本的logstash,对保证数据防丢失这块没做太多的设计。
举个列子:数据从kafka消费再output到elasticsearch,一旦elasticsearch集群不可用,ruby logstash会自动重试几次,如果还不成功就会放弃继续消费kafka里的数据,而且重试的动作也是elasticsearch插件自身来完成的,logstash本身并没有对数据防丢失做设计。
而java 版本logstash 的BaseOutput 这个抽象类里面有个failedMsgQueue队列,每个output实例维护一个,output 插件需要自身判断哪些数据失败了,再调用addFailedMsg方法把失败的数据写入到failedMsgQueue队列里。java logstash一旦发现failedMsgQueue里面有数据就会调用sendFailedMsg这个方法来消费这里的数据,直到数据消费完成才会去消费input里的数据。这个逻辑是可以通过consistency这个属性来控制的。该属性默认是关闭的。
还有一点是input和output插件都提供了release方法,这个主要是为了jvm退出时要执行一些动作而设计的。
因为大部分的input和output插件在获取和发送数据时都会先放在一个集合里面,再去慢慢消耗集合里面的数据。这样jvm退出时,插件就可以各自实现自己的逻辑,从而保证jvm退出前,集合里面的数据彻底消耗完。当然如果你强制杀死该进程(kill -9)那就没法保证了。
现在我们的elasticsearch插件已经实现了数据防丢失逻辑,并且已经在我们的生产环境稳定的跑了很长时间了。
jlogstash可以是分布式应用,而不只是单机应用:
我们这边开发了kafkadistributed插件,通过zookeeper把jlogstash变成分布式应用,因为从客户端采集上来的数据分发到kafka集群中数据是无序的,比如要分析jvm1.8 gc日志,cms的gc日志分成5个步骤,这5个步骤是一个整体,中间有可能夹杂着yonggc的日志,这样数据采集到kafka的时候就会乱序,后端又有多台jlogstash去订阅kafka,这样多台单机版本的jlogstash是没法保证一个完整的cms日志进入到同一个jvm上进行统一分析,所以我们通过zookeeper把jlogstash变成分布式应用,会把同一个文件的日志分发到同一个jlogstash上,这样就能保证cms数据的完整性。
最后希望jlogstash能为一些开发者解决一些问题,也希望有更多的人参与到jlogstash的插件开发里来。
注释:有人问jlogstash跟hangout有什么区别,这里就不做说明了,有兴趣的同学可以看看这两个的源码就知道区别了。
写之前这里先打个广告,java版本的logstash已经开源。git地址:https://github.com/dtstack,下面进入正题。
jlogstash性能:
当时袋鼠云的云日志系统的日志接收端是ruby版本的logstash,存储使用的是elasticsearch,前端的展示没有使用原生的kibana,而是自己写了一套前端。
本人是负责日志接收端的logstash开发人员,主要负责基于ruby版本的logstash编写一些公司业务需要的插件。
当时为了提升性能做了各种优化,比如用java重写了一些模块,再用ruby调用这些模块,比如ip的解析模块,但是最终优化的结果只是单机4core、4g的虚拟机每小时最多能处理800万的数据而已(我们的场景跟大部分人一样都是订阅kafka的消息,再经过一些filter(瓶颈主要是这里比较耗cpu)写入elasticsearch)。
因为logstash的核心代码是用ruby语言开发,虽然运行在jruby上,但是由于中间涉及到数据结构的转化,性能跟原生的class运行在jvm上肯定是有所差距的。
所以当时抱着尽可能最大程度上提升性能,更好地满足用户需求的目的,用java重写了logstash,并把需要用到的插件也进行了重写。在同样的4core、4g虚拟机环境下,每小时能处理4000万数据,性能有了近5倍的提升。
下面是java logstash 和 ruby logstash(2.3.2版本)按照logstash官方测试方案做的性能对比:
git 地址(https://github.com/DTStack/jlo ... sting)
以上三种场景的处理效率
java版本logstash性能分别是ruby版本logstash的2.99倍、4.15倍、3.49倍。
jlogstash尽可能保证数据不丢失:
ruby 版本的logstash,对保证数据防丢失这块没做太多的设计。
举个列子:数据从kafka消费再output到elasticsearch,一旦elasticsearch集群不可用,ruby logstash会自动重试几次,如果还不成功就会放弃继续消费kafka里的数据,而且重试的动作也是elasticsearch插件自身来完成的,logstash本身并没有对数据防丢失做设计。
而java 版本logstash 的BaseOutput 这个抽象类里面有个failedMsgQueue队列,每个output实例维护一个,output 插件需要自身判断哪些数据失败了,再调用addFailedMsg方法把失败的数据写入到failedMsgQueue队列里。java logstash一旦发现failedMsgQueue里面有数据就会调用sendFailedMsg这个方法来消费这里的数据,直到数据消费完成才会去消费input里的数据。这个逻辑是可以通过consistency这个属性来控制的。该属性默认是关闭的。
还有一点是input和output插件都提供了release方法,这个主要是为了jvm退出时要执行一些动作而设计的。
因为大部分的input和output插件在获取和发送数据时都会先放在一个集合里面,再去慢慢消耗集合里面的数据。这样jvm退出时,插件就可以各自实现自己的逻辑,从而保证jvm退出前,集合里面的数据彻底消耗完。当然如果你强制杀死该进程(kill -9)那就没法保证了。
现在我们的elasticsearch插件已经实现了数据防丢失逻辑,并且已经在我们的生产环境稳定的跑了很长时间了。
jlogstash可以是分布式应用,而不只是单机应用:
我们这边开发了kafkadistributed插件,通过zookeeper把jlogstash变成分布式应用,因为从客户端采集上来的数据分发到kafka集群中数据是无序的,比如要分析jvm1.8 gc日志,cms的gc日志分成5个步骤,这5个步骤是一个整体,中间有可能夹杂着yonggc的日志,这样数据采集到kafka的时候就会乱序,后端又有多台jlogstash去订阅kafka,这样多台单机版本的jlogstash是没法保证一个完整的cms日志进入到同一个jvm上进行统一分析,所以我们通过zookeeper把jlogstash变成分布式应用,会把同一个文件的日志分发到同一个jlogstash上,这样就能保证cms数据的完整性。
最后希望jlogstash能为一些开发者解决一些问题,也希望有更多的人参与到jlogstash的插件开发里来。
注释:有人问jlogstash跟hangout有什么区别,这里就不做说明了,有兴趣的同学可以看看这两个的源码就知道区别了。
收起阅读 »
Elastic Stack 5.2.2 发布
- 修复request熔断器没有正确处理当前运行请求数的bug,当请求返回前却被客户端关闭时没有对计数减一,会造成节点慢慢的不能处理任何请求,除非重启节点,所有的用户都应该升级 #23317
- 修复cgroup正则解析的bug,造成节点的不能正常启动 #23219
- 被shard锁暂缓执行的请求可能会别其他线程启动,并且该请求丢失了上下文,会造成该请求被当做非法请求而拒绝
- 恢复terms agg的include/exclude参数的支持
下载:https://www.elastic.co/downloads/elasticsearch
完整的Release notes:https://www.elastic.co/guide/e ... .html
XPack release notes:https://www.elastic.co/guide/e ... 5.2.2
Logstash 5.2.2
- 修复持久化队列在windows启用造成的崩溃
- 修复多实例公用相同的数据目录造成的数据损坏
- 修复JVM性能指标收集造成的吞吐影响
下载:https://www.elastic.co/downloads/logstash
Release notes:https://www.elastic.co/guide/e ... .html
Kibana 5.2.2
- 之前的版本kibana的visualization依赖一个旧的Elasticsearch的include/exclude特性,但是该功能在Elasticsearch5.2.1被突然移除了,引起了kibana的visualization的错误,现在Kibana对新创建的visualization使用正确的结构,并且能在查询时自动转换遗留的旧结构到新的结构
- 从5.2.0开始,包含sub-bucket的垂直条形图(vertical bar)配置为分组没有合适的缩减y轴,造成相当小甚至某些情况下不可用,这次回归将再次对y轴进行必要的扩展而不管其条形图的模式
下载:https://www.elastic.co/downloads/kibana
完整Release notes https://www.elastic.co/guide/e ... .html
Beats 5.2.2
- Metricbeat修复当docker容器被kill掉造成的docker 模块挂起的bug
- Metricbeat修复超时时间设置而不是默认值
Release notes:https://www.elastic.co/guide/e ... .html
下载:https://www.elastic.co/downloads/beats/
- 修复request熔断器没有正确处理当前运行请求数的bug,当请求返回前却被客户端关闭时没有对计数减一,会造成节点慢慢的不能处理任何请求,除非重启节点,所有的用户都应该升级 #23317
- 修复cgroup正则解析的bug,造成节点的不能正常启动 #23219
- 被shard锁暂缓执行的请求可能会别其他线程启动,并且该请求丢失了上下文,会造成该请求被当做非法请求而拒绝
- 恢复terms agg的include/exclude参数的支持
下载:https://www.elastic.co/downloads/elasticsearch
完整的Release notes:https://www.elastic.co/guide/e ... .html
XPack release notes:https://www.elastic.co/guide/e ... 5.2.2
Logstash 5.2.2
- 修复持久化队列在windows启用造成的崩溃
- 修复多实例公用相同的数据目录造成的数据损坏
- 修复JVM性能指标收集造成的吞吐影响
下载:https://www.elastic.co/downloads/logstash
Release notes:https://www.elastic.co/guide/e ... .html
Kibana 5.2.2
- 之前的版本kibana的visualization依赖一个旧的Elasticsearch的include/exclude特性,但是该功能在Elasticsearch5.2.1被突然移除了,引起了kibana的visualization的错误,现在Kibana对新创建的visualization使用正确的结构,并且能在查询时自动转换遗留的旧结构到新的结构
- 从5.2.0开始,包含sub-bucket的垂直条形图(vertical bar)配置为分组没有合适的缩减y轴,造成相当小甚至某些情况下不可用,这次回归将再次对y轴进行必要的扩展而不管其条形图的模式
下载:https://www.elastic.co/downloads/kibana
完整Release notes https://www.elastic.co/guide/e ... .html
Beats 5.2.2
- Metricbeat修复当docker容器被kill掉造成的docker 模块挂起的bug
- Metricbeat修复超时时间设置而不是默认值
Release notes:https://www.elastic.co/guide/e ... .html
下载:https://www.elastic.co/downloads/beats/ 收起阅读 »
欢迎参加Elastic的Meetup线下活动问卷调查
同学们,乡亲们:
想要今年的Elastic线下活动来到您身边么,快参加我们的问卷调查吧,如果您的城市不在下拉列表,记得添加进去,问卷调查比较简单啦,大概只需要花费您几分钟时间,快来吧:https://www.surveymonkey.com/r/elastic-china17 。
另外Elastic也在寻找各个城市的演讲者、场地赞助、协办方、志愿者。如果您有项目用到了任何Elasticsearch、Kibana、Logstash或Beats,并且有兴趣分享您的经验故事(不管是5分钟还是50分钟)请让我们知道,我们非常愿意与我们的社区一起分享您的故事。不管是哪种参与方式都欢迎,请在问卷内留下联系方式或联系我:medcl123(添加注明来意)。
我们感谢您参与本次问卷调查!问题集中在您想参加的线下活动类型,调查结果将被用来使组织者更好地安排活动计划。本调查预计需要花费2 - 5分钟才能完成。我们将与所有参与调查的人分享任何有趣的发现。所有收集的信息将保持匿名。为了鼓励大家花费这一天中的几分钟时间,将随机抽取五个人赢得 $50 美元的亚马逊礼品卡和十五个人将赢得 Elastic Stack 特别版T恤。为了进行抽奖活动,我们会在调查结束时要求您提供电子邮件,但只会用它来让您知道如果您中奖了。
除了这个问卷调查,我们在也同时更新了 Elastic 用户组的行为准则(Code of Conduct)。参加我们的活动意味着您同意我们的准则。完整的准则可以访问:https://www.elastic.co/community/codeofconduct。所有的细节可以这个链接页找到。如果您还要其他问题,也欢迎告知我们:) — 我们会一直在这里提供帮助! :)
同学们,乡亲们:
想要今年的Elastic线下活动来到您身边么,快参加我们的问卷调查吧,如果您的城市不在下拉列表,记得添加进去,问卷调查比较简单啦,大概只需要花费您几分钟时间,快来吧:https://www.surveymonkey.com/r/elastic-china17 。
另外Elastic也在寻找各个城市的演讲者、场地赞助、协办方、志愿者。如果您有项目用到了任何Elasticsearch、Kibana、Logstash或Beats,并且有兴趣分享您的经验故事(不管是5分钟还是50分钟)请让我们知道,我们非常愿意与我们的社区一起分享您的故事。不管是哪种参与方式都欢迎,请在问卷内留下联系方式或联系我:medcl123(添加注明来意)。
我们感谢您参与本次问卷调查!问题集中在您想参加的线下活动类型,调查结果将被用来使组织者更好地安排活动计划。本调查预计需要花费2 - 5分钟才能完成。我们将与所有参与调查的人分享任何有趣的发现。所有收集的信息将保持匿名。为了鼓励大家花费这一天中的几分钟时间,将随机抽取五个人赢得 $50 美元的亚马逊礼品卡和十五个人将赢得 Elastic Stack 特别版T恤。为了进行抽奖活动,我们会在调查结束时要求您提供电子邮件,但只会用它来让您知道如果您中奖了。
除了这个问卷调查,我们在也同时更新了 Elastic 用户组的行为准则(Code of Conduct)。参加我们的活动意味着您同意我们的准则。完整的准则可以访问:https://www.elastic.co/community/codeofconduct。所有的细节可以这个链接页找到。如果您还要其他问题,也欢迎告知我们:) — 我们会一直在这里提供帮助! :)
收起阅读 »
Elastic Stack 5.2.1 发布
基于 Lucene 6.4.1,所有5.2.0 用户都应该升级到该版本,修正了很多 bug,尤其了 Lucene6.4.1修复了两个重要的内存泄漏:
- 当存储字段配置为“best_compression",我们依赖于 JVM 回收机制来释放解压缩实例(Deflater/Inflater instances)。然而这些类也行只使用了很少的 JVM 堆栈内存,却使用了大量了本地内存,所以可能 会出现在 JVM 回收解压缩实例之前而操作系统先用完了本地内存。 LUCENE-7647。
- 特定的查询可能会持有 IndexReader 的一个引用,当这些查询被缓存之后,本来应该已经被删除的段会因为这个引用而继续被 Lucene 持有。LUCENE-7657。
功能废弃:
- Geo distance range 废弃,请使用`geo_distance` bucket aggregation 或 geo_distance sort来替代。#22835。
改进增强:
- 分配解释 API(allocation explaining)在未分配主分片信息中包含陈旧的副本信息(不管是陈旧还是损坏的)#22826。
Bug 修复:
- 如果查询超时,将缓存结果置为无效。#22807。
- Reindex 接口支持来源 es 版本<2.0,当不能清除旧的 scroll 不记录日志。
- 将参数:search.highlight.term_vector_multi_value 保留为节点级别。
其他细节详见 Release notes。
Download Elasticsearch 5.2.1
X-Pack 5.2.1 release notes
Kibana 5.2.1 发布内容:
在该版本中主要包括了一些重要的 bug 修复,包含一个安全风险的 fix 以及可能造成 kibana 崩溃的问题。
在5.0早期的版本中,如果配置了 SSL,特定的请求会造成 Kibana 无法释放文件打开句柄,这会造成进程随着时间推移的崩溃。请求在发生数据之前被取消也会造成进程的崩溃。还有一个安全的风险:ESA-2017-02(Http 头信息可能泄露敏感信息的问题,注:Kibana4不受影响)。
其他详见:Release notes
Download Kibana 5.2.1
Beats,Logstash 发布内容见:
https://www.elastic.co/guide/e ... .html
https://www.elastic.co/guide/e ... .html
基于 Lucene 6.4.1,所有5.2.0 用户都应该升级到该版本,修正了很多 bug,尤其了 Lucene6.4.1修复了两个重要的内存泄漏:
- 当存储字段配置为“best_compression",我们依赖于 JVM 回收机制来释放解压缩实例(Deflater/Inflater instances)。然而这些类也行只使用了很少的 JVM 堆栈内存,却使用了大量了本地内存,所以可能 会出现在 JVM 回收解压缩实例之前而操作系统先用完了本地内存。 LUCENE-7647。
- 特定的查询可能会持有 IndexReader 的一个引用,当这些查询被缓存之后,本来应该已经被删除的段会因为这个引用而继续被 Lucene 持有。LUCENE-7657。
功能废弃:
- Geo distance range 废弃,请使用`geo_distance` bucket aggregation 或 geo_distance sort来替代。#22835。
改进增强:
- 分配解释 API(allocation explaining)在未分配主分片信息中包含陈旧的副本信息(不管是陈旧还是损坏的)#22826。
Bug 修复:
- 如果查询超时,将缓存结果置为无效。#22807。
- Reindex 接口支持来源 es 版本<2.0,当不能清除旧的 scroll 不记录日志。
- 将参数:search.highlight.term_vector_multi_value 保留为节点级别。
其他细节详见 Release notes。
Download Elasticsearch 5.2.1
X-Pack 5.2.1 release notes
Kibana 5.2.1 发布内容:
在该版本中主要包括了一些重要的 bug 修复,包含一个安全风险的 fix 以及可能造成 kibana 崩溃的问题。
在5.0早期的版本中,如果配置了 SSL,特定的请求会造成 Kibana 无法释放文件打开句柄,这会造成进程随着时间推移的崩溃。请求在发生数据之前被取消也会造成进程的崩溃。还有一个安全的风险:ESA-2017-02(Http 头信息可能泄露敏感信息的问题,注:Kibana4不受影响)。
其他详见:Release notes
Download Kibana 5.2.1
Beats,Logstash 发布内容见:
https://www.elastic.co/guide/e ... .html
https://www.elastic.co/guide/e ... .html
收起阅读 »
Elasticsearch 5.x 字段折叠的使用
在 Elasticsearch 5.x 有一个字段折叠(Field Collapsing,#22337)的功能非常有意思,在这里分享一下,
字段折叠是一个很有历史的需求了,可以看这个 issue,编号#256,最初是2010年7月提的issue,也是讨论最多的帖子之一(240+评论),熬了6年才支持的特性,你说牛不牛,哈哈。
目测该特性将于5.3发布,尝鲜地址:Elasticsearch-5.3.0-SNAPSHOT,文档地址:search-request-collapse。
So,什么是字段折叠,可以理解就是按特定字段进行合并去重,比如我们有一个菜谱搜索,我希望按菜谱的“菜系”字段进行折叠,即返回结果每个菜系都返回一个结果,也就是按菜系去重,我搜索关键字“鱼”,要去返回的结果里面各种菜系都有,有湘菜,有粤菜,有中餐,有西餐,别全是湘菜,就是这个意思,通过按特定字段折叠之后,来丰富搜索结果的多样性。
说到这里,有人肯定会想到,使用 term agg+ top hits agg 来实现啊,这种组合两种聚和的方式可以实现上面的功能,不过也有一些局限性,比如,不能分页,#4915;结果不够精确(top term+top hits,es 的聚合实现选择了牺牲精度来提高速度);数据量大的情况下,聚合比较慢,影响搜索体验。
而新的的字段折叠的方式是怎么实现的的呢,有这些要点:
- 折叠+取 inner_hits 分两阶段执行(组合聚合的方式只有一个阶段),所以 top hits 永远是精确的。
- 字段折叠只在 top hits 层执行,不需要每次都在完整的结果集上对为每个折叠主键计算实际的 doc values 值,只对 top hits 这小部分数据操作就可以,和 term agg 相比要节省很多内存。
- 因为只在 top hits 上进行折叠,所以相比组合聚合的方式,速度要快很多。
- 折叠 top docs 不需要使用全局序列(global ordinals)来转换 string,相比 agg 这也节省了很多内存。
- 分页成为可能,和常规搜索一样,具有相同的局限,先获取 from+size 的内容,再合并。
- search_after 和 scroll 暂未实现,不过具备可行性。
- 折叠只影响搜索结果,不影响聚合,搜索结果的 total 是所有的命中纪录数,去重的结果数未知(无法计算)。
下面来看看具体的例子,就知道怎么回事了,使用起来很简单。
- 先准备索引和数据,这里以菜谱为例,name:菜谱名,type 为菜系,rating 为用户的累积平均评分
DELETE recipes
PUT recipes
POST recipes/type/_mapping
{
"properties": {
"name":{
"type": "text"
},
"rating":{
"type": "float"
},"type":{
"type": "keyword"
}
}
}
POST recipes/type/
{
"name":"清蒸鱼头","rating":1,"type":"湘菜"
}
POST recipes/type/
{
"name":"剁椒鱼头","rating":2,"type":"湘菜"
}
POST recipes/type/
{
"name":"红烧鲫鱼","rating":3,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(辣)","rating":3,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(微辣)","rating":4,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(变态辣)","rating":5,"type":"湘菜"
}
POST recipes/type/
{
"name":"广式鲫鱼汤","rating":5,"type":"粤菜"
}
POST recipes/type/
{
"name":"鱼香肉丝","rating":2,"type":"川菜"
}
POST recipes/type/
{
"name":"奶油鲍鱼汤","rating":2,"type":"西菜"
}
- 现在我们看看普通的查询效果是怎么样的,搜索关键字带“鱼”的菜,返回3条数据
POST recipes/type/_search
{
"query": {"match": {
"name": "鱼"
}},"size": 3
}
全是湘菜,我的天,最近上火不想吃辣,这个第一页的结果对我来说就是垃圾,如下:{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": 0.26742277,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": 0.26742277,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHXO_OA-dG63Txsa",
"_score": 0.19100356,
"_source": {
"name": "红烧鲫鱼",
"rating": 3,
"type": "湘菜"
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHWy_OA-dG63TxsZ",
"_score": 0.19100356,
"_source": {
"name": "剁椒鱼头",
"rating": 2,
"type": "湘菜"
}
}
]
}
}
我们再看看,这次我想加个评分排序,大家都喜欢的是那些,看看有没有喜欢吃的,执行查询:POST recipes/type/_search
{
"query": {"match": {
"name": "鱼"
}},"sort": [
{
"rating": {
"order": "desc"
}
}
],"size": 3
}
结果稍微好点了,不过3个里面2个是湘菜,还是有点不合适,结果如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHX7_OA-dG63Txsc",
"_score": null,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"sort": [
4
]
}
]
}
}
现在我知道了,我要看看其他菜系,这家不是还有西餐、广东菜等各种菜系的么,来来,帮我每个菜系来一个菜看看,换 terms agg 先得到唯一的 term 的 bucket,再组合 top_hits agg,返回按评分排序的第一个 top hits,有点复杂,没关系,看下面的查询就知道了:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"sort": [
{
"rating": {
"order": "desc"
}
}
],"aggs": {
"type": {
"terms": {
"field": "type",
"size": 10
},"aggs": {
"rated": {
"top_hits": {
"sort": [{
"rating": {"order": "desc"}
}],
"size": 1
}
}
}
}
},
"size": 0,
"from": 0
}
看下面的结果,虽然 json 结构有点复杂,不过总算是我们想要的结果了,湘菜、粤菜、川菜、西菜都出来了,每样一个,不重样:{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": 0,
"hits": []
},
"aggregations": {
"type": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "湘菜",
"doc_count": 6,
"rated": {
"hits": {
"total": 6,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
}
]
}
}
},
{
"key": "川菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYr_OA-dG63Txsf",
"_score": null,
"_source": {
"name": "鱼香肉丝",
"rating": 2,
"type": "川菜"
},
"sort": [
2
]
}
]
}
}
},
{
"key": "粤菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
}
]
}
}
},
{
"key": "西菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHY3_OA-dG63Txsg",
"_score": null,
"_source": {
"name": "奶油鲍鱼汤",
"rating": 2,
"type": "西菜"
},
"sort": [
2
]
}
]
}
}
}
]
}
}
}
上面的实现方法,前面已经说了,可以做,有局限性,那看看新的字段折叠法如何做到呢,查询如下,加一个 collapse 参数,指定对那个字段去重就行了,这里当然对菜系“type”字段进行去重了:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"collapse": {
"field": "type"
},
"size": 3,
"from": 0
}
结果很理想嘛,命中结果还是熟悉的那个味道(和查询结果长的一样嘛),如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRJ_OA-dG63TxpW",
"_score": 0.018980097,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"fields": {
"type": [
"湘菜"
]
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRk_OA-dG63TxpZ",
"_score": 0.013813315,
"_source": {
"name": "鱼香肉丝",
"rating": 2,
"type": "川菜"
},
"fields": {
"type": [
"川菜"
]
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRb_OA-dG63TxpY",
"_score": 0.0125863515,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"fields": {
"type": [
"粤菜"
]
}
}
]
}
}
我再试试翻页,把 from 改一下,现在返回了3条数据,from 改成3,新的查询如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRw_OA-dG63Txpa",
"_score": 0.012546891,
"_source": {
"name": "奶油鲍鱼汤",
"rating": 2,
"type": "西菜"
},
"fields": {
"type": [
"西菜"
]
}
}
]
}
}
上面的结果只有一条了,去重之后本来就只有4条数据,上面的工作正常,每个菜系只有一个菜啊,那我不乐意了,帮我每个菜系里面多返回几条,我好选菜啊,加上参数 inner_hits 来控制返回的条数,这里返回2条,按 rating 也排个序,新的查询构造如下:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"collapse": {
"field": "type",
"inner_hits": {
"name": "top_rated",
"size": 2,
"sort": [
{
"rating": "desc"
}
]
}
},
"sort": [
{
"rating": {
"order": "desc"
}
}
],
"size": 2,
"from": 0
}
查询结果如下,完美:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"fields": {
"type": [
"湘菜"
]
},
"sort": [
5
],
"inner_hits": {
"top_rated": {
"hits": {
"total": 6,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHX7_OA-dG63Txsc",
"_score": null,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"sort": [
4
]
}
]
}
}
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"fields": {
"type": [
"粤菜"
]
},
"sort": [
5
],
"inner_hits": {
"top_rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
}
]
}
}
}
}
]
}
}
好了,字段折叠介绍就到这里。
在 Elasticsearch 5.x 有一个字段折叠(Field Collapsing,#22337)的功能非常有意思,在这里分享一下,
字段折叠是一个很有历史的需求了,可以看这个 issue,编号#256,最初是2010年7月提的issue,也是讨论最多的帖子之一(240+评论),熬了6年才支持的特性,你说牛不牛,哈哈。
目测该特性将于5.3发布,尝鲜地址:Elasticsearch-5.3.0-SNAPSHOT,文档地址:search-request-collapse。
So,什么是字段折叠,可以理解就是按特定字段进行合并去重,比如我们有一个菜谱搜索,我希望按菜谱的“菜系”字段进行折叠,即返回结果每个菜系都返回一个结果,也就是按菜系去重,我搜索关键字“鱼”,要去返回的结果里面各种菜系都有,有湘菜,有粤菜,有中餐,有西餐,别全是湘菜,就是这个意思,通过按特定字段折叠之后,来丰富搜索结果的多样性。
说到这里,有人肯定会想到,使用 term agg+ top hits agg 来实现啊,这种组合两种聚和的方式可以实现上面的功能,不过也有一些局限性,比如,不能分页,#4915;结果不够精确(top term+top hits,es 的聚合实现选择了牺牲精度来提高速度);数据量大的情况下,聚合比较慢,影响搜索体验。
而新的的字段折叠的方式是怎么实现的的呢,有这些要点:
- 折叠+取 inner_hits 分两阶段执行(组合聚合的方式只有一个阶段),所以 top hits 永远是精确的。
- 字段折叠只在 top hits 层执行,不需要每次都在完整的结果集上对为每个折叠主键计算实际的 doc values 值,只对 top hits 这小部分数据操作就可以,和 term agg 相比要节省很多内存。
- 因为只在 top hits 上进行折叠,所以相比组合聚合的方式,速度要快很多。
- 折叠 top docs 不需要使用全局序列(global ordinals)来转换 string,相比 agg 这也节省了很多内存。
- 分页成为可能,和常规搜索一样,具有相同的局限,先获取 from+size 的内容,再合并。
- search_after 和 scroll 暂未实现,不过具备可行性。
- 折叠只影响搜索结果,不影响聚合,搜索结果的 total 是所有的命中纪录数,去重的结果数未知(无法计算)。
下面来看看具体的例子,就知道怎么回事了,使用起来很简单。
- 先准备索引和数据,这里以菜谱为例,name:菜谱名,type 为菜系,rating 为用户的累积平均评分
DELETE recipes
PUT recipes
POST recipes/type/_mapping
{
"properties": {
"name":{
"type": "text"
},
"rating":{
"type": "float"
},"type":{
"type": "keyword"
}
}
}
POST recipes/type/
{
"name":"清蒸鱼头","rating":1,"type":"湘菜"
}
POST recipes/type/
{
"name":"剁椒鱼头","rating":2,"type":"湘菜"
}
POST recipes/type/
{
"name":"红烧鲫鱼","rating":3,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(辣)","rating":3,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(微辣)","rating":4,"type":"湘菜"
}
POST recipes/type/
{
"name":"鲫鱼汤(变态辣)","rating":5,"type":"湘菜"
}
POST recipes/type/
{
"name":"广式鲫鱼汤","rating":5,"type":"粤菜"
}
POST recipes/type/
{
"name":"鱼香肉丝","rating":2,"type":"川菜"
}
POST recipes/type/
{
"name":"奶油鲍鱼汤","rating":2,"type":"西菜"
}
- 现在我们看看普通的查询效果是怎么样的,搜索关键字带“鱼”的菜,返回3条数据
POST recipes/type/_search
{
"query": {"match": {
"name": "鱼"
}},"size": 3
}
全是湘菜,我的天,最近上火不想吃辣,这个第一页的结果对我来说就是垃圾,如下:{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": 0.26742277,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": 0.26742277,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHXO_OA-dG63Txsa",
"_score": 0.19100356,
"_source": {
"name": "红烧鲫鱼",
"rating": 3,
"type": "湘菜"
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHWy_OA-dG63TxsZ",
"_score": 0.19100356,
"_source": {
"name": "剁椒鱼头",
"rating": 2,
"type": "湘菜"
}
}
]
}
}
我们再看看,这次我想加个评分排序,大家都喜欢的是那些,看看有没有喜欢吃的,执行查询:POST recipes/type/_search
{
"query": {"match": {
"name": "鱼"
}},"sort": [
{
"rating": {
"order": "desc"
}
}
],"size": 3
}
结果稍微好点了,不过3个里面2个是湘菜,还是有点不合适,结果如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHX7_OA-dG63Txsc",
"_score": null,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"sort": [
4
]
}
]
}
}
现在我知道了,我要看看其他菜系,这家不是还有西餐、广东菜等各种菜系的么,来来,帮我每个菜系来一个菜看看,换 terms agg 先得到唯一的 term 的 bucket,再组合 top_hits agg,返回按评分排序的第一个 top hits,有点复杂,没关系,看下面的查询就知道了:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"sort": [
{
"rating": {
"order": "desc"
}
}
],"aggs": {
"type": {
"terms": {
"field": "type",
"size": 10
},"aggs": {
"rated": {
"top_hits": {
"sort": [{
"rating": {"order": "desc"}
}],
"size": 1
}
}
}
}
},
"size": 0,
"from": 0
}
看下面的结果,虽然 json 结构有点复杂,不过总算是我们想要的结果了,湘菜、粤菜、川菜、西菜都出来了,每样一个,不重样:{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": 0,
"hits": []
},
"aggregations": {
"type": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "湘菜",
"doc_count": 6,
"rated": {
"hits": {
"total": 6,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
}
]
}
}
},
{
"key": "川菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYr_OA-dG63Txsf",
"_score": null,
"_source": {
"name": "鱼香肉丝",
"rating": 2,
"type": "川菜"
},
"sort": [
2
]
}
]
}
}
},
{
"key": "粤菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
}
]
}
}
},
{
"key": "西菜",
"doc_count": 1,
"rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHY3_OA-dG63Txsg",
"_score": null,
"_source": {
"name": "奶油鲍鱼汤",
"rating": 2,
"type": "西菜"
},
"sort": [
2
]
}
]
}
}
}
]
}
}
}
上面的实现方法,前面已经说了,可以做,有局限性,那看看新的字段折叠法如何做到呢,查询如下,加一个 collapse 参数,指定对那个字段去重就行了,这里当然对菜系“type”字段进行去重了:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"collapse": {
"field": "type"
},
"size": 3,
"from": 0
}
结果很理想嘛,命中结果还是熟悉的那个味道(和查询结果长的一样嘛),如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRJ_OA-dG63TxpW",
"_score": 0.018980097,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"fields": {
"type": [
"湘菜"
]
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRk_OA-dG63TxpZ",
"_score": 0.013813315,
"_source": {
"name": "鱼香肉丝",
"rating": 2,
"type": "川菜"
},
"fields": {
"type": [
"川菜"
]
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRb_OA-dG63TxpY",
"_score": 0.0125863515,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"fields": {
"type": [
"粤菜"
]
}
}
]
}
}
我再试试翻页,把 from 改一下,现在返回了3条数据,from 改成3,新的查询如下:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoDNlRw_OA-dG63Txpa",
"_score": 0.012546891,
"_source": {
"name": "奶油鲍鱼汤",
"rating": 2,
"type": "西菜"
},
"fields": {
"type": [
"西菜"
]
}
}
]
}
}
上面的结果只有一条了,去重之后本来就只有4条数据,上面的工作正常,每个菜系只有一个菜啊,那我不乐意了,帮我每个菜系里面多返回几条,我好选菜啊,加上参数 inner_hits 来控制返回的条数,这里返回2条,按 rating 也排个序,新的查询构造如下:GET recipes/type/_search
{
"query": {
"match": {
"name": "鱼"
}
},
"collapse": {
"field": "type",
"inner_hits": {
"name": "top_rated",
"size": 2,
"sort": [
{
"rating": "desc"
}
]
}
},
"sort": [
{
"rating": {
"order": "desc"
}
}
],
"size": 2,
"from": 0
}
查询结果如下,完美:{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 9,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"fields": {
"type": [
"湘菜"
]
},
"sort": [
5
],
"inner_hits": {
"top_rated": {
"hits": {
"total": 6,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYF_OA-dG63Txsd",
"_score": null,
"_source": {
"name": "鲫鱼汤(变态辣)",
"rating": 5,
"type": "湘菜"
},
"sort": [
5
]
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHX7_OA-dG63Txsc",
"_score": null,
"_source": {
"name": "鲫鱼汤(微辣)",
"rating": 4,
"type": "湘菜"
},
"sort": [
4
]
}
]
}
}
}
},
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"fields": {
"type": [
"粤菜"
]
},
"sort": [
5
],
"inner_hits": {
"top_rated": {
"hits": {
"total": 1,
"max_score": null,
"hits": [
{
"_index": "recipes",
"_type": "type",
"_id": "AVoESHYW_OA-dG63Txse",
"_score": null,
"_source": {
"name": "广式鲫鱼汤",
"rating": 5,
"type": "粤菜"
},
"sort": [
5
]
}
]
}
}
}
}
]
}
}
好了,字段折叠介绍就到这里。 收起阅读 »
Elastic Stack 5.2.0 发布
(Elastic Stack 包括 Elasticsearch、Logstash、Kibana、Beats):
Elasticsearch 5.2.0 主要亮点:
- 新增数字和日期范围字段类型,非常方便对范围类型进行交并集的查询,比如你的数据是日历类型的数据,每天都有一些会议信息,会议的开始和结束时间都不同,你想看本周那天有空,可以使用 range fields 很方便的进行查询。了解更多
- 新增分片分配解释 API,会告知分片失败的具体原因,如分片损坏、磁盘写满还是配置错误,进行快速运维,以前只能根据经验到处寻找可能是什么问题,费事费力,了解更多。
- 对 Keyword 类型标准化,在5.0将 string 类型拆成了 text 和 keyword两种类型,text 支持分词,keyword 则不分词默认支持 doc values,不过有时候你还需要对 keyword 类型进行一些标准化处理,如都转成小写,现在可以使用 normalizers 参数来指定标准化的 filter。
- Terms 聚合的分区,terms 聚合默认返回10个 term,以前如果你需要返回全部的 term 列表是不可能的任务(基于内存压力),现在你可以通过将请求分区,以多次请求的方式来取回这些数据,了解更多。
- 字段折叠,在搜索时可以按某个字段的值进行折叠,在每个折叠的值内进行排序选取 topN,了解更多。
相关链接:
Download Elasticsearch 5.2.0
Elasticsearch 5.2.0 release notes
Elasticsearch 5.2.0 breaking changes
X-Pack 5.2.0 release notes
Logstash 5.2.0 主要亮点:
- 新的监控 UI,现在 X-Pack 也能监控 logstash 了,X-Pack 基础免费版就包括,如下图:
- 更多的监控 API,新增3个类型的统计信息:cgroup、持久化队列和 output新增持续时间字段。
- 离线插件管理,在很多没有公网的部署环境,都需要离线安装,以前的离线安装不够完善,尽管大部分插件没问题,但是还是存在个别插件的依赖链下载不完整的问题,为了解决这个问题,我们基本上重新设计了整个工作流程使用了新的方式来打包插件和他所有的依赖,了解更多。
相关连接:
Download Logstash 5.2.0
Logstash 5.2.0 release notes
Kibana 5.2.0 主要亮点:
- 支持 Elasticsearch Tribe 节点,在“admin”集群的基础上,引入了新的“data”集群,“data”集群可以理解成 Kibana 后面的数据来源,可以是 tribe 节点,而“admin”集群是存放 kibana 可视化信息“。kibana”索引的地方,默认 data 和 admin 集群都是在同一个集群,且不能是 tribe 节点,目前还有一些细节正在处理,如 console 还不能很好的工作。
- 增加新的可视化类型:热点图(heatmap),可以很方便的按区间和按时间来显示数据的范围分布,如下图:
- 开始进行国际化的支持,感谢 IBM 团队的努力,目前已经提供了基础的框架支持,虽然是万里长征的第一大步,但也是非常激动人心的。
- 地图服务的改进,Elastic 自己的 Tile 地图服务已经上线几个月了,我们现在能提供10个级别的缩放了,X-Pack 基础用户可以达到12个级别的缩放,并且我们正在尝试18个级别的缩放,并且从5.2开始,我们能让这些级别动态调整,不用发布新的 Kibana。
- 监控容器中的 Elasticsearch,现在我们可以监控容器里面的 Elasticsearch 实例的运行情况了,CPU 利用率、GC、堆栈使用情况等,如下图:
相关连接:
Download Kibana 5.2.0
Kibana 5.2.0 release notes
Beats 5.2.0 主要亮点:
- 新增 Beat:Heartbeat,一个新的正式的官方beat 成员,用于可用性监控,和所有的 beat 一样,轻量级,Heartbeat 可以用于很多场景,比如安全,你不希望暴露某个端口时,使用 Heartbeat,当你发现该端口对外开启了,就可以触发通知,或者服务/网站可用性检测,服务down 了可以及时感知,目前支持:ICMP、TCP 和 HTTP 类型的监控,目前 Heartbeat还处于 beat 阶段,暂不推荐用于生产环境。
- Metricbeat 可跟踪网络连接,从5.2开始,Metricbeat 导出了 linux 系统的应用程序的网络连接信息,每个进程打开的 tcp 套接字,本地及远程的 ip 都包含在内,基于它,你可以进行如下的图分析:
- 收集Prometheus导出的指标,从5.2开始,监控系统普罗米修斯的收集模块导出的数据可以提供给 Metricbeat 然后索引进 Elasticsearch。
相关连接:
Download Beats 5.2.0
Beats 5.2.0 release notes
Elastic Stack 下载链接:https://www.elastic.co/downloads
Bug 反馈:http://github.com/elastic
以后有版本的更新消息都会在这里发布一份中文版,欢迎大家关注。
(Elastic Stack 包括 Elasticsearch、Logstash、Kibana、Beats):
Elasticsearch 5.2.0 主要亮点:
- 新增数字和日期范围字段类型,非常方便对范围类型进行交并集的查询,比如你的数据是日历类型的数据,每天都有一些会议信息,会议的开始和结束时间都不同,你想看本周那天有空,可以使用 range fields 很方便的进行查询。了解更多
- 新增分片分配解释 API,会告知分片失败的具体原因,如分片损坏、磁盘写满还是配置错误,进行快速运维,以前只能根据经验到处寻找可能是什么问题,费事费力,了解更多。
- 对 Keyword 类型标准化,在5.0将 string 类型拆成了 text 和 keyword两种类型,text 支持分词,keyword 则不分词默认支持 doc values,不过有时候你还需要对 keyword 类型进行一些标准化处理,如都转成小写,现在可以使用 normalizers 参数来指定标准化的 filter。
- Terms 聚合的分区,terms 聚合默认返回10个 term,以前如果你需要返回全部的 term 列表是不可能的任务(基于内存压力),现在你可以通过将请求分区,以多次请求的方式来取回这些数据,了解更多。
- 字段折叠,在搜索时可以按某个字段的值进行折叠,在每个折叠的值内进行排序选取 topN,了解更多。
相关链接:
Download Elasticsearch 5.2.0
Elasticsearch 5.2.0 release notes
Elasticsearch 5.2.0 breaking changes
X-Pack 5.2.0 release notes
Logstash 5.2.0 主要亮点:
- 新的监控 UI,现在 X-Pack 也能监控 logstash 了,X-Pack 基础免费版就包括,如下图:
- 更多的监控 API,新增3个类型的统计信息:cgroup、持久化队列和 output新增持续时间字段。
- 离线插件管理,在很多没有公网的部署环境,都需要离线安装,以前的离线安装不够完善,尽管大部分插件没问题,但是还是存在个别插件的依赖链下载不完整的问题,为了解决这个问题,我们基本上重新设计了整个工作流程使用了新的方式来打包插件和他所有的依赖,了解更多。
相关连接:
Download Logstash 5.2.0
Logstash 5.2.0 release notes
Kibana 5.2.0 主要亮点:
- 支持 Elasticsearch Tribe 节点,在“admin”集群的基础上,引入了新的“data”集群,“data”集群可以理解成 Kibana 后面的数据来源,可以是 tribe 节点,而“admin”集群是存放 kibana 可视化信息“。kibana”索引的地方,默认 data 和 admin 集群都是在同一个集群,且不能是 tribe 节点,目前还有一些细节正在处理,如 console 还不能很好的工作。
- 增加新的可视化类型:热点图(heatmap),可以很方便的按区间和按时间来显示数据的范围分布,如下图:
- 开始进行国际化的支持,感谢 IBM 团队的努力,目前已经提供了基础的框架支持,虽然是万里长征的第一大步,但也是非常激动人心的。
- 地图服务的改进,Elastic 自己的 Tile 地图服务已经上线几个月了,我们现在能提供10个级别的缩放了,X-Pack 基础用户可以达到12个级别的缩放,并且我们正在尝试18个级别的缩放,并且从5.2开始,我们能让这些级别动态调整,不用发布新的 Kibana。
- 监控容器中的 Elasticsearch,现在我们可以监控容器里面的 Elasticsearch 实例的运行情况了,CPU 利用率、GC、堆栈使用情况等,如下图:
相关连接:
Download Kibana 5.2.0
Kibana 5.2.0 release notes
Beats 5.2.0 主要亮点:
- 新增 Beat:Heartbeat,一个新的正式的官方beat 成员,用于可用性监控,和所有的 beat 一样,轻量级,Heartbeat 可以用于很多场景,比如安全,你不希望暴露某个端口时,使用 Heartbeat,当你发现该端口对外开启了,就可以触发通知,或者服务/网站可用性检测,服务down 了可以及时感知,目前支持:ICMP、TCP 和 HTTP 类型的监控,目前 Heartbeat还处于 beat 阶段,暂不推荐用于生产环境。
- Metricbeat 可跟踪网络连接,从5.2开始,Metricbeat 导出了 linux 系统的应用程序的网络连接信息,每个进程打开的 tcp 套接字,本地及远程的 ip 都包含在内,基于它,你可以进行如下的图分析:
- 收集Prometheus导出的指标,从5.2开始,监控系统普罗米修斯的收集模块导出的数据可以提供给 Metricbeat 然后索引进 Elasticsearch。
相关连接:
Download Beats 5.2.0
Beats 5.2.0 release notes
Elastic Stack 下载链接:https://www.elastic.co/downloads
Bug 反馈:http://github.com/elastic
以后有版本的更新消息都会在这里发布一份中文版,欢迎大家关注。 收起阅读 »
发现的一个不错的elasticsearch 官方文档的翻译文档
Elasticsearch 2.3.3 JAVA api说明文档
Elasticsearch 2.3.3 JAVA api说明文档 收起阅读 »
Elasticsearch 安全加固 101
最近 MongoDB 的安全事件闹得沸沸扬扬,应该不少人都听说了吧,事情大概是,因为 MongoDB 默认的安全设置造成了数据外泄并且被黑客勒索才能找回数据,想了解的,这里有几个链接:
http://www.jianshu.com/p/48d17a69e190
http://mt.sohu.com/20170107/n478047698.shtml
http://bbs.tianya.cn/post-itinfo-503786-1.shtml
安全从来不是等到出事才要注意的事情,可以说安全是第一重要的事情,不管你是公司的CTO、技术总监、运维总监、架构师还是一线工程师,都应该有安全意识,好了,废话不多说了,Elasticsearch 的用户现在越来越多了,有些已经成为公司的基础服务,所以数据的安全非常重要,今天主要给大家介绍 Elasticsearch 围绕安全方面的的几点使用事项:
下载安装
请使用正规渠道下载 Elasticsearch,首选官方网站,下载完成,记得要验证下载文件的 sha1值和官网下载的提供的sha1值进行对比,避免下载过程中被人拦截破坏文件,甚至注入恶意代码。
不要随便安装第三方的插件,插件有可能引入安全漏洞甚至本身自带后门,需谨慎使用。
链接君:https://www.elastic.co/downloads
使用最新的 Elasticsearch
请关注 Elastic 网站,及时更新升级 Elasticsearch 的最新版本,Elasticsearch 每次版本发布都会优化和改进一部分功能,尤其是安全漏洞的补丁,仔细阅读 Elasticsearch 的更新记录,Elasticsearch 的版本遵照 语义化版本 ,所以小版本间应该是能够无缝升级的,建议及时本地测试和线上更新,升级前,记得 snapshot 做好备份。
链接君:https://www.elastic.co/downloads
修改默认的 Elasticsearch 集群名称
Elasticsearch 默认的集群名称是 elasticsearch,请在生成环境上一定要修改成其他的名称,并且不同的环境和不同的集群要保证不相同,监控集群节点情况,如果有未知节点加入,一定要及时预警。
文档君:https://www.elastic.co/guide/e ... .name
不要暴露 Elasticsearch 在公网上
Elasticsearch 默认端口是9200,绑定的是本机127.0.0.1的这个 ip,这个默认参数其实很安全,但是有很多人想要绑定其他的 lan 口或者公网的 ip,可以修改相应参数,记住,修改有风险,如果确实需要将 Elasticsearch 暴露在公网环境,请修改特定的端口绑定IP,不要直接修改参数: network.host,而是要分别修改:http.port 来绑定 HTTP 协议9200 端口的 IP(RESTful 接口调用),修改:transport.tcp.port 对应的 TCP 9300 端口的 IP(集群内通信),如果你不需要 http 端口,你干脆禁用掉,另外还需要在 Elasticsearch 之上加上成熟的安全防护措施(注意是成熟的!),在这里提供几种方案:
- 9200的 HTTP 接口之上加上 Nginx 来提供 Http Basic-Auth 的基本的身份认证,辅助 SSL 证书进行传输层的加密,Nginx 进一步限制可接受 Verb 请求类型及可被操作的索引前缀。
- 使用 Elastic 的 X-Pack 插件,同样提供了 Http Basic-Auth 和 SSL 传输层的加密,X-Pack 还能提供内外 Elasticsearch 集群节点间的流量加密,避免旁路攻击。
文档君:https://www.elastic.co/guide/e ... tings
禁用批量删除索引
Elasticsearch 支持通过_all(全部)和通配符(*)来批量删除索引,在生产环境,这个有点危险,你可以通过设置: action.destructive_requires_name: true 来禁用它。
安全使用动态脚本
Elasticsearch 的脚本很方便的可以对数据进行操作,不过如果你暂时没有用上,还请禁用它(Elasticsearch 在1.2.x 以后已经默认禁用了),如果你已经在使用动态脚本,比如 Groovy,它不是沙盒机制的脚本引擎,启用 inline 或 store 类型的groovy 有安全风险,请限制脚本的接触方,比如通过模板的方式来限制脚本的调用,只需要执行特定预先定义好的脚本,对调用参数进行过滤和参数值的检测,做好验证,同时各种日志都必须要保留好,方便进行日志分析,异常的调用和请求一定要有发现和预警机制。
Elasticsearch 默认启用了 Java Security Manager ,但还请正确配置其白名单。
使用 Groovy 或者JavaScript 等脚本的用户,尽快迁移到 Painless 脚本,Painless 更快更安全。
文档君:https://www.elastic.co/guide/e ... .html
给 Elasticsearch 服务器加固
服务器加固是一个必备流程,不管上面运行的是什么服务;
首先,请开启防火墙,请设置防火墙规则,只开启必备的端口,完成之后,使用扫描工具扫描服务器,检查端口开发情况;
如果可能,不要用密码的方法来远程登录服务器,使用公私钥的方式来 ssh 登录服务器,如果只能使用密码,请妥善保管好你的用户名和密码,禁用 root 用户,不用使用弱密码。
关注 Java 最新的漏洞,使用安全的 JVM 运行时。
服务器及时更新最新的软件,使用安全的 repo 软件源,绑定软件源的 host和 ip,避免 dns 污染造成的攻击,关注服务器软件漏洞,及时打上补丁。
收集系统日志和安装相应的入侵检测软件,及时发现服务器是否有异常行为。
不要以 root 身份运行 Elasticsearch
如果你的运维人员打算以 root 身份来运行某个服务,这个运维人员一定是一个不合格的运维人员,记住一定不要以 root 身份来运行 Elasticsearch,另外,要不和其他的服务公用相同的用户,然后还要保证该用户的权限要最小化。
范例君:
sudo -u es-user ES_JAVA_OPTS="-Xms1024m -Xmx1024m" /opt/elasticsearch/bin/elasticsearc
正确设置 Elasticsearch 的数据目录
请确保 Elasticsearch 的目录分配了合理的读写权限,避免使用共享文件系统,确保只有 elasticsearch 的启动用户才能访问,同理,日志目录也一样需要正确配置,避免泄露敏感信息。
文档君:https://www.elastic.co/guide/e ... tings
定期对 Elasticsearch 进行备份
使用 Elasticsearch 提供的备份还原机制,定期对 Elasticsearch 的数据进行快照备份,以备不时之需。
文档君:https://www.elastic.co/guide/e ... .html
加上监控和预警
Elasticsearch 提供了很好的默认参数,对参数方面还做了正确性检测,bootstrap 启动检查,不准确的参数,直接不允许 Elasticsearch 启动,以至于有很多人抱怨,怎么现在部署到线上默认就需要做这么多设置才能使用呢,是的,以前启动就默认绑定了所有的网卡,集群见自动发现和相连,现在需要手动绑定局域网网卡,默认只是绑定的本机127.0.0.1的 ip,对上线来说麻烦了一点,做了这些检查也就是为了保证数据安全,以前很多的公网都能直接访问的 Elasticsearch 实例,都是因为默认设置就绑定了公网 ip,但是这些还不够,作为用户,你还需要收集各种日志和系统监控信息,及时主动掌握服务健康和资源使用情况,发现异常情况要及时处理,这里提供一些方案:
- 使用开源的 Elastic Stack 收集这些日志,可以使用 Filebeat 收集日志,Metricbeat收集系统监控信息,存进 Elasticsearch,一旦发现异常的波动,使用 Watcher 来进行预警,通过邮件或者 webhook 调用短信、微信或者电话。
- 使用其他厂商的安全监控产品。
- 使用托管的 Elasticsearch 云的产品,如 Elastic Cloud等等。
是的,把安全这个事情考虑进去之后,很多事情都要比没考虑要变得更加复杂和麻烦,千里之堤毁于蚁穴,一个不起眼的忽视就有可能造成全部数据的丢失和泄露,出来混迟早是要还的,安全问题千万不能忽视。
以上几点建议举例针对 linux 平台,其他平台思路基本上一样,仅供参考,安全是一个包含很多方方面面的学科,抛砖引玉,希望大家有用。
最后,Elastic 非常关心我们的产品安全,如果您发现有任何安全方面的问题,还请在这里上报:
https://www.elastic.co/community/security
企业用户需要 X-Pack 及 Elastic 官方技术支持,请访问下面的链接:
https://www.elastic.co/cn/contact
最近 MongoDB 的安全事件闹得沸沸扬扬,应该不少人都听说了吧,事情大概是,因为 MongoDB 默认的安全设置造成了数据外泄并且被黑客勒索才能找回数据,想了解的,这里有几个链接:
http://www.jianshu.com/p/48d17a69e190
http://mt.sohu.com/20170107/n478047698.shtml
http://bbs.tianya.cn/post-itinfo-503786-1.shtml
安全从来不是等到出事才要注意的事情,可以说安全是第一重要的事情,不管你是公司的CTO、技术总监、运维总监、架构师还是一线工程师,都应该有安全意识,好了,废话不多说了,Elasticsearch 的用户现在越来越多了,有些已经成为公司的基础服务,所以数据的安全非常重要,今天主要给大家介绍 Elasticsearch 围绕安全方面的的几点使用事项:
下载安装
请使用正规渠道下载 Elasticsearch,首选官方网站,下载完成,记得要验证下载文件的 sha1值和官网下载的提供的sha1值进行对比,避免下载过程中被人拦截破坏文件,甚至注入恶意代码。
不要随便安装第三方的插件,插件有可能引入安全漏洞甚至本身自带后门,需谨慎使用。
链接君:https://www.elastic.co/downloads
使用最新的 Elasticsearch
请关注 Elastic 网站,及时更新升级 Elasticsearch 的最新版本,Elasticsearch 每次版本发布都会优化和改进一部分功能,尤其是安全漏洞的补丁,仔细阅读 Elasticsearch 的更新记录,Elasticsearch 的版本遵照 语义化版本 ,所以小版本间应该是能够无缝升级的,建议及时本地测试和线上更新,升级前,记得 snapshot 做好备份。
链接君:https://www.elastic.co/downloads
修改默认的 Elasticsearch 集群名称
Elasticsearch 默认的集群名称是 elasticsearch,请在生成环境上一定要修改成其他的名称,并且不同的环境和不同的集群要保证不相同,监控集群节点情况,如果有未知节点加入,一定要及时预警。
文档君:https://www.elastic.co/guide/e ... .name
不要暴露 Elasticsearch 在公网上
Elasticsearch 默认端口是9200,绑定的是本机127.0.0.1的这个 ip,这个默认参数其实很安全,但是有很多人想要绑定其他的 lan 口或者公网的 ip,可以修改相应参数,记住,修改有风险,如果确实需要将 Elasticsearch 暴露在公网环境,请修改特定的端口绑定IP,不要直接修改参数: network.host,而是要分别修改:http.port 来绑定 HTTP 协议9200 端口的 IP(RESTful 接口调用),修改:transport.tcp.port 对应的 TCP 9300 端口的 IP(集群内通信),如果你不需要 http 端口,你干脆禁用掉,另外还需要在 Elasticsearch 之上加上成熟的安全防护措施(注意是成熟的!),在这里提供几种方案:
- 9200的 HTTP 接口之上加上 Nginx 来提供 Http Basic-Auth 的基本的身份认证,辅助 SSL 证书进行传输层的加密,Nginx 进一步限制可接受 Verb 请求类型及可被操作的索引前缀。
- 使用 Elastic 的 X-Pack 插件,同样提供了 Http Basic-Auth 和 SSL 传输层的加密,X-Pack 还能提供内外 Elasticsearch 集群节点间的流量加密,避免旁路攻击。
文档君:https://www.elastic.co/guide/e ... tings
禁用批量删除索引
Elasticsearch 支持通过_all(全部)和通配符(*)来批量删除索引,在生产环境,这个有点危险,你可以通过设置: action.destructive_requires_name: true 来禁用它。
安全使用动态脚本
Elasticsearch 的脚本很方便的可以对数据进行操作,不过如果你暂时没有用上,还请禁用它(Elasticsearch 在1.2.x 以后已经默认禁用了),如果你已经在使用动态脚本,比如 Groovy,它不是沙盒机制的脚本引擎,启用 inline 或 store 类型的groovy 有安全风险,请限制脚本的接触方,比如通过模板的方式来限制脚本的调用,只需要执行特定预先定义好的脚本,对调用参数进行过滤和参数值的检测,做好验证,同时各种日志都必须要保留好,方便进行日志分析,异常的调用和请求一定要有发现和预警机制。
Elasticsearch 默认启用了 Java Security Manager ,但还请正确配置其白名单。
使用 Groovy 或者JavaScript 等脚本的用户,尽快迁移到 Painless 脚本,Painless 更快更安全。
文档君:https://www.elastic.co/guide/e ... .html
给 Elasticsearch 服务器加固
服务器加固是一个必备流程,不管上面运行的是什么服务;
首先,请开启防火墙,请设置防火墙规则,只开启必备的端口,完成之后,使用扫描工具扫描服务器,检查端口开发情况;
如果可能,不要用密码的方法来远程登录服务器,使用公私钥的方式来 ssh 登录服务器,如果只能使用密码,请妥善保管好你的用户名和密码,禁用 root 用户,不用使用弱密码。
关注 Java 最新的漏洞,使用安全的 JVM 运行时。
服务器及时更新最新的软件,使用安全的 repo 软件源,绑定软件源的 host和 ip,避免 dns 污染造成的攻击,关注服务器软件漏洞,及时打上补丁。
收集系统日志和安装相应的入侵检测软件,及时发现服务器是否有异常行为。
不要以 root 身份运行 Elasticsearch
如果你的运维人员打算以 root 身份来运行某个服务,这个运维人员一定是一个不合格的运维人员,记住一定不要以 root 身份来运行 Elasticsearch,另外,要不和其他的服务公用相同的用户,然后还要保证该用户的权限要最小化。
范例君:
sudo -u es-user ES_JAVA_OPTS="-Xms1024m -Xmx1024m" /opt/elasticsearch/bin/elasticsearc
正确设置 Elasticsearch 的数据目录
请确保 Elasticsearch 的目录分配了合理的读写权限,避免使用共享文件系统,确保只有 elasticsearch 的启动用户才能访问,同理,日志目录也一样需要正确配置,避免泄露敏感信息。
文档君:https://www.elastic.co/guide/e ... tings
定期对 Elasticsearch 进行备份
使用 Elasticsearch 提供的备份还原机制,定期对 Elasticsearch 的数据进行快照备份,以备不时之需。
文档君:https://www.elastic.co/guide/e ... .html
加上监控和预警
Elasticsearch 提供了很好的默认参数,对参数方面还做了正确性检测,bootstrap 启动检查,不准确的参数,直接不允许 Elasticsearch 启动,以至于有很多人抱怨,怎么现在部署到线上默认就需要做这么多设置才能使用呢,是的,以前启动就默认绑定了所有的网卡,集群见自动发现和相连,现在需要手动绑定局域网网卡,默认只是绑定的本机127.0.0.1的 ip,对上线来说麻烦了一点,做了这些检查也就是为了保证数据安全,以前很多的公网都能直接访问的 Elasticsearch 实例,都是因为默认设置就绑定了公网 ip,但是这些还不够,作为用户,你还需要收集各种日志和系统监控信息,及时主动掌握服务健康和资源使用情况,发现异常情况要及时处理,这里提供一些方案:
- 使用开源的 Elastic Stack 收集这些日志,可以使用 Filebeat 收集日志,Metricbeat收集系统监控信息,存进 Elasticsearch,一旦发现异常的波动,使用 Watcher 来进行预警,通过邮件或者 webhook 调用短信、微信或者电话。
- 使用其他厂商的安全监控产品。
- 使用托管的 Elasticsearch 云的产品,如 Elastic Cloud等等。
是的,把安全这个事情考虑进去之后,很多事情都要比没考虑要变得更加复杂和麻烦,千里之堤毁于蚁穴,一个不起眼的忽视就有可能造成全部数据的丢失和泄露,出来混迟早是要还的,安全问题千万不能忽视。
以上几点建议举例针对 linux 平台,其他平台思路基本上一样,仅供参考,安全是一个包含很多方方面面的学科,抛砖引玉,希望大家有用。
最后,Elastic 非常关心我们的产品安全,如果您发现有任何安全方面的问题,还请在这里上报:
https://www.elastic.co/community/security
企业用户需要 X-Pack 及 Elastic 官方技术支持,请访问下面的链接:
https://www.elastic.co/cn/contact 收起阅读 »
招聘
技术总监1名:
1、 日志分析产品研发经验优先,8年以上工作经验
2、 精通elasticsearch、Redis、MongoDB、Neo4J等NoSQL系统,熟悉elasticsearch集群管理和性能调优,有在实际项目中使用搜索引擎工具、内存数据库、文档数据库、图数据库的经验
3、 熟悉Hadoop、Spark、Spark Streaming、Storm、HBase、ZooKeeper等大数据生态系统组件;熟悉Kafka、ZeroMQ等消息系统组件
4、 精通日志处理工具集ELK,flume,heka等
5、 熟悉Linux环境,熟练使用Python和Shell进行脚本编程
6、 精通JAVA语言,熟悉主流开源框架Struts2、Spring、Hibernate,SpringMVC的运行机制及使用。熟练掌握和使用J2EE开发中常见面向对象设计模式(Singleton,Proxy,Factory等)
7、 有系统运维管理平台项目开发及管理经验优先
8、 提供优厚待遇及期权,薪水50万以上
9、 金融行业背景优先考虑
技术专家2名:
1、熟悉Hadoop生态圈相关技术,如Hbase,Hive,Zookeeper,flume,kafka,storm
2、了解spark运行机制,研读spark部分内核源码;了解ELK日志分析平台(elasticsearch、logstash、kibana)使用
3、 熟悉Linux系统的基本操作和集群环境的搭建和部署
4、 熟悉JavaEE的开发,掌握JavaEE流行框架的使用,如:Struts2、Spring、Hibernate、SpringMVC等
5、熟悉主流数据库Oracle、DB2、MySQL、Mariadb的使用及性能优化;会使用NoSql数据库(如redis)解决开发过程中遇到的业务问题
6、提供优厚待遇和期权,薪水30万以上
7、金融行业优先考虑
如有意向请联系郝女士 联系电话:13520834022 邮箱:haojinjin16@163.com
招聘单位:洋大数据信息技术(北京)有限公司
技术总监1名:
1、 日志分析产品研发经验优先,8年以上工作经验
2、 精通elasticsearch、Redis、MongoDB、Neo4J等NoSQL系统,熟悉elasticsearch集群管理和性能调优,有在实际项目中使用搜索引擎工具、内存数据库、文档数据库、图数据库的经验
3、 熟悉Hadoop、Spark、Spark Streaming、Storm、HBase、ZooKeeper等大数据生态系统组件;熟悉Kafka、ZeroMQ等消息系统组件
4、 精通日志处理工具集ELK,flume,heka等
5、 熟悉Linux环境,熟练使用Python和Shell进行脚本编程
6、 精通JAVA语言,熟悉主流开源框架Struts2、Spring、Hibernate,SpringMVC的运行机制及使用。熟练掌握和使用J2EE开发中常见面向对象设计模式(Singleton,Proxy,Factory等)
7、 有系统运维管理平台项目开发及管理经验优先
8、 提供优厚待遇及期权,薪水50万以上
9、 金融行业背景优先考虑
技术专家2名:
1、熟悉Hadoop生态圈相关技术,如Hbase,Hive,Zookeeper,flume,kafka,storm
2、了解spark运行机制,研读spark部分内核源码;了解ELK日志分析平台(elasticsearch、logstash、kibana)使用
3、 熟悉Linux系统的基本操作和集群环境的搭建和部署
4、 熟悉JavaEE的开发,掌握JavaEE流行框架的使用,如:Struts2、Spring、Hibernate、SpringMVC等
5、熟悉主流数据库Oracle、DB2、MySQL、Mariadb的使用及性能优化;会使用NoSql数据库(如redis)解决开发过程中遇到的业务问题
6、提供优厚待遇和期权,薪水30万以上
7、金融行业优先考虑
如有意向请联系郝女士 联系电话:13520834022 邮箱:haojinjin16@163.com
招聘单位:洋大数据信息技术(北京)有限公司 收起阅读 »