如何避免Elasticsearch搜索模板动态脚本编译次数过多错误?
我用Mustache编写Elasticsearch搜索模板,根据不同参数构建动态查询。但频繁修改请求参数值时,会收到错误:[script] Too many dynamic script compilations within, max: [150/5m];。
我清楚参数值不变时ES会利用缓存避免重编译,但我的场景里每次请求参数(本地时间戳、可变距离、客户端随机种子等)均不同,无法复用缓存。目前只能调高集群设置的max_compilations_rate,但这会增加服务器负载。
我的脚本基于Elasticsearch 8.2,用于计算多参数相关的评分,整体结构如下:
{ "script": { "lang": "mustache", "source": "...", "params": { ... } } }
模板源码结构:
{ "runtime_mappings": { "is_opened": { "type": "long", "script": { "source": " ... " } } {{#user_location}} ,"distance": { "type": "long", "script": { "source": " ... " } } {{/user_location}} }, "query": { "script_score": { "query": { ... }, "script": { "source": " ... " } } }, "fields": [ "is_opened" {{#user_location}},"distance"{{/user_location}} ], ... }
希望优化内部脚本,避免参数值变化时每次都触发重新编译,有没有可行的方法?
优化方案
1. 剥离脚本源码中的动态值,改用ES脚本参数传递
当前你的Mustache模板可能直接将参数值嵌入脚本source(比如"source": "doc['timestamp'].value > {{local_timestamp}}"),这种写法会导致参数变化时生成全新的脚本源码,触发重编译。
正确做法是:在Mustache模板中定义ES脚本的params字段,将动态值作为参数传入,保持脚本source固定。示例:
// runtime_mappings中的distance字段优化示例 "distance": { "type": "long", "script": { "source": "doc['location'].arcDistance(params.user_lat, params.user_lon)", "params": { "user_lat": {{user_location.lat}}, "user_lon": {{user_location.lon}} } } }
这种方式下,脚本source是固定不变的,ES只会编译一次并缓存,后续请求仅传递参数值即可,不会触发重编译。
2. 禁止Mustache动态生成不同的脚本源码片段
模板中用{{#user_location}}动态添加runtime mapping的做法本身没问题,但要避免让Mustache根据参数值生成不同的脚本source文本。比如不要根据参数拼接不同的运算逻辑,而是让脚本结构固定,用参数控制逻辑分支。
错误示例(会生成不同脚本源码):
{{#if distance_threshold > 1000}} "source": "doc['distance'].value * 0.5" {{else}} "source": "doc['distance'].value * 1.0" {{/if}}
正确示例(脚本源码固定,用参数判断逻辑):
"source": "doc['distance'].value * (params.distance_threshold > 1000 ? 0.5 : 1.0)", "params": { "distance_threshold": {{distance_threshold}} }
3. 预编译存储脚本(可选)
对于复杂的评分脚本,可以提前将其存储为集群级别的存储脚本,在Mustache模板中直接引用脚本ID,而非每次传递source。ES会提前编译该脚本,后续请求仅需传递参数。
第一步:创建存储脚本
PUT _scripts/rating_calculator { "script": { "lang": "painless", "source": "params.random_seed * doc['relevance'].value + doc['distance'].value / params.max_distance" } }
第二步:在Mustache模板中引用
"script_score": { "query": { ... }, "script": { "id": "rating_calculator", "params": { "random_seed": {{random_seed}}, "max_distance": {{max_distance}} } } }
4. 确保runtime mappings脚本的复用性
对于is_opened这类固定的runtime字段,确保其脚本source完全固定,不要嵌入动态参数。若需根据参数调整逻辑,同样通过params传递:
"is_opened": { "type": "long", "script": { "source": "doc['open_time'].value <= params.current_timestamp && doc['close_time'].value >= params.current_timestamp ? 1 : 0", "params": { "current_timestamp": {{local_timestamp}} } } }
内容的提问来源于stack exchange,提问作者Ricou13

