傅希鸣:四天搞懂elasticsearch原理-1
傅希鸣:四天搞懂elasticsearch原理-2
傅希鸣:四天搞懂elasticsearch原理-3
傅希鸣:四天搞懂elasticsearch原理-4
B站大佬:鸣哥东山再起
基于Apache Lucene的开源分布式搜索引擎
应用场景:
集群(cluster)
节点(node)
索引(index)
文档(document)
分片(shard)
分片越多搜索越慢
分段(segment)
分段不会被修改
一个索引的所有分片会自动均匀分布在所有节点中。
加入新节点后,原集群节点的分片,会部分迁移到新节点。
设置分片数量稍微大于节点数量,有利于横向扩容时,分片蔓延到所有新节点(每个节点都有分片是最理想状态)。
主分片和所有副本分片都就绪时,索引的健康状态是绿色的。
挂掉了n个节点,如果副本分片是n,那么剩余的副本分片将自动提升为主分片。
然后所有的主分片能够完整的索引,但是副本分片缺失,此时健康状态是
黄色
。如果挂点n+1个节点,主分片将缺失,健康状态是红色。
根据实际情况设置副本数量(副本太多会影响性能)。
通常同时挂掉两个的概率不高,1个副本可以满足常规容灾要求。
将文档写入某个索引
文档被随机到一个主分片
从主分片同步到副本分片
返回成功的结果
副本分片越多,索引数据越慢,因为要所有副本分片都完成才算完成
查询数据
节点转发请求到本节点的一个分片,到其他节点的另一个分片
所有分片都返回搜索结果到发起分片
发起节点返回搜索结果到请求方
不同节点上的主分片+副本分片的总数越多,请求被分摊得越多,并发搜索性能越好
单如果节点数很少,分片都集中到少数节点上,搜索速度会变慢,因为增加了开销,实际没有分摊负载
单个搜索无法通过分片加速
n个分段构成一个分片,分段大小不固定
写入:
只能写打开的分段(为了避免冲突合并)
删除是假删除,也是往打开的分段写 (类似kafka分区只能追加写)
分段大小超过一定阈值,会触发分段合并
小分段合并成打分段,为了查询加速,单合并过程是先创建一个大分段,把俩小的放进去,再删除俩小的。这个过程会耗费大量资源(硬盘,内存,CPU)。
读取:
只能读关闭的分段(所以叫做准实时)
对一个分片查询,会等它所有的分段结果,所以分段越多,查询越慢
刷新时,会关闭一批分段,这时候数据才能被查到
刷新频率太快会导致分段碎片多
刷新频率慢会导致读写实时性低
一切都是为了更快:
弱化关系、弱化一致性,都是为了速度
想读的快,就要牺牲写速度,反之亦然
- Jvm heap每1G不超过20分片(机器8G内存,那么分片不超过160个,越少越好)
- 每个分片大小在20G—40G
- https://www.elastic.co/cn/blog/how-many-shards-should-i-have-in-my-elsaticsearch-cluster
类似数据的字段类型定义,你定义了一个字段,就要指定一个类型
如果不指定字段类型,es在插入第一条数据时,会自动创建一个index,同时会帮你创建映射
字段如果和定义的类型不匹配,会插入失败
最好提前定义好映射而不是依赖自动
字段的mapping配置中,除了指定类型,还可以指定参数
通过模板(template)创建索引
- 字段
- 核心类型
- text和keyword,都是字符串,keyword不被分析(不被分析:不会全文检索,会精确匹配,like =)
- 数字类型:long,integer,short byte,double,float,half_float,scaled_float
- 日期类型:date
- 布尔类型:boolean
- 二进制类型:binary
- 范围类型:integer_range,float_range,long_range,double_range,date_range
- 复合类型
- 对象类型:通常的一个json会被扁平化
- 嵌套类型:json字段之间的关系会被保留
- 地理类型
- 地理点和地理区域
- 特殊类型
- ip,completion,token_count,murmur3,percolator,join
- 数组
- 单个字段的列表形式
- 数组只要一个元素命中即整条doc命中
- 数组只要一个元素命中即整条doc命中
- 多元字段
- 内置的一些特殊字段,以下户线开头
- _index, _id, _type(7.0版本后废弃), _uid
- _source, _size, _all, _field_names
- _routing:指定文档在哪个分片
- _meta
如果事先没有创建index,或者是索引了新字段,字段mapping将会被动态设置
Es会根据json字段自动判断类型,甚至能够发现string里面填写的 是日期、数值还是文本来设置字段
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/dynamic-mapping.html
curl -X PUT "localhost:9200/data/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{ "count": 5 }
'
Creates the data
index, the _doc
mapping type, and a field called count
with datatype long
.
可以配置映射模板,自定义类型识别
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/dynamic-templates.html
"dynamic_templates": [
{
"my_template_name": { 1
... match conditions ... 2
"mapping": { ... } 3
}
},
...
]
1.The template name can be any string value. 动态模板名称
2.The match conditions can include any of : match_mapping_type
, match
, match_pattern
, unmatch
, path_match
, path_unmatch
.匹配他条件
3.The mapping that the matched field should use. 被匹配的字段使用的映射类型
curl -X PUT "localhost:9200/my_index?pretty" -H 'Content-Type: application/json' -d'
{
"mappings": {
"_doc": {
"dynamic_templates": [
{
"integers": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
},
{
"strings": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}
}
'
curl -X PUT "localhost:9200/my_index/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
"my_integer": 5,
"my_string": "Some string"
}
'
The my_integer
field is mapped as an integer
.
The my_string
field is mapped as a text
, with a keyword
multi field.
其他例子参考:https://www.elastic.co/guide/en/elasticsearch/reference/6.8/dynamic-templates.html
索引模板,通过索引命中模板,模板中设置字段类型
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-templates.html
示例
注意
关键概念,先说大概。
定义:解析、转变、分解文本、使得搜索更加相关
分析包括三个步骤
如图:大小变小写---->文本变单词------>冠词去掉,复数变单词
前面提过segment创建之后不能修改
因此文档更新实际上是建了一个新的然后删除旧的
其实删除也不是真删除,而是加上一条删除标记
文档更新包括:检索文档(按id)、处理文档、重新索引文档
有三种方式:整条更新、字段合并、若存在则放弃更新
可以使用自动生成id来插入新文档,这样可以节省检索文档所耗费的资源,加快索引速度
es显然是分布式的,那么就会有并发问题
在一个更新获取原文档进行修改期间,可能有另一个更新也在修改这篇文档,那么第一个更新就丢失了
es用文档版本号来解决这个问题(类似乐观锁)
可以通过id删除单个,也可以条件批量删除
删除文档拖慢查询和进一步的索引
删除索引是很快的,因为是直接移除索引相关的分片文件
删除是不可恢复的,在生产环境也没有权限控制,一定要小心操作(7.0+可控制权限)
DELETE my_index
小心!! DELETE_all会直接瞬间清空所有索引!!
出了删除,还有一个更安全的操作,就是关闭索引
索引关闭以后,不能读取和写入,直到再次打开
curl -X POST "localhost:9200/my_index/_close?pretty"
curl -X POST "localhost:9200/my_index/_open?pretty"
关闭后的索引只占磁盘,非常cheap,因此我们通常会关闭索引而不是 删除索引
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/indices-open-close.html
扩展包功能
curl -X POST "localhost:9200/my_index/_freeze?pretty"
curl -X POST "localhost:9200/my_index/_unfreeze?pretty"
介于打开和关闭之间
腾讯云es做限制,需要特殊参数才能搜 GET /my_index/_search/?ignore_throttled=false
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/freeze-index-api.html
curl -X POST "localhost:9200/_reindex?pretty" -H 'Content-Type: application/json' -d'
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter"
}
}
'
注意:
reindex操作不会复制索引的配置,需要提前配置,或者配置template
reindex之前最好先把目标配置的副本数减为0,并关闭刷新,加快写入
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/docs-reindex.html
ILM(index lifycycle managment (ILM) APIs)
策略:指定索引生命周期的四种步骤:hot warm cold delete
{
"policy":{
"phases":{
"hot":{
"actions":{
//滚动存储 设置阈值 50G 一天 滚动创建一个新的index
"rollover":{
"max_size":"50GB",
"max_age":"1d"
}
}
},
"warm":{
"min_age":"1d",
"actions":{
"readonly":{},
//压缩分段
"forcemerge":{
"max_num_segments":1
},
//缩减分片
"shrink":{
"number_of_shards":1
},
//分片节点感知
"allocate":{
"include":{
"box_type":"hot,warm"
}
}
}
},
"cold":{
"min_age":"3d",
"actions":{
//索引关闭
"freeze":{},
"allocate":{
"include":{
"box_type":"cold"
}
}
}
},
"delete":{
"min_age":"1d",
"actions":{
"delete":{}
}
}
}
}
}
rollover 滚动存储
shrink 缩减分片
allocate 分片节点感知
forcemerge 压缩分段
freeze 把索引 关闭
Delete 彻底删除
分为查询上下文(query context)和过滤上下文(filter context)
过滤器不计算相关性
,只关心是否命中条件curl -X GET "localhost:9200/_search?pretty" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": [
{ "match": { "title": "Search" }},
{ "match": { "content": "Elasticsearch" }}
],
"filter": [
{ "term": { "status": "published" }},
{ "range": { "publish_date": { "gte": "2015-01-01" }}}
]
}
}
}
'
The
query
parameter indicates query context.The
bool
and twomatch
clauses are used in query context, which means that they are used to score how well each document matches.The
filter
parameter indicates filter context. Itsterm
andrange
clauses are used in filter context. They will filter out documents which do not match, but they will not affect the score for matching documents.
kibana的搜索框和filter是过滤上下文
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/query-filter-context.html
full-text-queries 全文检索
term-level-queries 精确匹配查询
compound-queries 嵌套查询
joining-quieres 关联查询
geo-queries
specialized-queries
span queries
kibana的搜索可以使用本页所有的查询
参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/6.8/query-dsl.html
最基本的全文检索查询,支持单词查询、模糊匹配、短语查询、近义词查询
// 全文检索 默认or
GET index_name/_search
{
"query": {
"match":{
"products.product_name":{
"query":"shirt blue"
}
},
"size":100
}
}
// 全文检索 指定用 and
GET index_name/_search
{
"query": {
"match":{
"products.product_name":{
"query":"shirt blue",
"operator":"and"
}
}
}
}
//全文检索,模糊匹配
GET index_name/_search
{
"query": {
"match":{
"products.product_name":{
"query":"shirt blAe", // 故意写错,可以模糊匹配命中
"fuzziness":"AUTO"
}
}
}
}
类似match ,专门查询短语,可以指定短语的间隔slop(默认是0,一个间隔是一个词)
,比如 is test 可以命中 this is a test。
GET index_name/_search
{
"query": {
"match_phrase":{
"products.product_name":{
"query":"shirt blue",
"slop":3
}
}
}
}
类似短语查询,但最后一个单词是前缀查询,用于最后一个单词想不起来的情况
把match查询用在多个字段上
给非普通单词更大的权重
比如eat是普通单词,robinfu是特殊单词
使用Lucene查询语法的查询
可以指定各种AND | OR | NOT查询条件,而且支持在一条语句里对多字段查询
kibana的查询框就是用这个
es文档说仅适用于高级玩家
傻瓜版的query_string,可以兼容错误的语法,不会搞挂查询,适合当作搜索框直接暴露给用户
精确匹配整个查询语句
// term查询,被查询字段是keyword
GET index_name/_search
{
"query": {
"term":{
"products.product_name.keyword":{
"query":"this is a Test"
}
}
}
}
// term查询,被查询字段是text ,被分析过,用小写
GET index_name/_search
{
"query": {
"term":{
"products.product_name":{
"query":"test"
}
}
}
}
类似term,可以传入一个数组,匹配一个即可
类似terms,可以指定匹配条件数
支持脚本通过计算指定
可以按区间查日期、数字、甚至字符串
GET index_name/_search
{
"query": {
"range":{
"order_date":{
"gte":"2020-08-07T23:33:08+00:00",
"lte":"2020-08-07T23:33:12+00:00"
}
}
}
}
//前缀查询,注意是 被分析的(分词) 字段
GET index_name/_search
{
"query": {
"prefix":{
"products.product_name":"b1" //products.product_name.keyword也可以
}
}
}
通配符查询,支持单个?和多个*
通配符放在越前面,查询效率越低
因此es禁止其放在最前面
GET index_name/_search
{
"query": {
"wildcard":{
"products.product_name.keyword":"b*b*u"
}
}
}
使用不当会造成效率底下的查询
不要出现过度通配
比如ab可以命中ba
指定被查询字段的mapping类型
可指定多个id
包裹住的查询,会使用filter上下文,不计算相关性得分,可以指定常量分值
最常用的组合查询,与,或,非,filter,可嵌套
GET index_name/_search
{
"query": {
"bool":{
//must表示子查询必须满足
"must":{
"term":{"currency":"EUR"}
},
//filter 表示不计算得分
"filter":{
"term":{"customer_fist_name.keyword":"Eddie"}
},
//must_not 表示子查询禁止出现下述条件
"must_not":{
"range":{
"order_date":{"gte":"2020-08-09","lte":"2020-08-09"}
}
},
// should表示下面数组里面至少出现一个(也就是只要出现一个即可)
"should":[
{"term":{"products.discount_amount":"0"}},
{"term":{"category.keyword":"Men`s Clothing"}}
],
// minimum_should_match 标识should里面至少命中多少个,1就是至少命中一个,2就是至少命中2个
"minimum_should_match":1,
// boost 指定子查询的分值加成
"boost":1.0
}
}
}
对多个子查询的得分取最高
如果有子查询得分相近,还有加成选项
可以对子查询的得分进行复杂计算,比如最大,最下,平均,随机,各种复杂的数据运算
可以对子查询进行加分positive或者减分negative
区别于bool query 里的NOT,不是去掉,而是降低命中者的权重
倒排索引:索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而成为倒排索引(inverted index)。
分析:文档在建立倒排索引之前,es让每个被分析字段所做的一系列操作
创建索引的时候配置分析器
使用 template配置分析器
在elasticsearch的配置里设置全局默认分析器
全文检索类的搜索语言可以指定分析器,优先级如下:
PUT my_index
{
"settings":{
"analysis":{ //分析器设置
"analyzer":{ //定制分析器
"my_custom_analyzer":{ //定制的分析器名称
"type":"custom",
"char_filter":[ //指定字符过滤器
"emoticons"
],
"tokenizer":"punctuation", //指定分词器
"filter":[ //指定分词过滤器
"lowercae",
"english_stop"
]
}
},
"tokenizer":{ //自定义分词器
"punctuation":{
"type":"pattern",
"pattern":"[ .,!?]"
}
},
"char_filter":{ //自定义字符过滤器
"emoticons":{
"type":"mapping",
"mappings":[
":) => _happy_",
":( => _sad_"
]
}
},
"filter":{ //自定义分词过滤器
"english_stop":{
"type":"stop",
"stopwords":"_english_"
}
}
}
}
}
指定各种字符串使用指定分析器进行分析,直接展示分析结果
可以指定各种预定义分析器、自定义分析器
甚至可以分别指定字符过滤器、分词器、分词过滤器
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/_testing_analyzers.html
curl -X POST "localhost:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'
{
"analyzer": "whitespace",
"text": "The quick brown fox."
}
'
curl -X POST "localhost:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'
{
"tokenizer": "standard",
"filter": [ "lowercase", "asciifolding" ],
"text": "Is this déja vu?"
}
'
查看某个具体的文档的具体索引信息
这个文档有哪些分词,以及每个分词的词频、位置、开始和结束位置等
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/docs-termvectors.html
curl -X GET "localhost:9200/twitter/_doc/1/_termvectors?pretty"
curl -X GET "localhost:9200/twitter/_doc/1/_termvectors?fields=message&pretty"
它删除大多数标点符号、字母大写转小写、并删除停用词
遇到非字母就分词、所有字母转小写
只是按空格分词,别的啥也没干
在simple基础上,过滤掉停用词
真的什么都没干,就把字段当做停用词,最好别用,直接用keyword类型不要分析字段就好了
可以配置正则表达式作为分词条件,此外还会转小写和删除停用词
用于特定语言的字符串,支持34种语言,中文?不存在
生成指纹,一般用来检测字段重复,比如论文查重
POST _analyze
{
"analyzer":"fingerprint",
"text":"Yes,yes,Godel said this sentence is consistent and."
}
//返回
{
"token":[
{
"token":"and consistent godel is said sentence this yes",
"start_offset":0,
"end_offset":52,
"type":"fingerprint",
"position":0
}
]
}
可以随便组合内置的或者呢自己定义的字符过滤器,分词器,分词过滤器
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analysis-tokenizers.html
标准分析器里用的,基于语法的分词器,大致按空格、标点切分,适用于大多数欧洲语言
只要遇到不是字母的就分词,数字、符号、标点、空格等
相当于字母分词器+小写分词器
遇到空格、制表符、换行等空白符号则分词,注意不会去掉标点
在标准分词器基础上,增加对url和邮箱的识别,比如 Emai me at [email protected]切分为Email , me , at , [email protected]
专门针对英语的分词器,可以识别缩略词、公司名、邮箱、网络地址
泰语分词器,专门针对泰语,如果不是泰语则变为标准分词器
N元语法和侧边N元语法分词器,是es中非常独特的分词器,可以把单词切分为多个片段,以便于部分匹配。
先把单词按空格、标点等切成单词,再把单词切成n个字符的片段。
可配置min_gram最小元,max_gram最大元,token_chars分词范围(可选字符,数字,空格,标点,符号)
比如:{"min_gram":1,"max_gram":2,"token_chars":"["letter","digit"]}
Quick Foxes. =》
Q Qu u ui ic c ck k F Fo o ox x xe e es s
可用于模糊查询和位置语言分析
是N元分析的变体,从一侧开始切词。
{"min_gram":2,"max_gram":10,"token_chars":"["letter","digit"]}
Quick Foxes. =》
Qu, Qui, Quic, Quick, Fo, Fox, Foxe, Foxes
可以用于按每个单词,做类似前缀匹配的搜索
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analysis-tokenizers.html
略
把分词做一些转换操作
Standard Token Filter 默认
ASCII Folding Token Filter
Flatten Graph Token Filter
Length Token Filter
Lowercase Token Filter
Uppercase Token Filter
NGram Token Filter
Edge NGram Token Filter
Porter Stem Token Filter
Shingle Token Filter
Stop Token Filter
Word Delimiter Token Filter
Word Delimiter Graph Token Filter
Stemmer Token Filter
Stemmer Override Token Filter
Keyword Marker Token Filter
Keyword Repeat Token Filter
Synonym Token Filter
Reverse Token Filter
Truncate Token Filter
Unique Token Filter
Trim Token Filter
CJK Bigram Token Filter
中日韩
文用二元语法生成分词,因为不知道怎么切Keep Words Token Filter
Keep Types Token Filter
Fingerprint Token Filter
其他分词过滤器看官网
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/analysis-tokenfilters.html
去掉html标识符,并对html编码的符号解码
"I'm so happy!
" -----> "\nI`m so happy!\n"
// 标准分词器+下划线过滤器+ascii过滤器
POST _analyze
{
“tokenizer”:“standard”,
“filter”:[“lowercase”,“asciifolding”],
“text”:“Is this déja vu?” //é 会被替换成e
}
//N元语法分词器
POST _analyze
{
“tokenizer”:“ngram”,
“text”:“Quick Fox”
}
//侧边N元语法分词器
POST _analyze
{
“tokenizer”:“edge_ngram”,
“text”:“Quick Fox”
}
自定义分词器创建的索引:
PUT robbin-test-custom_analyzer
{
"mappings":{
"_doc":{
"properties":{
"rotext":{
"type":"text",
"fileds":{
"standard":{
"type":"text",
"analyzer":"standard" //分析器名称
},
"custom":{
"type":"text",
"analyzer":"robin_analyzer" //自定义分析器名称
}
}
}
}
}
},
//下面是定义分析器的细节
"settings":{
"analysis":{
"analyzer":{ //分析器的内容
"default":{ //名称为default的分析器
"type":"stop"
},
"robin_analyzer":{ //名称为robin_analyzer的分析器
"type":"custom",
"char_filter":[ //字符过滤器
"emoticons"
],
"tokenizer":"punctuation", //分词器
"filter":[ //分词过滤器
"lowercase",
"english_stop"
]
}
},
"tokenizer":{
"punctuation":{ //自定义的 ,名称为punctuation 的分词器,这里是具体细节
"type":"pattern",
"pattern":"[.,!?]"
}
},
"char_filter":{
"emoticons":{
"type":"mapping",
"mappings":[
":) => _happy_",
":( => _sad_"
]
}
},
"filter":{
"english_stop":{
"type":"stop",
"stopwords":"_english_"
}
}
}
}
}
执行结果:
{
"acknowledged":true,
"shards_acknowledged":true,
"index":"robbin-test-custom_analyzer"
}
基于上面的索引, 使用index的默认分析器(停用词分析器)
上面中的停用词 and被干掉了
这里 停用词 and没有被干掉
结果可以看到 大写的I转小写的i,并且 ““变成了”_happy_”。
PUT cjk_bigram_example
{
"settings":{
"analysis":{
"analyzer":{
"default":{
"tokenizer":"standard",
"filter":["han_bigrams_filter"]
}
},
"filter":{
"han_bigrams_filter":{
"type":"cjk_bigram",
"ignored_scripts":[
"hiragana",
"hatakaana",
"hangul"
],
"out_put_unigrams":true
}
}
}
}
}
结果:
中日韩文被二元语法,分析 ,英文不变
你好吗 ====》 你 你好 好 好吗 吗
Elastic search 6开始,默认使用BM25算法进行打分
TF-IDF(旧的)
Okapi BM25(新的)
用来查看具体的一次查询的得分计算情况,返回结果中会有 “_explanation” 里面时间具体的得分情况,类似mysql的 explai关键字
//方式1
GET get-together-group/_search
{
"query":{
"match_phrase":{
"description":{
"query":"learn about",
"slop":1
}
}
},
"explain":true
}
//方式2
GET get-together-group/_doc/1/_explain
{
"query":{
"match_phrase":{
"description":{
"query":"learn about",
"slop":1
}
}
}
}
有时打分会十分消耗资源
再打分(rescore)机制
Boosting (boost:促进)
Function_score
其实和算分没有太大关系,只是提到了脚本顺序说下
这里指的是未被分析的字段的数据,用到的场景有:
字段数据会被大规模加载,因此elasticsearch会将其缓存到内存
加载太多了会占用过多内存
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/modules-fielddata.html#
大概可以理解为分类统计,比如对一组数据的某个词条进行计数、或者计算某个数字类型的平均值
在kibana上随处可见:各种visualize都是基于此
分为度量聚集和桶聚集
对比搜索最大的不同
后过滤器
aggregation:聚合 精确计算
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/search-aggregations-metrics-avg-aggregation.html
Avg Aggregation:请平均值
Weighted Avg Aggregation:带权重的平均值
Max Aggregation:最大值
Min Aggregation:最小值
Sum Aggregation:求和
Value Count Aggregation:计数,最常用
Stats Aggregation:一次性返回 avg, max, min, sum, count
Extended Stats Aggregation:
Geo Bounds Aggregation:求坐标边界,返回矩形的左上和右下坐标
Geo Centroid Aggregation:求坐标中心,返回一个点
Scripted Metric Aggregation:自定义聚集,有点像 map-reduce
GET get-together-event/_search
{
"query":{
"match_all":{}
},
"aggs":{
"myAggs":{
"stats":{
"field":"date"
}
}
}
}
#返回结果:
{
"took":0,
"timed_out":false,
"_shareds":{...},
"hits":{...},
"aggregations":{
"myAggs":{
"count":15,
"min":1361212200000,
"max":1378751400000,
"avg":1271268440000,
"sum":2056902600000,
"min_as_string":"2013-02-18T:30:00.000Z",
"max_as_string":"2013-02-18T:30:00.000Z",
"avg_as_string":"2013-02-18T:30:00.000Z",
"sum_as_string":"2013-02-18T:30:00.000Z"
}
}
}
普通的聚集操作都要全部便利查询范围内的所有文档,如果数据量巨大时,需要很昂贵的代价,尤其是内存。很多时候并不需要精确的统计,可以牺牲部分精确性,来节省消耗的资源。
Cardinality Aggregation:
Percentiles Aggregation:
Percentiles Ranks Aggregation:
Top Hits Aggregation:
把数据按照某个标签分组,kibana里非常常见
Terms Aggregation:每个不同的词条一个桶
Range Aggregation:可以按指定范围分桶,通常用于数字类型字段
Date Range Aggregation:指定时间范围分桶
Histogram Aggregation:类似range,但是间隔固定长度,用于绘制直方图
Date Histogram Aggregation:日期直方图分桶
桶下可以嵌套或者数值
# 统计平均
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"avg":{
"field":"cost"
}
}
}
}
#返回结果:
{
"took":13,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":10000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"value":8.48455218860048
}
}
}
kibana分桶演示,五分钟一个桶,求平局值, x轴:时间,y轴:字段 cost平局值
#加权平均
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"weighted_avg":{
"value":{
"field":"cost"
},
"weight":{
"field":"cost"
}
}
}
}
}
#返回结果:
{
"took":14,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":10000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"value":1337.104116640804
}
}
}
#求各种统计
GET get-together-event/_search
{
"query":{
"match_all":{}
},
"aggs":{
"myAggs":{
"stats":{
"field":"cost"
}
}
}
}
#返回结果:
{
"took":11,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"myAggs":{
"count":15,
"min":1361212200000,
"max":1378751400000,
"avg":1271268440000,
"sum":2056902600000
}
}
}
#求各种高级统计
GET get-together-event/_search
{
"query":{
"match_all":{}
},
"aggs":{
"myAggs":{
"extended_stats":{
"field":"cost"
}
}
}
}
#返回结果:
{
"took":11,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"count":15,
"min":1361212200000,
"max":1378751400000,
"avg":1271268440000,
"sum":2056902600000,
"sum_of_squares":1.27789785,
"variance":11256.23564445,
"std_deviaton":106.2356459456,
"std_deviaton_bounds":{
"upper":220.456456451231,
"lower":-203.23456456124
}
}
}
}
#抽样的基数统计
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"cardinality":{
"field":"application.name"
}
}
}
}
#返回结果:
{
"took":13,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"value":34
}
}
}
# 百分位聚集
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"percentiles":{
"field":"cost"
}
}
}
}
# 返回结果:
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"value":{
"1.0":0.0,
"5.0":0.0,
"25.0":0.0,
"50.0":1.0,
"75.0":4.0,
"95.0":17.6334156456465,
"99.0":141.123456456455
}
}
}
}
percentile_ranks
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"percentile_ranks":{
"field":"cost",
"values":[10,20,30,100,200,500,1000]
}
}
}
}
#返回结果:
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"value":{
"10.0":93.60010274082556,
"20.0":95.12345678945645,
"30.0":95.59632456232212,
"100.0":97.1231564421231,
"200.0":99.8945132121221,
"500.0":99.6334156456465,
"1000.0":99.2123456456455
}
}
}
}
桶聚集-terms
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"terms":{
"field":"application.name", //按照名称,取前5
"size":5
}
}
}
}
#返回结果:
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"doc_count_error_upper_bound":7064,
"sum_other_doc_count":193834,
"buckets":[
{
"key":"travel_lbs_track-service",
"doc_counnt":329834
},
{
"key":"travel_cron-service",
"doc_counnt":309834
},
// ...
]
}
}
}
}
相关性词条聚集,用于搜索推荐
GET format-req-log-test-2020.09.03-000015/_search
{
"query":{
"match":{
"message":"shunfeng"
}
},
"size":0,
"aggs":{
"c":{
"significant_terms":{
"field":"application.name"
}
}
}
}
#返回结果:
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"doc_count":7064,
"bg_count":193834,
"buckets":[
{
"key":"travel_shunfeng-service",
"doc_counnt":329834,
"score":70.123456456,
"bg_counnt":89098
},
{
"key":"travel_client_api",
"doc_counnt":309834,
"score":70.123456456,
"bg_counnt":89098
},
//...
]
}
}
}
}
相关性分词聚集,用于搜索推荐
GET format-req-log-test-2020.09.03-000015/_search
{
"query":{
"match":{
"message":"shunfeng"
}
},
"size":0,
"aggs":{
"c":{
"significant_text":{
"field":"message"
}
}
}
}
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"doc_count":7064,
"bg_count":193834,
"buckets":[
{
"key":"shunfeng",
"doc_counnt":329834,
"score":70.123456456,
"bg_counnt":89098
},
{
"key":"10.3.0.23",
"doc_counnt":309834,
"score":70.123456456,
"bg_counnt":89098
},
//...
]
}
}
}
}
# 桶聚集-range
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"range":{
"field":"message",
"ranges":[
{"to":10},
{"from":10,"to":50},
{"from":50,"to":500},
{"from":500,"to":5000}
]
}
}
}
}
#返回结果:
{
"took":495,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"doc_count":7064,
"bg_count":193834,
"buckets":[
{
"key":"*-10.0",
"to":10.0,
"doc_counnt":32983421
},
{
"key":"10.0-50.0",
"from":10.0,
"to":50.0,
"doc_counnt":31774
},
{
"key":"50.0-500.0",
"from":50.0,
"to":500.0,
"doc_counnt":43320
},
{
"key":"500.0-5000.0",
"from":500.0,
"to":5000.0,
"doc_counnt":586
}
]
}
}
}
}
#桶聚集 date-range
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"date_range":{
"field":"@timestamp",
"format":"yyyy-MM-dd HH:mm:ss",
"ranges":[
{"from":"now-30m/m","to":10},
{"from":"now-20m/m"}
]
}
}
}
}
#返回结果:
{
"took":10,
"timed_out":false,
"_shareds":{
"total":6,
"successful":6,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1000,
"relation":"gte"
},
"max_socre":null,
"hits":[]
},
"aggregations":{
"c":{
"buckets":[
{
"key":"2020-09-04 08:14:00-2020-09-04 08:24:00",
"from":1.59920724E12,
"from_as_string":"2020-09-04 08:14:00",
"to":1.59920784E12,
"to_as_string":"2020-09-04 08:24:00",
"doc_counnt":11362
},
{
"key":"2020-09-04 08:24:00-*",
"from":1.59920724E12,
"from_as_string":"2020-09-04 08:24:00",
"doc_counnt":21360
}
]
}
}
}
}
#桶聚集 histogram
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"histogram":{
"field":"cost",
"interval":100
}
}
}
}
#桶聚集 日期直方图 date_histogram
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"c":{
"date_histogram":{
"field":"@timtstamp",
"interval":"30m"
}
}
}
}
#桶聚集 日期直方图 date_histogram 嵌套聚集
GET format-req-log-test-2020.09.03-000015/_search
{
"size":0,
"aggs":{
"main":{
"date_histogram":{
"field":"@timtstamp",
"interval":"30m"
},
"aggs":{ //嵌套一个度量
"sub":{
"stats":{
"field":"cost"
}
}
}
}
}
}
Es有提供文档关系的操作,但有很多限制,功能也不是很强大
后期版本把_type去掉之后,文档的关系操作就更加弱化了
官网指导
可以节省重复创建连接的网络开销,要通过测试才能知道最佳的一次批处理量,并不是越大越好,太大了会占用内存,并且bulk有个处理队列,过慢的index会导致队列满而丢弃后面的请求。
es是准实时系统,新写入的分段需要被刷新才被完全创建,才可用于查询,慢的刷新频率可以使降低分段合并的频率,分段合并十分耗资源,默认刷新频率是1s,对index修改,index.refresh.interval即可立即生效。
比如reindex或是导入基础数据这种一次性批量索引操作,可以配置成不刷新,并且把副本数也配置成0,完了之后再设置成正常值,每一次写入都要等所有副本都报告写入完成才算完成,副本数数量越多写入越慢。
操作系统会自动把不常用的内存交换到磁盘(虚拟内存),es是运行与jvm的,这个操作可能会导致gc。
默认是指明文档id的,这样的话es需要先判断一下这个id的文档是否已经存在,以做一些合并或者更新操作。如果用自主生成的id,则可以跳过这个步骤节省开支。
分片数量影响到分段数量,分片少的话允许的分段数也会少,从而会增加分段合并的频率,消耗性能。
如果写入规模巨大,要控制index的规模(按月、按周、按天适当分,或自动滚动),同时根据集群节点数量设置合适的分片数。使得每个分片的数据量有限(每个分片:30G-40G)。
副本数量越多,写入越慢,但是副本数多的话,负载均衡,可以容灾,所以需要权衡。
不需要分析的字段就不要分析
过滤器上下文:不计算得分可以减少资源消耗,过滤器还可以缓存
脚本非常耗性能,因为每次计算且无法缓存,如果非用不可,用painless或expressions(空间换时间)
比如某个字段经常被range查询或聚集,那在索引字段的时候,就把range范围确定好,比如15属于10-100,就存一个15一个10-100(空间换时间)
使用适当的分析器,如果对查询速度要求很高,就要在索引的时候牺牲性能。
数字是存在另外的地方,所以有时候数字可以存成keyword而不是numeric会更快。
比如term查询比query查询更省资源,query会被分析,衍生出很多子查询。通配符查询很费性能,尤其是通配符放在前面
不管是嵌套还是父子,都会使查询量倍增,通过冗余数据,以空间换时间,存储的成本很低。
可以均衡查询负载,分担查询
如果es按时间索引,你又恰好知道它在哪个时间段。精确的查询到这个索引而不是查询一大片,显然会更快。
索引的时候有一个_route
参数,可以控制某个文档索引到哪个分片,如果你的一个查询的所有结果都从一个分片获取,就能减少数据合并的开销,如果分片在不同机器,还能节省网络开销。
节点的配置有一个allocation awareness,可以根据rack(机架),group(集群),zone(地区)来配置节点,使得分片均匀分布,从而降低单点热度,同一个分片的副本不在一起,还可以容灾。
可以更容易命中缓存
分段越少,查询越快,因为每次查询都要拆到所有分段去处理,再合并结果。
有一个_forcemerge
接口,可以把分段弄能1。
同理,甚至可以合并分片(reindex或shink)。
机器内存最多分一半给es,剩下留给文件系统,因为es非常依赖操作系统的文件缓存,尤其是查询操作。
es需要频繁读取磁盘
官网优化
不被用来查询的字段,不索引
不做全文检索,不分词(keyword)
不关注文档的相关性,关闭norms
https://www.elastic.co/guide/en/elasticsearch/reference/6.8/norms.html
PUT my_index/_mapping/_doc
{
"properties": {
"title": {
"type": "text",
"norms": false
}
}
}
不需要短语检索,关闭位置索引
默认会对string字段做两次索引(text和keyword)
分片越大,存储效率越高
滚动存储,使其大小可控
使用收缩api收缩分片
_all, _source
分段合并
数字类型的字段用最小类型
空间换时间的概念贯穿始终
写快读慢,读慢写快
答:
分段的打开和关闭是es自己控制的,无法干预,只能设置刷新时间控制频率。
forcemerge 可以强行合并 (官网搜索forcemerge)
post/twitter/_forcemerge
答:
不太清楚,应该不会,es应该会自动控制同时打开的分段,
打开的分段不会影响查询,只有关闭的分段才会查询得到,但是打开会消耗资源,可能会有影响。
对于kibana而言,把kafka的offerset直接指定到最新,从当前消息来消费,再起一个线程消费 旧数据
未完待续
本文仅做学习只用,侵权速删