ES 27 - Elasticsearch脚本的使用实践
本文以 es 6.6.0 版本为例进行演示.
1 关于脚本
es提供了脚本支持 —— 可以通过groovy外置脚本(已过时)、内置painless
脚本实现各种复杂操作.
—— painless有轻便之意, 使用时直接在语法中调用即可, 无需外置, 也就是不支持通过外部文件存储painless脚本并调用的方法.
// 向es中插入一条数据: put employee/developer/1 { "name": "shou feng", "age": 20, "salary": 10000 } // 通过get发送脚本, 允许为每个匹配的文档返回脚本评估(script evaluation)内容: get employee/_search { "script_fields": { "change_age_field": { // 该字段不存在 - script fields可以处理未存储的字段 "script": { "lang": "expression", "source": "doc['age'] * multiplier", // 获取age字段的值进行计算 "params": { "multiplier": 2 } } } } } // 响应结果为: { "took" : 1, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : 1.0, "hits" : [ { "_index" : "employee", "_type" : "developer", "_id" : "1", "_score" : 1.0, "fields" : { "change_age_field" : [ 40.0 ] } } ] } }
2 脚本使用的最佳实践
elasticsearch第一次加载一个新脚本时, 会将新脚本编译并存储在缓存中.
编译可能是一个繁重的过程, 如果需要将变量传递给脚本, 建议: 将它们作为命名参数传递给脚本本身(方式①), 而不是硬编码在脚本中(方式②).
(1) 方式① 参数传递:
"source": "doc['age'] * multiplier", "params": { "multiplier": 2 }
(2) 方式② 硬编码:
"source": "doc['age'] * 2"
(3) 优劣对比:
每次乘数改变时都必须重新编译第②个版本, 而第①个版本只编译一次.
如果短时间内编译过多的脚本, es将拒绝带有
circuit_breaking_exception
错误的新脚本.elasticsearch默认情况下, 每分钟最多编译15个内联脚本, 可以通过修改
script.max_compilations_rate
的值来更改此设置.
2.1 创建脚本并存储
可以使用_scripts
api 将脚本存储在集群状态中, 并从集群状态中检索脚本.
使用_scripts/{id}
的方式操作脚本, 具体步骤如下:
(1) 首先在集群状态中创建名为calculate-score的脚本:
post _scripts/calculate-score { "script": { "lang": "painless", "source": "math.log(_score * 2) + params.my_modifier" } }
(2) 检索存储的脚本:
get _scripts/calculate-score
(3) 通过脚本id使用已创建的脚本:
get _search { "query": { "script": { "script": { "id": "calculate-score", "params": { "my_modifier": 2 // 传递脚本所需的参数 } } } } }
(4) 删除脚本:
delete _scripts/calculate-score
2.2 脚本的缓存
默认情况下, 所有的脚本都会被缓存到es集群中, 因此只有当脚本被更新之后, es才会重新编译它们.
同样, 脚本没有过期时间的说法, 但可以使用script.cache.expire
设置更改过期时间.
也可以使用script.cache.max_size
配置此脚本缓存的大小, 默认缓存大小为100.
存储脚本的大小限制为65535字节, 可以通过 script.max_size_in_bytes
来更改, 但是如果脚本非常大, 就应该考虑相关脚本的实现引擎是否足够优秀.
2.3 script field - 脚本字段
脚本字段还可以通过访问_source
字段来提取文档的其他字段 —— 使用params ['_source']
提取要从中获取的内容.
比如访问
_source
元字段中message
字段的内容, 可以用:"script": "params['_source']['message']"
访问.
另外: 理解doc['my_field'].value
和params['_source']['my_field']
之间的区别非常重要:
① 使用doc关键字: 将导致该字段的术语加载到内存(缓存)中, 这样脚本的执行速度会更快, 但也会带来更多的内存消耗. 另外,
doc […]
符号只允许简单的值字段(不能从中返回json对象), 并且它只对非分析或基于单个术语的字段有意义.② 使用params关键字: 每次使用时都必须加载和解析
_source
, 这是非常缓慢的.建议: 使用
doc
关键字, 从文档中访问相关字段的值, 这种方式更加高效.
参考资料
版权声明
作者:
出处: 博客园
下一篇: Oracle数据的导入与导出
推荐阅读
-
ES 19 - Elasticsearch的检索语法(_search API的使用)
-
ES 27 - Elasticsearch脚本的使用实践
-
ElasticSearch 5.5.X function_score脚本的使用
-
ES 32 - Elasticsearch 数据建模的探索与实践
-
ES 12 - 配置使用Elasticsearch的动态映射(dynamic mapping)
-
ES 19 - Elasticsearch的检索语法(_search API的使用)
-
ES 27 - Elasticsearch脚本的使用实践
-
ES 32 - Elasticsearch 数据建模的探索与实践
-
【最佳实践】Elasticsearch Snapshot 备份的使用方法