Elasticsearch中的腳本(script)有什麼作用? 如何創建、搜索、使用腳本? 腳本的緩存又是什麼? 對於腳本的使用, 有哪些高效的實踐策略? 本篇博文對這些內容作個簡單的探討. ...
目錄
本文以 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
關鍵字, 從文檔中訪問相關欄位的值, 這種方式更加高效.
參考資料
版權聲明