Elasticsearch 使用中的常见问题

1、重启集群后,出现了unassigned shards

集群的shard数量较多,在节点重启后,由于recovery并发限制,shard分配次数超限,集群就不会在分配shard了,从而出现unassigned shards; 可以使用api对shard进行重新分配:POST _cluster/reroute?retry_failed=true

2、使用ES过程中,出现bulk reject, 查看日志看到bulk queue满了

可以通过GET _cat/thread_pool/bulk?s=queue:desc&v查看正在拒绝或者历史拒绝的个数;一般默认bulk queue大小是1024, 造成bulk reject的大多数原因大概有两种:

  1. shard容量多大,建议每个shard容量控制在20G-50G,shard数量不能过多,也不能过少
  2. shard分布不均,可通过调整index.routing.allocation.total_shards_per_node参数来解决

3、磁盘写满后,清理了一部分数据,此时无法向ES中写入数据

磁盘写满后,ES会把集群的block级别改为read_only_allow_delete, 此时需要修改该参数,通过调用PUT _cluster/settings {cluster. blocks.read_only_allow_delete:”false”}解决,如果出现index level级别的block贼需要修改对应索引的配置

4、使用logstash收集日志时,如何把logstash默认添加的@timestamp字段替换为日志中的时间?

1
2
3
4
5
6
7
8
9
filter {
grok {
match => ["message", "%{TIMESTAMP_ISO8601:logdate}"
}
date {
match => ["logdate", "yyyy-MM-dd HH:mm:ss,SSS"]
target => "@timestamp"
}
}

5、使用logstash收集日志时,如何修改logstash默认添加的@timestamp字段的时区为东八区?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
filter {
date {
match => ["message","UNIX_MS"]
target => "@timestamp"
}
ruby {
code=>"event.set('timestamp',event.get('@timestamp').time.localtime + 8*60*60)"
}
ruby {
code => "event.set('@timestamp',event.get('timestamp'))"
}
mutate {
remove_field => ["timestamp"]
}
}

6、调用_forcemerge API发现并没有减少segment的数量?

_forcemerge API执行merge是有条件的,当索引数据量比较大时,经过了一定时间的merge之后,每个segement都会比较大,如果此时indexing的速率较低,merge操作期望可以merge的segment数量比较大,而实际上候选的segment数量没有那么大时,就不会触发merge操作

7、聚合查询越来越慢

需要确认进行聚合的字段唯一值是否比较多,唯一值较多的情况下聚合查询构建Global Ordinals会比较慢,如果索引没有持续写入,构建好的Global Ordinals就会进行缓存,之后的查询就可以使用缓存中的Global Ordinals;但是如果索引在持续写入,每当底层的segment发生变化时(有新数据写入导致产生新的Segment、Segment Merge),就需要重新构建Global Ordinals,随着数据量的增大聚合字段的唯一值越来越多,构建Global Ordinals越来越慢,所以对持续写入的索引,聚合查询会越来越慢。从业务角度进行优化的方案可以参考:https://cloud.tencent.com/developer/article/1421924

8、在kibana上创建index pattern卡主了,一直转圈圈,无法创建

首先确认下.kibana的索引是否被设置为只读了,当集群出现过磁盘写满时,ES会自动把索引设置block级别设置为readonly_allow_delete, 如果被设置为只读,需要修改block级别PUT .kibana/_settings {“index.blocks.read_only_allow_delete”:false}

9、通过logstash向ES写入日志报错:”type”=>”illegal_state_exception”, “reason”=>”Can’t get text on a START_OBJECT

原因是es对字段解析错误,类型是字符串(keyword或者text)的字段接收到的值为对象(如json对象),出现这种情况,可以在logstash 配置文件中使用json_encode filter plugin对原来是对象的字段转换为字符串。

10、查询dsl中使用了min_score参数用来限定返回文档的得分,为什么多次执行hit的文档数量不一致?

主分片和副本分片的差异导致的,底层segment可能不一致,特别是在有文档被删除的情况下主分片和副本分片的segment中标记删除的文档的数量可能会不一致(比如主分片进行了merge, 副本分片没有进行merge),导致最终主分片和副本分片上文档的得分不同,影响了最终的查询结果;可以在查询时指定preference=_primary指定只查询主分片解决,或者自定义preference解决

11、completion suggester自动补全功能,在添加seggest inputs时指定了多个词,为什么查询时options只返回一项?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
创建索引并添加doc:

curl -XPUT "http://localhost:10001/music" -H 'Content-Type: application/json' -d'
{
"mappings": {
"_doc":{
"properties" : {
"suggest" : {
"type" : "completion"
},
"title" : {
"type": "keyword"
}
}
}
}}'

curl -XPUT "http://localhost:10001/music/_doc/7?refresh" -H 'Content-Type: application/json' -d'
{
"suggest" : {
"input": [ "bat", "bar"]
}
}'

查询:

curl -XPOST "http://localhost:10001/music/_search?pretty" -H 'Content-Type: application/json' -d'
{
"suggest": {
"song-suggest" : {
"prefix" : "ba",
"completion" : {
"field" : "suggest"
}
}
}
}'
结果:

...
"options": [
{
"text": "bar",
"_index": "music",
"_type": "_doc",
"_id": "7",
"_score": 1,
"_source": {
"suggest": {
"input": [
"bat",
"bar"
]
}
}
}
]
......

completion suggester返回的options是文档级别的,查询命中后就会提前终止,所以只能返回第一个匹配到的值;解决方法是bat, bar两个词分别放在两个doc中

12、2核4G 3节点的集群,在保证集群性能稳定的情况下,最多可以支持多少shard?

每个节点上可以存储的分片数量与可用的堆内存大小成正比关系,分片数量越多,分片的元数据占用的内存越多,一般情况下,1GB的堆内存对应分片数量不超过20

13、ES报这个错误是什么原因

报错:”caused_by”=>{“type”=>”max_bytes_length_exceeded_exception”, “reason”=>”max_bytes_length_exceeded_exception: bytes can be at most 32766 in length; got 33023”}?

原因是索引中有字段为keyword类型,但是写入时的该字段的数据长度超过了keyword类型的最大长度32766个字节,可以把该字段的类型设置为text解决。

14、使用filebeat收集日志写入ES中,报错如下:

报错:”Bulk item insert failed (i=0, status=500): {“type”:”string_index_out_of_bounds_exception”,”reason”:”String index out of range: 0”}”

当filebeat的output.elasticsearch.index配置项有取自上游event中的某个字段,而某个event中该字段不存在时,想ES写入数据会报错。索引的名称如果要从event中的某个字段获取,需要确保该字段一定会存在。

15、ES的_refresh和_flush操作的区别是什么

refresh调用了lucene DirectoryReader的 openIfChanged()方法,相当于重新打开了indexReader, 使得写入的数据都可以被搜索到;flush操作调用了luecene IndexWriter的commit()方法,把仍在在文件系统缓存中的segment写入到磁盘中,实现了数据的真正持久化,flush同时还会触发refresh操作。

16、在ES的script query中,使用doc[‘my_field’].value和 params[‘_source’][‘my_field’],两种方式有什么不同?

使用doc[]的方式会把字段的所有terms都加载进内存并且会被缓存,因此比较消耗内存,同时doc[]的方式只支持单值字段;使用_source的方式terms不会被缓存,相比doc[]的方式较慢。

17、ES的删除操作不是真正的删除,那通过什么方式可以获取到准确的文档数量?

通过_cat/count API获取集群中所有文档数量,不包含已删除的文档;通过{index}/_stats API获取索引中的文档数量,结果中docs.count值为除了标记删除文档之外的总的文档数,docs.deleted为标记删除的文档数。

18、ES的_search API,当不指定索引时是会向集群中所有的索引发起查询请求吗?

是默认行为,最好通过指定索引名称或者对多个索引设置别名后进行查询,可以减少不必要的开销;5.6版本以后增加了分片预过滤功能,当要查询的分片数量超过128并且查询可能会被重写为MatchNoneQuery时,会进行过滤,过滤掉不需要的shard,_search API的返回结果中的_shards.skipped表示了过滤掉了多少shard。

19、如果使用scroll批量获取查询结果,ES执行该查询时还会使用node query cache或者shard request cache吗?

scroll请求不会用到cache,因为使用cache在查询请求执行过程中会修改search context,会破坏掉scroll的context。

20、如何实现字符串的前后通配符匹配,比如输入bc,想查询出abc, abcd, bcd, 但是不能查询出abdc?

使用query_string查询:

1
2
3
4
5
6
7
{
"query":{
"query_string":{
"query":"field:*ab*"
}
}
}

21、使用query_string查询指定的整型字段,查询的值为正数时正常,值为负数时抛异常,如“status:-1”时会报错

需要把负号转义(“status:-1”)或者对负数加引号, 否则会认为负号是一个操作符而解析失败。

22、在kibana中要对long型的字段进行聚合,但是提示不支持

如果索引是按天创建的,并且一个index pattern下包含多个索引,检查下索引的mapping,是否之前的索引中字段类型是否是字符串类型,如果不同索引中该字段的类型不一致,则不能对该字段进行聚合。https://github.com/elastic/kibana/issues/3451

23、通过bulk api向es写数据时,报错如下:

报错:failed to execute bulk item (index) BulkShardRequest …source[n/a, actual length: [4.5kb], max length: 2kb]}], 是字段超过了最大长度2kb的限制吗?

日志中打出的max length不是字段的最大长度的意思,而是超过了2kb就不把具体的doc信息在日志中打出,bulk失败的信息需要查看日志中后面的异常信息,一般是字段解析失败或者是bulk队列满导致的。

24、索引中有一个字符串型的字段,需要在kibana中这个字段求平均值并且展示出来,该怎么实现呢?

参考https://www.elastic.co/guide/en/kibana/current/scripted-fields.html, 使用script fields。

25、filebeat 7.x版本,在配置文件中定义了index名称,为什么写入到es中仍然生成的是filebeat-*之类的索引?

通过配置setup.ilm.enabled: false解决。索引生命周期管理ilm功能默认开启,开启的情况下索引名称只能为filebeat-*, 通过setup.ilm.enabled: false进行关闭;如果要使用自定义的索引名称,同时又需要启用ilm,可以修改filebeat的模板。

26、使用filebeat+es+kibana收集容器中的nginx日志,但是发现kibana中的message是一整段,字段都放在一起了,这个该怎么处理?

两种解决办法:

  1. nginx日志配置为json格式, filebeat配置文件中指定解析json格式的日志json.keys_under_root: true
  2. 仍然使用默认的nginx日志格式,在es中自定义ingest pipeline解析每个字段,filebeat配置文件中指定使用定义好的pipeline

27、ES的fielddata和docvalues有什么区别呢?

fielddata是在堆内存的,docvalues是在堆外内存的;docvalues默认对所有not_analyzed字段开启(index时生成),如果要对analyzed字段进行聚合,就要使用fielddata了(使用时把所有的数据全都加载进内存);如果不需要对analyzed字段进行聚合,就可以降低堆内存,Elasticsearch(更快的 GC)和 Lucene(更多的内存用于缓存)的性能越好

28、ES的ik-analyzer分词插件默认会把英文大写字母转换为小写,但是业务查询时是对大小写敏感的,怎么可以做到使用ik-analyzer分词插件的同时禁止把大写字母转换为小写?

可以基于ik_smart分词类型自定义analyzer, 同时配置参数 “enable_lowercase”: false:

1
2
3
4
5
6
7
8
9
10
11
12
{
"settings": {
"analysis": {
"analyzer": {
"my_ik": {
"type": "ik_smart",
"enable_lowercase": false
}
}
}
}
}

29、业务日志按文件大小滚动(最大100MB,滚动后压缩),使用filbeat收集日志时,发现在日志滚动较快的情况下filebeat没有释放掉已经被删除掉的日志文件,导致磁盘使用率较高,这个怎么解决?

可以通过配置close_timeout参数释放掉已经删除文件的文件句柄。

30、使用filebeat 5.6.4收集nginx日志文件(按天滚动,滚动后压缩),发现filebeat data目录下registry文件越来越大,并没有清理掉很早日志文件的state信息,这个怎么解决?

通过配置ignore_older和clean_inactive两个参数,清理掉registry中无用的文件state信息。

31、es集群是yellow,分片完成度不是100%的问题

过多的复本数配置会延长分片完成时间,可以根据业务情况考虑减少复本数,以下仅供参考:

1
curl -XPUT localhost:9200/.kibana/_settings -H “Content-Type:application/json” -d {“index.number_of_replicas”:0}
-------------本文结束感谢您的阅读-------------