场景

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

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;
}
}

继续在源码里搜索可以发现,该值经历了一个计算得到

    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,根据服务器配置做调整,一般建议为服务器内存的一半,并且建议Xms与Xmx大小一致。 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的更多相关文章

  1. ELK之kibana的web报错[request] Data too large, data for [<agg [2]>] would be larger than limit of

    http://blog.51cto.com/11819159/1926411 ELK架构:elasticsearch+kibana+filebeat 版本信息: elasticsearch 5.2.1 ...

  2. 【Linux】【Kibana】解决Kibana启动失败:Data too large问题

    今天重启Kibana容器,结果启动不了,一看日志发现是Data数据量太大报错. FATAL [circuit_breaking_exception] [parent] Data too large, ...

  3. fastboot 刷system.img 提示 sending 'system' (*KB)... FAILED (remote: data too large)

    华为G6-C00卡刷提示OEMSBL错误,只能线刷 ,但是官方找不到线刷img镜像,无奈 网上下了个可以线刷的工具套件 流氓ROM . 使用HuaweiUpdateExtractor(工具百度)把官方 ...

  4. Working with large data sets in MySQL

    What does working with large data sets in mySQL teach you ? Of course you have to learn a lot about ...

  5. Result window is too large, from + size must be less than or equal to: [10000] but was [78440]. See the scroll api for a more efficient way to request large data sets

    {"error":{"root_cause":[{"type":"query_phase_execution_exception& ...

  6. Datasets for Data Mining and Data Science

    https://github.com/mattbane/RecommenderSystem http://grouplens.org/datasets/movielens/ KDDCUP-2012官网 ...

  7. Data Management and Data Management Tools

    Data Management ObjectivesBy the end o this module, you should understand the fundamentals of data m ...

  8. 【转】浏览器中的data类型的Url格式,data:image/png,data:image/jpeg!

    所谓"data"类型的Url格式,是在RFC2397中 提出的,目的对于一些"小"的数据,可以在网页中直接嵌入,而不是从外部文件载入.例如对于img这个Tag, ...

  9. SQL data reader reading data performance test

    /*Author: Jiangong SUN*/ As I've manipulated a lot of data using SQL data reader in recent project. ...

  10. 初探 spring data(一)--- spring data 概述

    由于自己一个项目要用多到Sql与NoSql两种截然不同的数据结构,但在编程上我希望统一接口API,让不同类型的数据库能在相同的编程接口模式下运作.于是找了一个spring的官网,发现一个spring ...

随机推荐

  1. 毕设进度更新(真的不知道自己做到哪- - 备忘录性质)+3.19是mavan配置的常见问题

    3.19 maven的配置 我也不知道我的cmd 输入mvn complie就是报错 也没办法下载 奇了怪了 检查了setting文件也没得- - 然后也没办法像老师一样直接导入tomcat的包 但是 ...

  2. CNCF社区首个!KubeEdge达到软件供应链SLSA L3等级

    摘要:在v1.13.0版本中,KubeEdge项目已达到 SLSAL3等级(包括二进制和容器镜像构件),成为CNCF社区首个达到SLSA L3等级的项目. 本文分享自华为云社区<CNCF社区首个 ...

  3. 【OpenWrt】N1刷机过程及旁路由设置(通用)

    最近买了个N1盒子,从源系统刷到OpenWrt,总结一下整体过程,大部分都来源于网络教程和自己实践,如有错误欢迎指出! N1盒子刷机 固件准备 最著名的应该是F大的吧,原贴地址:[2023-2-14] ...

  4. 01Python变量的使用

    Python变量 变量的定义 变量:在程序运行过程中,值会发生变化的量 把程序运算的中间结果临时存到内存里,以备后面的代码继续调用,这几个名字的学名就叫做"变量". 变量的作用 我 ...

  5. JZOJ 3527.迷宫花坛(garden)

    题面 思路 考场想到 \(tarjan\) 缩点 然而忘了缩点怎么打 于是甩了个暴力 改题时学了个圆方树 发现挺好用 于是······注意重边 \(Code\) #include<cstdio& ...

  6. Java面向对象进阶第一天

    面向对象高级第一天 static关键字 是静态的意思,可以修饰成员变量,也可以修饰成员方法 成员变量的分类 静态成员变量 有static修饰,属于类,与类一起加载,内存中只有一份,可以被共享访问. 什 ...

  7. php .inc 文件

    inc 文件顾名思义是include file的意思.即PHP的包含文件,这里用后缀来表示文件的作用, inc文件一般加载一些设置 举个例子   <? php//这里是数据库连接的配置信息db. ...

  8. LeetCode-622 设计循环队列

    来源:力扣(LeetCode)链接:https://leetcode.cn/problems/design-circular-queue 题目描述 设计你的循环队列实现. 循环队列是一种线性数据结构, ...

  9. Unity的超大开放世界解决方案

    https://blog.csdn.net/u011105442/article/details/104614043

  10. ftpget

    Usage: ftpget [OPTIONS] HOST [LOCAL_FILE] REMOTE_FILE ftpget -u user -p passwd ip LOCAL_FILE REMOTE_ ...