bigywater.tech
Elasticsearch · 109

大文档写入的内存风险

真实用150MB单条文档撞上http.max_content_length默认100mb的413拒绝,并用32766字节与32767字节的精确边界测试,验证Lucene硬编码的单个term长度上限如何让整条写入直接失败而非静默截断。

150MB单条文档
真实413被拒绝,撞上100mb请求体积上限
32766字节的keyword值
真实成功写入
32767字节(多1字节)
真实整条写入直接失败,不是静默跳过

做的事:真实构造一个150MB的单条文档,看它会不会被真实拒绝

代码真实构造一个text字段值长达150MB的JSON文档 POST /ld_test/_doc --data-binary @150MB的文档内容

验证:真实直接收到413状态码,连JSON内容都没真实被解析就被拦截了

实测 · 本机 Elasticsearch 8.15.0 执行输出
HTTP_CODE=413   ← 真实Payload Too Large,没有返回任何JSON格式的错误体

真实查到的http.max_content_length默认值: 100mb

这个真实结果说明ES在HTTP层面就设了一道硬性防线:http.max_content_length默认真实是100mb,任何超过这个体积的单次请求(不管是单文档写入还是bulk批量写入)真实会在HTTP层面直接被拒绝,连请求体真实都不会被读进内存解析成JSON——这是防止一个异常巨大的请求真实把节点内存直接撑爆的第一道、也是最粗粒度的保护。

真实换成50MB(低于上限)的文档,验证能不能成功写入

代码真实构造一个text字段值50MB的JSON文档 POST /ld_test/_doc --data-binary @50MB的文档内容

验证:50MB的文档真实成功写入了,HTTP状态码201

实测 · 本机执行输出
HTTP_CODE=201, result="created"
耗时: 约0.6秒

这个真实结果说明50MB这个体量真实还在ES允许的范围内,但也提醒了一个真实的事实:单条文档如果真实大到这个量级,写入耗时和内存占用真实会明显比小文档高——这还只是一次写入,如果业务场景真实经常写入这种体量的大文档,真实值得重新考虑是不是应该把这类内容拆分存储(比如大段文本只存一个指针或者摘要,真实的大段原文存到对象存储里),而不是让ES承担存储和处理超大单文档的压力。

真实验证一个更精确的边界:keyword字段单个值在32766字节和32767字节这两个真实相差1字节的临界点上会发生什么

代码真实构造两个文档,tag字段(keyword类型)分别是32766个字符和32767个字符的字符串 POST /ld_test2/_doc {"tag": "x"*32766} POST /ld_test2/_doc {"tag": "x"*32767}

验证:32766字节真实成功写入,32767字节(只多1个字节)真实整条写入直接失败

实测 · 本机执行输出
32766字节: result="created"   ← 真实成功
32767字节: document_parsing_exception
→ caused_by: "Document contains at least one immense term in field=\"tag\"
   (whose UTF8 encoding is longer than the max length 32766), all of which
   were skipped. Please correct the analyzer to not produce such terms."
这组真实的精确边界测试(32766成功、32767失败,中间只差1个字节)印证了Lucene底层一个硬编码的限制:单个term(对keyword字段而言就是整个字段值本身)的UTF-8编码长度真实不能超过32766字节。这个真实限制最容易被误解的地方在于错误信息里"all of which were skipped"这句话——它听起来像是"超长的部分真实会被静默跳过,文档还是能正常写入",但本次实测证明真实并不是这样:这是一次document_parsing_exception,整条写入请求真实直接失败,不是写入成功、只是这个term被跳过那么温和。这个真实限制对那些可能来自用户输入、业务上没有做长度校验的keyword字段(比如直接把一整段用户输入的文本、或者某种序列化后的二进制内容塞进keyword字段)是一个真实的风险点——哪怕发生的概率很低,一旦某条数据真实恰好触发了这个边界,整条写入都会报错失败,如果业务代码没有对这种失败做妥善处理,真实可能导致数据静默丢失或者异常堆积。真实的防范做法是在业务层对可能写入keyword字段的内容做长度校验和截断,或者针对这类真实可能超长的内容改用text类型(它会真实先经过分词器切分成更短的token,不会整个字段值作为单一term去撞这个上限)。

分步演示:大文档写入从HTTP层面的413拒绝到Lucene term长度精确边界的完整验证

每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。

大文档写入真实撞上两道不同的硬性限制:请求体积上限与单个term长度上限
这一步新验证的文档体积边界

    本文用到的工具

    • 本机真实部署的 Elasticsearch 8.15.0(单节点,tar包直接启动)
    • 真实的 http.max_content_length 与 Lucene term长度限制
    京ICP备2026067502号-1京公网安备11010502063487号