ES实战- data too large, data for

这篇具有很好参考价值的文章主要介绍了ES实战- data too large, data for。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

场景

客户现场业务系统突然查询不到数据,个人一开始分析以为是聚合查询报错,于是去看了下系统日志,看到如下日志打印:

Caused by: ElasticsearchStatusException[Elasticsearch exception
[type=circuit_breaking_exception, reason=[parent] Data too large, 
data for [<http_request>] would be [1032639682/984.8mb], 
which is larger than the
limit of [1032637056/972.7mb], 
real usage: [1032637056/984. 7mb], 
new bytes reserved: [2626/2.5kb], 
usages [request=72/72b, fielddata=0/0b, in_flight_requests=2626/2.5kb, accounting=6830904/6. 5mb]
] 

尝试重启ES后系统可以恢复正常,但是运行一段时间后又会再次上报这个Data too large的错误。

异常原因

最终定位结论:请求数据量太大,内存使用达到设定的极限值,触发了es熔断请求,一旦某个内存使用达到设定的内存限值,
则触发熔断,不再响应任何请求。报错信息中的“Elasticsearch exception [type=circuit_breaking_exception”表明是
触发了熔断器导致报错,且日志显示实际使用量[1032637056/984. 7mb]已经超过了限制[1032637056/972.7mb],故触发熔断机制。

报错分析

PS:es版本7.10
分析报错,看日志Caused by: ElasticsearchStatusException[Elasticsearch exception这段,显然是es内部报的错,那还等啥,解铃还须系铃人,直接去撸es源码去瞧瞧到底咋引发的呗。

首先,异常抛出异常类是ElasticsearchStatusException,那么先看它:
public class ElasticsearchStatusException extends ElasticsearchException {
 、、、//此处源码无关紧要,咱不看
}
可以看到,这个异常是继承了ElasticsearchException异常的,那么我们就去找他爹看看
public class ElasticsearchException extends RuntimeException implements ToXContentFragment, Writeable {
、、、//略去无关源码
//ps:我们就事论事,在开篇,我们的异常日志打印输出了“ElasticsearchStatusException[Elasticsearch exception[ ”我们就在这个代码里用这个搜一下,发现如下源码:
static String buildMessage(String type, String reason, String stack) {
        StringBuilder message = new StringBuilder("Elasticsearch exception [");
        message.append("type").append('=').append(type).append(", ");
        message.append("reason").append('=').append(reason);
        if (stack != null) {
            message.append(", ").append("stack_trace").append('=').append(stack);
        }
        message.append(']');
        return message.toString();
    }
、、、//略去无关源码
}

仔细比对下报错输出格式就会发现,确实就是这个方法打印输出的。那么看下源码打印方法的这一行

  message.append("type").append('=').append(type).append(", ");

对应我们报错里就是type=circuit_breaking_exception,显然,这就是报错的错误类型,有了这个,我们就可以有的放矢,具体分析了。这个type啥意思,我们直接翻译就是电路中断异常,那么用行话来说就是触发了熔断机制。在ElasticsearchException类中可以找到这个异常的枚举:

 private static enum ElasticsearchExceptionHandle {
     、、、//略去无关源码
 CIRCUIT_BREAKING_EXCEPTION(CircuitBreakingException.class, CircuitBreakingException::new, 133, ElasticsearchException.UNKNOWN_VERSION_ADDED),
     、、、//略去无关源码
 }

既然知道了错误结果,那么我们反过来,顺藤摸瓜就行了呀,谁会抛出这个异常呢? 继续在源码里搜索抛出此异常的代码 “throws CircuitBreakingException”,结果还真有发现,报错类位置org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService,具体代码如下

  public void checkParentLimit(long newBytesReserved, String label) throws CircuitBreakingException {
        HierarchyCircuitBreakerService.MemoryUsage memoryUsed = this.memoryUsed(newBytesReserved);
        long parentLimit = this.parentSettings.getLimit();
        if (memoryUsed.totalUsage > parentLimit && this.overLimitStrategy.overLimit(memoryUsed).totalUsage > parentLimit) {
            this.parentTripCount.incrementAndGet();
            StringBuilder message = new StringBuilder("[parent] Data too large, data for [" + label + "] would be [" + memoryUsed.totalUsage + "/" + new ByteSizeValue(memoryUsed.totalUsage) + "], which is larger than the limit of [" + parentLimit + "/" + new ByteSizeValue(parentLimit) + "]");
            if (this.trackRealMemoryUsage) {
                long realUsage = memoryUsed.baseUsage;
                message.append(", real usage: [");
                message.append(realUsage);
                message.append("/");
                message.append(new ByteSizeValue(realUsage));
                message.append("], new bytes reserved: [");
                message.append(newBytesReserved);
                message.append("/");
                message.append(new ByteSizeValue(newBytesReserved));
                message.append("]");
            }
            message.append(", usages [");
            message.append((String)this.breakers.entrySet().stream().map((e) -> {
                CircuitBreaker breaker = (CircuitBreaker)e.getValue();
                long breakerUsed = (long)((double)breaker.getUsed() * breaker.getOverhead());
                return (String)e.getKey() + "=" + breakerUsed + "/" + new ByteSizeValue(breakerUsed);
            }).collect(Collectors.joining(", ")));
            message.append("]");
            Durability durability = memoryUsed.transientChildUsage >= memoryUsed.permanentChildUsage ? Durability.TRANSIENT : Durability.PERMANENT;
            logger.debug(() -> {
                return new ParameterizedMessage("{}", message.toString());
            });
            throw new CircuitBreakingException(message.toString(), memoryUsed.totalUsage, parentLimit, durability);
        }
    }

嚯,这不就是拼接的我们的报错日志吗?那么什么条件下才会进入这个拼接逻辑呢,继续看上面的源码,可以看到:

if (memoryUsed.totalUsage > parentLimit && this.overLimitStrategy.overLimit(memoryUsed).totalUsage > parentLimit){
    ```//拼接错误信息
}

从代码可以看出,当memoryUsed.totalUsage > parentLimit时且this.overLimitStrategy.overLimit(memoryUsed).totalUsage > parentLimit才会出现熔断;
哦豁,这一堆变量比来比去,都是啥意思啊,别急,我们继续分析,慢慢拨开云雾,首先看parentLimit,都在和它比,先看看它到底是个什么玩意。

看代码:long parentLimit = this.parentSettings.getLimit();
原来是从this.parentSettings里取得值,那就直捣黄龙,看它是怎么怎么赋值的。
在HierarchyCircuitBreakerService这个类中搜this.parentSettings初始化的地方,得到:
 this.parentSettings = new BreakerSettings("parent", ((ByteSizeValue)TOTAL_CIRCUIT_BREAKER_LIMIT_SETTING.get(settings)).getBytes(), 1.0D, Type.PARENT, (Durability)null);
logger.trace(() -> {
    return new ParameterizedMessage("parent circuit breaker with settings {}", this.parentSettings);
        });
看到它的值是一个BreakerSettings对象,它new了一个BreakerSettings对象,来吧,继续,看下这个对象的构造函数:
    public BreakerSettings(String name, long limitBytes, double overhead, Type type, Durability durability) {
        this.name = name;
        this.limitBytes = limitBytes;
        this.overhead = overhead;
        this.type = type;
        this.durability = durability;
    }
调用这个构造函数发现就第二个值它是比较特殊的,其他的参数都是固定值,那么我们就干它,TOTAL_CIRCUIT_BREAKER_LIMIT_SETTING,看它又是在哪赋值,还是在HierarchyCircuitBreakerService类中,搜出TOTAL_CIRCUIT_BREAKER_LIMIT_SETTING赋值之处,发现在一个静态代码块里,原来类加载时它就已经赋值了。
 static {
 USE_REAL_MEMORY_USAGE_SETTING = Setting.boolSetting("indices.breaker.total.use_real_memory", true, new Property[]{Property.NodeScope});
 
  TOTAL_CIRCUIT_BREAKER_LIMIT_SETTING = Setting.memorySizeSetting("indices.breaker.total.limit", (settings) -> {
   return (Boolean)USE_REAL_MEMORY_USAGE_SETTING.get(settings) ? "95%" : "70%";},
   new Property[]{Property.Dynamic, Property.NodeScope});
        、、、//略去无关源码
    }
至此,这个参数怎么来的可以知道了,parentLimit的值与配置indices.breaker.total.limit(默认值为95%或者70%),至于是95%还是70%则与indices.breaker.total.use_real_memory(默认值为true)的配置有关。

parentLimit的值与配置indices.breaker.total.limit(默认值为95%或者70%)有关,它的默认值与indices.breaker.total.use_real_memory(默认值为true)的配置有关,如下代码所示:
再来看下和它做比较的memoryUsed.totalUsage这个参数,

HierarchyCircuitBreakerService.MemoryUsage memoryUsed = this.memoryUsed(newBytesReserved);
memoryUsed.totalUsage它也是从一个对象里取得一个属性值,MemoryUsage对象如下,作为了一个内部类出现:
    static class MemoryUsage {
        final long baseUsage;
        final long totalUsage;
        final long transientChildUsage;
        final long permanentChildUsage;
        MemoryUsage(long baseUsage, long totalUsage, long transientChildUsage, long permanentChildUsage) {
            this.baseUsage = baseUsage;
            this.totalUsage = totalUsage;
            this.transientChildUsage = transientChildUsage;
            this.permanentChildUsage = permanentChildUsage;
        }
    }

继续在源码里搜索可以发现,该值经历了一个计算得到文章来源地址https://www.toymoban.com/news/detail-437938.html

    private HierarchyCircuitBreakerService.MemoryUsage memoryUsed(long newBytesReserved) {
        long transientUsage = 0L;
        long permanentUsage = 0L;
        Iterator var7 = this.breakers.values().iterator();
        while(var7.hasNext()) {
            CircuitBreaker breaker = (CircuitBreaker)var7.next();
            long breakerUsed = (long)((double)breaker.getUsed() * breaker.getOverhead());
            if (breaker.getDurability() == Durability.TRANSIENT) {
                transientUsage += breakerUsed;
            } else if (breaker.getDurability() == Durability.PERMANENT) {
                permanentUsage += breakerUsed;
            }
        }
        long parentEstimated;
        if (this.trackRealMemoryUsage) {
            parentEstimated = this.currentMemoryUsage();
            return new HierarchyCircuitBreakerService.MemoryUsage(parentEstimated, parentEstimated + newBytesReserved, transientUsage, permanentUsage);
        } else {
            parentEstimated = transientUsage + permanentUsage;
            return new HierarchyCircuitBreakerService.MemoryUsage(parentEstimated, parentEstimated, transientUsage, permanentUsage);
        }
    }
代码中最后返回结果根据trackRealMemoryUsage的值进行了if判断,看下源码它在哪赋值:
  this.trackRealMemoryUsage = (Boolean)USE_REAL_MEMORY_USAGE_SETTING.get(settings);
可以看到它和USE_REAL_MEMORY_USAGE_SETTING取值有关。这个在刚才我们分析parentLimit已经看到过了,它也是在类加载时就已经初始化了,代码如下
static{
USE_REAL_MEMORY_USAGE_SETTING = Setting.boolSetting("indices.breaker.total.use_real_memory", true, new Property[]{Property.NodeScope});
 、、、//略去无关源码
}
也就是说trackRealMemoryUsage取值和配置indices.breaker.total.use_real_memory有关。
至此,这个参数的来源也搞清楚了,存在的疑惑大概就是上文中的配置项indices.breaker.total.limit,indices.breaker.total.use_real_memory,到底是啥意思了。
我也不知道这是啥玩意,于是就网上找资料了解了一下,
indices.breaker.total.limit  所有breaker使用的内存值,默认值为 JVM 堆内存的70%,当内存达到最高值时会触发内存回收。elasticsearch包含多个circuit breaker来避免操作的内存溢出。每个breaker都指定可以使用内存的限制。另外有一个父级breaker指定所有的breaker可以使用的总内存。
总熔断器
 indices.breaker.total.use_real_memory
 它的值直接影响JVM堆内存分配的大小,
 1、值为 true, indices.breaker.total.limit 为堆大小的 95%2、值为 false,indices.breaker.total.limit 为堆大小的70%
原来这玩意是elasticsearch断路器生效的参数配量项,关于ES断路器,在此不做详细介绍,Elasticsearch包含多个断路器,每个断路器指定它可以使用多少内存的限制.其功能就是用于防止操作导致OutOfMemoryError.

解决方案

1、调大ES JVM堆内存
ES默认是1g,根据服务器配置做调整,一般建议为服务器内存的一半,并且建议XmsXmx大小一致。
2、设置indices.fielddata.cache.size
如果服务器没有足够的内存可考虑此选项,有了这个设置,最久未使用(LRU)的 fielddata 会被回收为新数据腾出空间,在elasticsearch.yml中配置:indices.fielddata.cache.size:  40%
ps:这样的方式可以帮助你合理的分配你有限的内存,但是不能改变你的内存。所以终极解决办法还是增加你的内存大小。
通过postman或者其他接口测试工具更改:
PUT _cluster/settings
{
  "persistent" : {
    "indices.breaker.fielddata.limit" : "20%" 
  }
}

到了这里,关于ES实战- data too large, data for的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ES实战-result window is too large

    做分页查询,当分页达到一定量的时候,报如下错误: 原因分析: es对from + size的大小进行限制,必须小于等于10000。 方案一(有风险) 将max_result_window参数阈值调大,在业务中限制分页大小,使from+size=10000; 具体操作 改法一: 动态更改索引设置,为max_result_window参数赋值足够

    2024年03月15日
    浏览(61)
  • elasticsearch Data too large问题解决

    现象: 1、钉钉上收到es集群监控告警,集群变为yellow 2、kibana上没有数据,打开索引管理报以下错误: 解决: 1、修改config目录下jvm配置文件中关于jvm的参数 2、重启es集群 2.1 如果分片有副本的话,重启过程中,要临时关闭分片复制功能。 每个结点重启时,ElasticSearch集群的

    2024年02月07日
    浏览(38)
  • 【Elasticsearch】Elasticsearch中使用_id排序导致 data too large 问题

    Elasticsearch 一个弹性伸缩的搜索数据库,后文简称 :ES ,最近有一个ES 服务查询数据时候出现了数据 Hits 结果多次查询不一致的问题,而且这块代码已经很长时间没有修改,一直稳定运行了很长时间,用户翻译查询列表数据的时候又是出现1条,有时候出现2条或者3条。(再加上

    2024年02月03日
    浏览(36)
  • elasticsearch6.1.3查询只返回部分数据或报错Data too large

    1、ES导出报错 CircuitBreakingException[[FIELDDATA] Data too large, data for [proccessDate] would be larger than limit of [10307921510/9.5gb]] 2、ES导出丢失数据,只返回范围内部分数据,同时未报错 ES在查询时,会将索引数据缓存在内存(JVM)中。 当缓存数据到达驱逐线时,会自动驱逐掉部分数据,把缓

    2024年02月06日
    浏览(52)
  • 解决Elasticsearch exception [type=circuit_breaking_exception, reason=[parent] Data too large问题

    一、背景 公司有一批8万的数据存储在Mysql中,然后我使用多线程的方式调用Elasticsearch的bulk()方法推送到ES,但是在推送过程中出现了该问题,这属于插入数据时产生的问题 二、异常 三、解决办法 加大 -Xms 和 -Xmx 的值,比如 docker-compose.yaml 文件中可以这样设置: 四、解释

    2024年02月03日
    浏览(52)
  • 访问elasticsearch集群提示ERROR 新增es数据失败,后台日志返回413 Request Entity Too Larg

    在运维paas云平台时有研发反馈客户端访问elasticsearch服务偶发性的出现报错,提示报错如下: 报错日志内容核心点如下: 根据日志报错内容及研发反馈的信息得到如下有用信息: 1、连接elasticsearch服务并请求处理业务是偶发性的提示报错,也就是说明elasticsearch服务是正常的

    2024年04月17日
    浏览(49)
  • SQLSTATE[22001]: String data, right truncated: 1406 Data too long for column

    mysql 字段报错 原因是:插入字段长度超过设定的长度 解决方法 在my.ini里找到 查询是否生效 结果 修改 另外在数据库中把 content 字段加大

    2024年02月13日
    浏览(54)
  • 解决mysql报错:1406, Data too long for column(多种方案)

    传送 centosAI 适用于linux系统 一般1406, Data too long for column错误大部分会是因为某一列表的数据长度受限,最好的办法是进入数据库,找到表结构中对应的表头,修改数据库类型,这里以vod_first_letter为例,如果直接修改数据类型,可能会因为文本内容不兼容而导致失败,如将V

    2024年01月21日
    浏览(52)
  • Data too long for column ‘xxxx‘ at row 1 解决办法

    很简单的啦,往下看 Data too long for column ‘xxxx’ at row 1 第一种情况就是很普遍的,xxx字段长度不够 就是用Mybatis映射文件xml,字段匹配顺序错误即 原来很短的一个列,插入了很长的数据 就是数据库字符集的问题 重新设置字符串长度 仔细看一眼sql,就能排错 PS: 前端提出来

    2024年02月07日
    浏览(42)
  • Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535.

    新建表或者修改表varchar字段长度的时候,出现这个错误 大概意思就是行大小太大,不能超过65535 长度改为21842就正常了,这是为什么? 最终我们执行正确的SQL语句 这里的21842长度是怎么来的? 首先它是什么意思?表示21842字符 首先来了解几个规则,对我们的字符数有影响的

    2024年02月05日
    浏览(75)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包