Learning ElasticSearch

Learning ElasticSearch

最近在学习ElasticSearch,从底层的Lucene开始了解
特别推荐这两本书,非常非常感谢社区的贡献,有机会还是支持出版的纸质书籍
这篇文章等于是对这两本书阅读后做了摘要,方便零基础的用户快速上手ElasticSearch
Elasticsearch 权威指南(中文版)
ELKstack 中文指南(ElasticSearch章节)

ES vs SQL

Relational DB Elasticsearch
Databases Indices
Tables Types
Rows Documents
Columns Fields

Elasticsearch集群可以包含多个索引(indices)(数据库),每一个索引可以包含多个类型(types)(表),每一个类型包含多个文档(documents)(行),然后每个文档包含多个字段(Fields)(列)。

“索引”含义的区分

你可能已经注意到索引(index)这个词在Elasticsearch中有着不同的含义,所以有必要在此做一下区分:

  • 索引(名词) 如上文所述,一个索引(index)就像是传统关系数据库中的数据库,它是相关文档存储的地方,index的复数是indices 或indexes。
  • 索引(动词) 「索引一个文档」表示把一个文档存储到索引(名词)里,以便它可以被检索或者查询。这很像SQL中的INSERT关键字,差别是,如果文档已经存在,新的文档将覆盖旧的文档。
  • 倒排索引 传统数据库为特定列增加一个索引,例如B-Tree索引来加速检索。Elasticsearch和Lucene使用一种叫做倒排索引(inverted index)的数据结构来达到相同目的。

Excerise

  1. Ping & Server info

    http http://127.0.0.1:9200/
    {
    "cluster_name": "elasticsearch",
    "name": "Goliath",
    "status": 200,
    "tagline": "You Know, for Search",
    "version": {
    "build_hash": "c88f77ffc81301dfa9dfd81ca2232f09588bd512",
    "build_snapshot": false,
    "build_timestamp": "2015-02-19T13:05:36Z",
    "lucene_version": "4.10.3",
    "number": "1.4.4"
    }
    }
  2. All rows

    http 'http://localhost:9200/_count'
    {
    "_shards": {
    "failed": 0,
    "successful": 436,
    "total": 436
    },
    "count": 35672472
    }
  3. All indices

    http 'localhost:9200/_cat/indices?v'
    health status index pri rep docs.count docs.deleted store.size pri.store.size
    yellow open .kibana 1 1 10 2 27.5kb 27.5kb
    yellow open logstash-2016.01.18 5 1 289193 0 170.8mb 170.8mb
    yellow open logstash-2016.03.22 5 1 597585 0 141.1mb 141.1mb
    yellow open logstash-2016.03.17 5 1 582696 0 138.5mb 138.5mb
    yellow open logstash-2016.02.25 5 1 304186 0 103.2mb 103.2mb
    yellow open logstash-2016.02.24 5 1 298755 0 102.5mb 102.5mb
  4. Cluster health

    http '127.0.0.1:9200/_cluster/health?pretty'
    {
    "active_primary_shards": 436,
    "active_shards": 436,
    "cluster_name": "elasticsearch",
    "initializing_shards": 0,
    "number_of_data_nodes": 1,
    "number_of_nodes": 1,
    "relocating_shards": 0,
    "status": "yellow",
    "timed_out": false,
    "unassigned_shards": 436
    }
  5. Nodes list

    http '127.0.0.1:9200/_cat/nodes'
    366dcee51992 172.17.5.127 78 80 1.21 d * Goliath
  6. Task list

    http 'http://127.0.0.1:9200/_cluster/pending_tasks'
    {
    "tasks": []
    }
  7. Node health
    jvm

    http '127.0.0.1:9200/_nodes/stats/jvm'
    {
    "cluster_name": "elasticsearch",
    "nodes": {
    "vwKzZrSrTDidFr-jtZdRjg": {
    "host": "366dcee51992",
    "ip": [
    "inet[/172.17.5.127:9300]",
    "NONE"
    ],
    "jvm": {
    "buffer_pools": {
    "direct": {
    "count": 1610,
    "total_capacity_in_bytes": 39064123,
    "used_in_bytes": 39064123
    },
    "mapped": {
    "count": 2656,
    "total_capacity_in_bytes": 6260870265,
    "used_in_bytes": 6260870265
    }
    },
    "gc": {
    "collectors": {
    "old": {
    "collection_count": 20469,
    "collection_time_in_millis": 1804674
    },
    "young": {
    "collection_count": 396171,
    "collection_time_in_millis": 4463746
    }
    }
    },
    "mem": {
    "heap_committed_in_bytes": 1037959168,
    "heap_max_in_bytes": 1037959168,
    "heap_used_in_bytes": 849427344,
    "heap_used_percent": 81,
    "non_heap_committed_in_bytes": 85303296,
    "non_heap_used_in_bytes": 56807912,
    "pools": {
    "old": {
    "max_in_bytes": 715849728,
    "peak_max_in_bytes": 715849728,
    "peak_used_in_bytes": 672561680,
    "used_in_bytes": 602039528
    },
    "survivor": {
    "max_in_bytes": 35782656,
    "peak_max_in_bytes": 35782656,
    "peak_used_in_bytes": 35782656,
    "used_in_bytes": 6359176
    },
    "young": {
    "max_in_bytes": 286326784,
    "peak_max_in_bytes": 286326784,
    "peak_used_in_bytes": 286326784,
    "used_in_bytes": 241028640
    }
    }
    },
    "threads": {
    "count": 130,
    "peak_count": 135
    },
    "timestamp": 1459242935482,
    "uptime_in_millis": 2528035636
    },
    "name": "Goliath",
    "timestamp": 1459242935482,
    "transport_address": "inet[/172.17.5.127:9300]"
    }
    }
    }

os

http '127.0.0.1:9200/_nodes/stats/os'
{
"cluster_name": "elasticsearch",
"nodes": {
"vwKzZrSrTDidFr-jtZdRjg": {
"host": "366dcee51992",
"ip": [
"inet[/172.17.5.127:9300]",
"NONE"
],
"name": "Goliath",
"os": {
"cpu": {
"idle": 75,
"stolen": 0,
"sys": 2,
"usage": 17,
"user": 15
},
"load_average": [
3.23,
2.33,
2.27
],
"mem": {
"actual_free_in_bytes": 5407027200,
"actual_used_in_bytes": 28110413824,
"free_in_bytes": 2586775552,
"free_percent": 16,
"used_in_bytes": 30930665472,
"used_percent": 83
},
"swap": {
"free_in_bytes": 7885094912,
"used_in_bytes": 8892116992
},
"timestamp": 1459245065474,
"uptime_in_millis": 21615006
},
"timestamp": 1459245065474,
"transport_address": "inet[/172.17.5.127:9300]"
}
}
}
  1. Index info

    http http://127.0.0.1:9200/logstash-2016.03.29/
    {
    "logstash-2016.03.29": {
    "aliases": {},
    "mappings": {
    "_default_": {
    "_all": {
    "enabled": true
    },
    "dynamic_templates": [
    {
    "string_fields": {
    "mapping": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "index": "analyzed",
    "omit_norms": true,
    "type": "string"
    },
    "match": "*",
    "match_mapping_type": "string"
    }
    }
    ],
    "properties": {
    "@version": {
    "index": "not_analyzed",
    "type": "string"
    },
    "geoip": {
    "dynamic": "true",
    "properties": {
    "location": {
    "type": "geo_point"
    }
    }
    }
    }
    },
    "syslog": {
    "_all": {
    "enabled": true
    },
    "dynamic_templates": [
    {
    "string_fields": {
    "mapping": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "index": "analyzed",
    "omit_norms": true,
    "type": "string"
    },
    "match": "*",
    "match_mapping_type": "string"
    }
    }
    ],
    "properties": {
    "@timestamp": {
    "format": "dateOptionalTime",
    "type": "date"
    },
    "@version": {
    "index": "not_analyzed",
    "type": "string"
    },
    "brief": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "count": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "geoip": {
    "dynamic": "true",
    "properties": {
    "location": {
    "type": "geo_point"
    }
    }
    },
    "host": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "hostname": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "info": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "log_type": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "module": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "msg": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "oid": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "serverity": {
    "type": "long"
    },
    "severity": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "status": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog5424_pri": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog_facility": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog_facility_code": {
    "type": "long"
    },
    "syslog_message": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog_program": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog_severity": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "syslog_severity_code": {
    "type": "long"
    },
    "tags": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "timestamp": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    },
    "type": {
    "fields": {
    "raw": {
    "ignore_above": 256,
    "index": "not_analyzed",
    "type": "string"
    }
    },
    "norms": {
    "enabled": false
    },
    "type": "string"
    }
    }
    }
    },
    "settings": {
    "index": {
    "creation_date": "1459189864276",
    "number_of_replicas": "1",
    "number_of_shards": "5",
    "refresh_interval": "5s",
    "uuid": "qUZ_ZY3_RPOdhrdJ5mi32A",
    "version": {
    "created": "1040499"
    }
    }
    },
    "warmers": {}
    }
    }

cat 接口的命令行使用

之前介绍的各种接口数据,其响应数据都是 JSON 格式,更适用于程序处理。对于我们日常运维,在 Linux 命令行终端环境来说,简单的分行和分列表格才是更方便的样式。为此,Elasticsearch 提供了 cat 接口。

cat 接口可以读取各种监控数据,可用接口列表如下:

/_cat/allocation
/_cat/shards
/_cat/shards/{index}
/_cat/master
/_cat/nodes
/_cat/indices
/_cat/indices/{index}
/_cat/segments
/_cat/segments/{index}
/_cat/count
/_cat/count/{index}
/_cat/recovery
/_cat/recovery/{index}
/_cat/health
/_cat/pending_tasks
/_cat/aliases
/_cat/aliases/{alias}
/_cat/thread_pool
/_cat/plugins
/_cat/fielddata
/_cat/fielddata/{fields}

增删改查

增删改查是数据库的基础操作方法。ES 虽然不是数据库,但是很多场合下,都被人们当做一个文档型 NoSQL 数据库在使用,原因自然是因为在接口和分布式架构层面的相似性。虽然在 ELKstack 场景下,数据的写入和查询,分别由 Logstash 和 Kibana 代劳,作为测试、调研和排错时的基本功,还是需要了解一下 ES 的增删改查用法的。

数据写入

ES 的一大特点,就是全 RESTful 接口处理 JSON 请求。所以,数据写入非常简单:

curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/testlog -d '{
"date" : "1434966686000",
"user" : "chenlin7",
"mesg" : "first message into Elasticsearch"
}'

命令返回响应结果为:

{"_index":"logstash-2015.06.21","_type":"testlog","_id":"AU4ew3h2nBE6n0qcyVJK","_version":1,"created":true}

数据获取

“可以看到,在数据写入的时候,会返回该数据的 _id。这就是后续用来获取数据的关键:

curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK
命令返回响应结果为:

{"_index":"logstash-2015.06.21","_type":"testlog","_id":"AU4ew3h2nBE6n0qcyVJK","_version":1,"found":true,"_source":{
"date" : "1434966686000",
"user" : "chenlin7",
"mesg" : "first message into Elasticsearch"
}}

这个 _source 里的内容,正是之前写入的数据。

如果觉得这个返回看起来有点太过麻烦,可以使用
curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_source
来指明只获取源数据部分。

更进一步的,如果你只想看数据中的一部分字段内容,可以使用
curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK?fields=user,mesg
来指明获取字段,结果如下:

{"_index":"logstash-2015.06.21","_type":"testlog","_id":"AU4ew3h2nBE6n0qcyVJK","_version":1,"found":true,"fields":{"user":["chenlin7"],"mesg":["first message into Elasticsearch"]}}

数据删除

要删除数据,修改发送的 HTTP 请求方法为 DELETE 即可:
curl -XDELETE http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK
删除不单针对单条数据,还可以删除整个 type,乃至整个索引。甚至可以用通配符。
curl -XDELETE http://127.0.0.1:9200/logstash-2015.06.0*

数据更新

已经写过的数据,同样还是可以修改的。有两种办法,一种是全量提交,即指明 _id 再发送一次写入请求。

curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK -d '{
"date" : "1434966686000",
"user" : "chenlin7",
"mesg" " "first message into Elasticsearch but version 2"
}'

另一种是局部更新,使用 /_update 接口:

curl -XPOST 'http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_update' -d '{
"doc" : {
"user" : "someone"
}
}'

或者

curl -XPOST 'http://127.0.0.1:9200/logstash-2015.06.21/testlog/AU4ew3h2nBE6n0qcyVJK/_update' -d '{
"script" : "ctx._source.user = \"someone\""
}'

搜索请求

上节介绍的,都是针对单条数据的操作。在 ES 环境中,更多的是搜索和聚合请求。在之前章节中,我们也介绍过数据获取和数据搜索的一点区别:==刚写入的数据,可以通过 translog 立刻获取;但是却要等到 refresh 成为一个 segment 后,才能被搜索到。==本节就介绍一下 ES 的搜索语法。

全文搜索

ES 对搜索请求,有简易语法和完整语法两种方式。简易语法作为以后在 Kibana 上最常用的方式,一定是需要学会的。而在命令行里,我们可以通过最简单的方式来做到。还是上节输入的数据:
curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/_search?q=first
可以看到返回结果:

{"took":240,"timed_out":false,"_shards":{"total":27,"successful":27,"failed":0},"hits":{"total":1,"max_score":0.11506981,"hits":[{"_index":"logstash-2015.06.21","_type":"testlog","_id":"AU4ew3h2nBE6n0qcyVJK","_score":0.11506981,"_source":{
"date" : "1434966686000",
"user" : "chenlin7",
"mesg" : "first message into Elasticsearch"
}}]}}

还可以用下面语句搜索,结果是一样的。
curl -XGET http://127.0.0.1:9200/logstash-2015.06.21/testlog/_search?q=user:"chenlin7

Excerpt From: 饶琛琳. “ELKstack 中文指南.” iBooks.

querystring 语法

curl -XGET http://127.0.0.1:9200/logstash-2016.03.29/_search?q=first

上例中,?q=后面写的,就是 querystring 语法。鉴于这部分内容会在 Kibana 上经常使用,这里详细解析一下语法:

  • 全文检索:直接写搜索的单词,如上例中的 first
  • 单字段的全文检索:在搜索单词之前加上字段名和冒号,比如如果知道单词 first 肯定出现在 mesg 字段,可以写作 mesg:first
  • 单字段的精确检索:在搜索单词前后加双引号,比如 user:"chenlin7"
    多个检索条件的组合:可以使用 NOT, AND 和 OR 来组合检索,注意必须是大写。比如 user:("chenlin7" OR "chenlin") AND NOT mesg:first
  • 字段是否存在_exists_:user 表示要求 user 字段存在,_missing_:user 表示要求 user 字段不存在;
  • 通配符:用 ? 表示单字母, 表示任意个字母。比如 `fir?t mess`;
  • 正则:需要比通配符更复杂一点的表达式,可以使用正则。比如 mesg:/mes{2}ages?/。注意 ES 中正则性能很差,而且支持的功能也不是特别强大,尽量不要使用。ES 支持的正则语法见:query-dsl-regexp-query.html
  • 近似搜索:用 ~ 表示搜索单词可能有一两个字母写的不对,请 ES 按照相似度返回结果。比如 frist~
  • 范围搜索:对数值和时间,ES 都可以使用范围搜索,比如:rtt:>300date:["now-6h" TO "now"} 等。其中,[] 表示端点数值包含在范围内,{} 表示端点数值不包含在范围内;

完整语法

ES 支持各种类型的检索请求,除了可以用 querystring 语法表达的以外,还有很多其他类型,具体列表和示例可参见:query-dsl-regexp-query.html

作为最简单和常用的示例,这里展示一下 term query 的写法,相当于 querystring 语法中的 user:"chenlin7"

curl -XGET http://127.0.0.1:9200/_search -d '
{
"query": {
"term": {
"user": "chenlin7"
}
}
}'

聚合请求

在检索范围确定之后,ES 还支持对结果集做聚合查询,返回更直接的聚合统计结果。在 ES 1.0 版本之前,这个接口叫 Facet,1.0 版本之后,这个接口改为 Aggregation。

Kibana 分别在 v3 中使用 Facet,v4 中使用 Aggregation。不过总的来说,Aggregation 是 Facet 接口的强化升级版本,我们直接了解 Aggregation 即可。本书后续章节也会介绍如何在 Kibana 的 v3 版本中使用 aggregation 接口做二次开发。

Aggregation 分为 bucket 和 metric 两种,分别用作词元划分和数值计算。而其中的 bucket aggregation,还支持在自身结果集的基础上,叠加新的 aggregation。这就是 aggregation 比 facet 最领先的地方。比如实现一个时序百分比统计,在 facet 接口就无法直接完成,而在 aggregation 接口就很简单了:

# curl -XPOST 'http://127.0.0.1:9200/logstash-2015.06.22/_search?size=0&pretty' -d'{
"aggs" : {
"percentile_over_time" : {
"date_histogram" : {
"field" : "@timestamp",
"interval" : "1h"
},
"aggs" : {
"percentile_one_time" : {
"percentiles" : {
"field" : "requesttime"
}
}
}
}
}
}'

得到结果如下:

{
"took" : 151595,
"timed_out" : false,
"_shards" : {
"total" : 81,
"successful" : 81,
"failed" : 0
},
"hits" : {
"total" : 3307142043,
"max_score" : 1.0,
"hits" : [ ]
},
"aggregations" : {
"percentile_over_time" : {
"buckets" : [ {
"key_as_string" : "22/Jun/2015:22:00:00 +0000",
"key" : 1435010400000,
"doc_count" : 459273981,
"percentile_one_time" : {
"values" : {
"1.0" : 0.004,
"5.0" : 0.006,
"25.0" : 0.023,
"50.0" : 0.035,
"75.0" : 0.08774675719725569,
"95.0" : 0.25732934416125663,
"99.0" : 0.7508899754871812
}
}
}, {
"key_as_string" : "23/Jun/2015:00:00:00 +0000",
"key" : 1435017600000,
"doc_count" : 768620219,
"percentile_one_time" : {
"values" : {
"1.0" : 0.004,
"5.0" : 0.007000000000000001,
"25.0" : 0.025,
"50.0" : 0.03987809503972864,
"75.0" : 0.10297843567746187,
"95.0" : 0.30047269327062875,
"99.0" : 1.015495933753329
}
}
}, {
"key_as_string" : "23/Jun/2015:02:00:00 +0000",
"key" : 1435024800000,
"doc_count" : 849467060,
"percentile_one_time" : {
"values" : {
"1.0" : 0.004,
"5.0" : 0.008,
"25.0" : 0.027000000000000003,
"50.0" : 0.0439999899006102,
"75.0" : 0.1160416197625958,
"95.0" : 0.3383140614483838,
"99.0" : 1.0275839684542212
}
}
} ]
}
}
}

ES 目前能支持的聚合请求列表,参见:search-aggregations.html

GC

对不了解 JVM 的 GC 的读者,这里先介绍一下 GC(垃圾收集)以及 GC 对 Elasticsearch 的影响。

Java is a garbage-collected language, which means that the programmer does not manually manage memory allocation and deallocation. The programmer simply writes code, and the Java Virtual Machine (JVM) manages the process of allocating memory as needed, and then later cleaning up that memory when no longer needed. Java 是一个自动垃圾收集的编程语言,启动 JVM 虚拟机的时候,会分配到固定大小的内存块,这个块叫做 heap(堆)。JVM 会把 heap 分成两个组:

  • Young 新实例化的对象所分配的空间。这个空间一般来说只有 100MB 到 500MB 大小。Young 空间又分为两个 survivor(幸存)空间。当 Young 空间满,就会发生一次 young gc,还存活的对象,就被移入幸存空间里,已失效的对象则被移除。
  • Old 老对象存储的空间。这些对象应该是长期存活而且在较长一段时间内不会变化的内容。“这个空间会大很多,在 ES 来说,一节点上可能就有 30GB 内存是这个空间。前面提到的 young gc 中,如果某个对象连续多次幸存下来,就会被移进 Old 空间内。而等到 Old 空间满,就会发生一次 old gc,把失效对象移除。
    听起来很美好的样子,但是这些都是有代价的!在 GC 发生的时候,JVM 需要暂停程序运行,以便自己追踪对象图收集全部失效对象。在这期间,其他一切都不会继续运行。请求没有响应,ping 没有应答,分片不会分配……

当然,young gc 一般来说执行极快,没太大影响。但是 old 空间那么大,稍慢一点的 gc 就意味着程序几秒乃至十几秒的不可用,这太危险了。

JVM 本身对 gc 算法一直在努力优化,Elasticsearch 也尽量复用内部对象,复用网络缓冲,然后还提供像 Doc Values 这样的特性。但不管怎么说,gc 性能总是我们需要密切关注的数据,因为它是集群稳定性最大的影响因子。

如果你的 ES 集群监控里发现经常有很耗时的 GC,说明集群负载很重,内存不足。严重情况下,这些 GC 导致节点无法正确响应集群之间的 ping ,可能就直接从集群里退出了。然后数据分片也随之在集群中重新迁移,引发更大的网络和磁盘 IO,正常的写入和搜索也会受到影响。

查询慢

更常见的可能,是集群存储长期数据导致索引映射数据确实大到了 master 节点内存不足以快速处理的地步。

这时候,根据实际情况,可以有以下几种选择:

索引就是特别多:给 master 加内存。
索引里字段太多:改用 nested object 方式节省字段数量。
索引多到内存就是不够了:把一部分数据拆出来另一个集群。

Web Console

elasticsearch-kopf