记录在使用ES的过程中Data too large的实战排错处理方式

2,552 阅读5分钟

本文正在参加「技术专题19期 漫谈数据库技术」活动

记录在使用ES的过程中遇到的一些问题常见问题的处理方式

项目中有用到ES并且在使用过程中常常因为一些原因遇到集群不可用的状况,这里记录下一些解决办法,做一个知识积累也方便后面查看。

Data too large问题描述

在日志中遇到错误信息 [parent] Data too large, data for [<transport_request>] would be [11968889573/11.1gb], which is larger than the limit of [11964848537/11.1gb], usages [request=0/0b, fielddata=8659917927/8gb, in_flight_requests=4749779/4.5mb, accounting=3304221867/3gb]通常情况通过ES API查看stats也能发现错误。

通过查看stats

GET /_stats/fielddata?fields=*
显示输出
{
  "_shards" : {
    "total" : 8906,
    "successful" : 8164,
    "failed" : 742,
    "failures" : [
      {
        "shard" : 0,
        "index" : "smart.monitor.2022.04.13",
        "status" : "INTERNAL_SERVER_ERROR",
        "reason" : {
          "type" : "failed_node_exception",
          "reason" : "Failed node [zKRmwmSdQuySvm10dTVjIQ]",
          "node_id" : "zKRmwmSdQuySvm10dTVjIQ",
          "caused_by" : {
            "type" : "circuit_breaking_exception",
            "reason" : "[parent] Data too large, data for [<transport_request>] would be [11964950036/11.1gb], which is larger than the limit of [11964848537/11.1gb], usages [request=0/0b, fielddata=9032984133/8.4gb, in_flight_requests=90543/88.4kb, accounting=2931875360/2.7gb]",
            "bytes_wanted" : 11964950036,
            "bytes_limit" : 11964848537
          }
        }
      },
      ...
      ]
    }
}    

日志中输出

["monitor"-57cb676788-lbns8] org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [11968889573/11.1gb], which is larger than the limit of [11964848537/11.1gb], usages [request=0/0b, fielddata=8659917927/8gb, in_flight_requests=4749779/4.5mb, accounting=3304221867/3gb]
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService.checkParentLimit(HierarchyCircuitBreakerService.java:262)
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.common.breaker.ChildMemoryCircuitBreaker.addEstimateBytesAndMaybeBreak(ChildMemoryCircuitBreaker.java:128)
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.transport.TcpTransport.handleRequest(TcpTransport.java:1036)
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.transport.TcpTransport.messageReceived(TcpTransport.java:932)
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.transport.TcpTransport.inboundMessage(TcpTransport.java:763)
["monitor"-57cb676788-lbns8] 	at org.elasticsearch.transport.netty4.Netty4MessageChannelHandler.channelRead(Netty4MessageChannelHandler.java:53)
["monitor"-57cb676788-lbns8] 	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
["monitor"-57cb676788-lbns8] 	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
["monitor"-57cb676788-lbns8] 	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
["monitor"-57cb676788-lbns8] 	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:323)
["monitor"-57cb676788-lbns8] 	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:297)

可以看到不管是通过API还是程序日志输出,其实都是提示发现是实际需要的容量超过limit了,可以看stats API返回的信息中"bytes_wanted" : 11964950036, "bytes_limit" : 11964848537。这个问题主要在于ES会将数据加载到内存中然后在进行处理,这里的问题主要在于ES的fielddata的内存被占满了,关于fielddata的解释可以参考官方文档

解决思路

  • 方法1:即然错误在说大小不够用,那么我来调整下limit,查看ES的circuit-breaker文档之后发现可以通过两个参数来控制fielddata的limit限制(但是这个方法用起来要慎重,我没有采用这个方式)。
PUT /_cluster/settings{
{
  "indices" : {
      "breaker" : {
        "fielddata" : {
          "limit" : "80%"
        },
        "total" : {
          "limit" : "80%"
        }
      }
    }
}
  • 方法2:内存不够用,那么清理点缓存(找到node节点上数据量大的点清理掉可以释放出JVM的内存)。
POST smartops.*.*.monitor.2022.04.0*/_cache/clear

并且在处理这个问题的时候,我也发现集群中有些节点的JVM使用率也很高,最高的达到了90%以上,其他几个节点也有80%以上的JVM使用率。 es内存使用率高- 清理索引缓存之前JVM.png 那么结合起来看,应该有两处问题

  • 是JVM使用率高过需要处理;
  • fielddata达到了内存限制,新的索引无发申请到空间报错(异常的表象);

处理问题

最终我选择先使用清理无效索引缓存的方式来处理这个问题了,之所以选择这个方式没有选择直接调整breaker.fielddata的大小,是考虑到JVM已经很高使用率,如果放大了限制将indices.breaker.fielddata.limit从默认JVM的60%调整到80%甚至90%,那么异常也可以解决,但是势必会更加加重JVM的压力,现在已经有节点90%了使用率了,那么可能会导致JVM撑爆可能问题会比现在更严重。

清理前节点压力 es内存使用率高- 清理索引缓存之前JVM.png 通过查看近期的索引发现,近期数据量增加导致JVM中加载数据量增大和fielddata压力过大 es内存使用率高- 大索引数据量的副本.png es内存使用率高- 清理索引缓存JVM.png 其实在我本次遇到的问题中,到这里问题已经解决了节点的JVM使用率也下去了,服务中也没有data too large的错误日志了。

总结

处理这次问题过程中,也找了相关的资料也看到很多资料都有说到调整indices.breaker.fielddata.limit,但是大家在处理实际问题过程中一定要全面考量,如果节点JVM压力没那么大那么通过可以调整参数来处理,在我的这次的场景中JVM本身的使用率也已经很高了,如果再调整limit,那么只会增加JVM压力,其实最后分析发现集群中的数据量因为进去客户增多导致压力变大,持久来看需要进行冷热数据处理,而不是完全照搬其他策略。

在想起之前看ES官方对于调优的资料中有一句话印象很深刻,ES可以处理很多事情并且功能很强大,但是对于大数据的处理和分析不同的场景需要结合实际情况来分析,不可以盲目的生搬硬套。