场景

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

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. Moonraker

    Moonraker 目录 Moonraker 1 信息收集 1.1 端口扫描 1.2 后台目录扫描 1.3 目录分析 1.3.1 /services/ 1.3.2 /svc-inq/salesmoon ...

  2. The size of the request headers is too long.

    当 IIS7/7.5 收到的请求头的长度超过16K(默认值),就会引发"Bad Request - Request Too Long. HTTP Error 400. The size of ...

  3. Solon v2.2.1 发布。向 Graalvm Native 友好靠近

    本次更新最重要的是增加了 Solon APT 项目,为更简单的完成 Graalvm Native 打包提供了帮助:其次是增加了 @ProxyComponent 和 @SolonMain 注解:以及优化 ...

  4. Django中获取用户IP方法

    Django中通过request.META可以来获取用户的IP. request.META 是一个Python字典,包含了所有本次HTTP请求的Header信息,比如用户IP地址和用户Agent(通常 ...

  5. SqlServer取分组第一条数据

    SELECT * FROM (SELECT t.*, RANK() OVER (PARTITION BY t.a ORDER BY t.b DESC) AS drank FROM table1 t) ...

  6. IntelliJ IDEA运行项目的时候提示 Command line is too long 错误

    这时候你需要调整运行项目的配置,将 Configuration 中的 Shorten Command Line 修改为 JAR 就可以了.

  7. 阿里云服务器ECS(Centos8)下安装和配置python3.8

    1.安装依赖包: yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-dev ...

  8. pdf中添加二维码无法扫描解决方案

    正常行驶的bitmap类型的二维码格式,加载到PDF中,将会导致二维码失真,无法扫描. 矢量图可根据尺寸大小进行调节,不会出现失真模糊情况 所用依赖 <PackageReference Incl ...

  9. windows下搭建h5游戏小小火影教程【附安装包】

    小小三国是一款很好玩的H5游戏.只需要用一台64位的windows电脑或服务器,你就可以和朋友们一起联机玩这个游戏了.本文将会教你如何搭建这个游戏,步骤如下. 1.下载好后你会看到该压缩包 2.解压到 ...

  10. win10多用户

    到https://github.com/stascorp/rdpwrap/releases下载 最新版文件(已经停更了,最新的版本是 v1.6.2) 下载后解压到 C:\Program Files\R ...