好久沒更新了 今天講下如何借助ES的flatten數據類型解決字段膨脹的問題
背景介紹
目前商城業(yè)務中需要使用ES完成一項特殊的排序邏輯:任意一件商品可以被運營劃分到多個品類下,當遇到節(jié)假日或者熱點時,運營同學需要干預商品排序,即在某些品類下的商品可以按照特定字段值進行排序,沒有設置運營排序值的則需要按照綜合分進行排序
問題描述
我們知道ES并不擅長做數據關聯,那么如何實現一件商品可以按照不同的品類權重值進行排序呢?
解決思路
這里想到的是借助ES的嵌套結構來實現,即每一個嵌套子文檔都是一個k/v結構,key為品類id,value為排序值,如下:
"navigator_sort" : {
"895729" : 0,
"895839" : 0 ,
"895567" : 100,
"898804" : 0,
"898805" : 0,
"895566" : 75
}
如果需要按照指定的品類進行排序,那么可以這么做:
"sort": [
{
"navigator_sort.895567": {
"order": "desc"
}
}
]
面臨的問題
默認情況下,Elasticsearch 會在提取nested字段中內容時,會自動映射文檔中包含的字段。 雖然這是使用 Elasticsearch 的最簡單方法,但隨著時間的推移,它往往會導致字段爆炸,并且 Elasticsearch 的性能將受到 “內存不足(out of memory)” 錯誤以及索引和查詢數據時性能不佳的影響。
什么是字段爆炸?
Elasticsearch 必須為每個新字段更新集群狀態(tài),并且該集群狀態(tài)必須傳遞給所有節(jié)點。跨節(jié)點的集群狀態(tài)傳輸是單線程操作 , 因此要更新的字段映射越多,完成更新所需的時間就越長。這種延遲通常以性能不佳的集群而結束,有時會導致整個集群停機。這被稱為 “映射爆炸(mapping explosion)”。
這也是 Elasticsearch 從 5.x 及更高版本開始將索引中的字段數限制為 1,000 個的原因之一。如果我們的字段數超過 1,000,我們必須手動更改默認索引字段限制(使用 index.mapping.total_fields.limit 設置)或者我們需要重新考慮我們的架構。
替代方案 flattened
使用 Elasticsearch Flattened數據類型,具有大量嵌套字段的對象被視為單個關鍵字字段。 換句話說,Elasticsearch 扁平化數據類型用于有效減少Mapping中包含的字段數量,同時仍允許我們查詢扁平化數據
# 構建索引
PUT my-index-000001
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"create_user": {
"type": "keyword"
},
"full_path_json": {
"type": "flattened",
"similarity": "boolean"
}
}
}
}
# 保存一條這樣的數據
{
"title" : "Something really urgent",
"create_user" : "1",
"full_path_json" : [
{
"op_user" : "A",
"folders" : [
"v3",
"v4",
"v3tv4"
],
"A" : {
"op_time" : "2022-01-12",
"is_share" : 1,
"in_folder" : 1
}
},
{
"op_user" : "2",
"2" : {
"op_time" : "2022-12-12",
"is_share" : 1
}
}
]
}
# 查詢和排序
GET my-index-000001/_search
{
"sort": [
{
"full_path_json.A.op_time": {
"order": "desc"
}
}
],
"query": {
"bool": {
"filter": [
{
"term": {
"title": "urgent"
}
},
{
"term": {
"full_path_json.folders": "f3"
}
}
]
}
}
}
雖然可以查詢在單個字段中 “扁平化” 的嵌套字段,但需要注意某些限制。 扁平對象中的所有字段值都存儲為keyword 【 keyword類型字段不進行任何類型的文本分詞(text tokenization)或分析,而是按原樣存儲】
失去的特性:
1、失去了使用不區(qū)分大小寫的查詢的能力,這樣你就不必輸入完全匹配的查詢
2、排序僅支持按照字符大小進行排序