不必Reindex,利用runtime_fields优雅地解决字段类型错误问题
前言
作為程序員入職一家新公司,當你看到前任程序員寫的代碼的時候,你是不是經常有這樣的感覺:我屮艸芔茻!這代碼真他喵的爛!
問題
對于程序員來說,代碼水平良莠不齊比較常見的事情,之所以代碼質量不高,一方面原因是自身不太關注于代碼的整體管理,另一方面原因是經驗不足。實際上在處理公司業務的時候,這種前人挖坑,后人填坑的事情時有發生。
對,我們今天暫時不討論代碼規范的問題,而是說一說,再處理搜索業務的時候,如果你已經接手了一些無法描述的代碼,咱們怎么補救。
首先對于任何問題而言,預防問題發生永遠勝于問題發生之后再對其進行補救,這是一定的。不過既然問題已經發生,我們就只能以最小代價或者針對自身情況選擇處理方式,比如,我們可以選擇是先治標還是先治本,最主要區別在于見效速度。下面我們分別來說一下這兩種解決方案的區別和適用場景:
常見場景
場景一
字段類型錯誤:這種情況一般發生在項目初期對業務的預估錯誤或者技術負責人對ES本身不夠了解。
很多人都知道,為了優化性能,可以適當縮減字段屬性。比如確定不需要聚合或者排序的字段,可對其關閉正排索引,不需要對其進行檢索的字段可關閉倒排索引,不需要評分的字段可以關閉評分功能以此可節約字段存儲空間,提高效率。
另外一種情況是在創建索引的時候,對ES的字段本身不夠了解,比如之前我們提到的“yyyy-MM-dd HH:mm:ss”這種格式并非默認支持的時間類型,如果一開始技術員并不知道,而將數據直接寫入,有可能的結果就是,時間總段存儲成為了“text”類型,而給后期埋下隱患。
場景二
上述問題是字段類型錯誤,還有一種情況是針對于數據本身。比如:當我們某個索引報錯了不同來源或渠道的數據,每個渠道可能采集數據負責的程序員并不相同,最常見的情況比如,我們采集用戶行為日志,需要涉及“APP”、“Web”、“PC”、“小程序”等來源,可能負責開發的程序員分別來自不同的部門,如果在最開始沒有最好約定,可能就會出現字段或者單位不統一或者一些列其他問題。總之就是協調發生問題而導致數據不統一。這也給后期做數據分析或者統計造成很大影響。
案例
我們來看下面一個例子,創建以下映射并寫入一條數據:
PUT twitter {"mappings": {"properties": {"uid": {"enabled": false}}} } POST twitter/_doc {"uid": "1" }以上代碼為我們在項目初期,負責人對將來業務的預估是不會出現通過uid 進行檢索的情況,因此錯誤的把此字段的enabled 設置為了false, 此時數據將不能通過此字段進行檢索。注意是不能通過此字段檢索而不是不能將此字段檢索出來,只是沒有創建索引而非刪除了元數據。具體體現為:下面代碼第一行執行有數據,第二行無數據。
以下代碼執行有結果
# 有數據 GET twitter/_search以下代碼執行無結果
# 無數據 GET twitter/_search {"query": {"term": {"uid": {"value": 1}}} }痛點及現狀
后期經過業務的發展和迭代,逐漸發現了此字段無法檢索的問題,而很多公司的選擇都是Reindex,的確,重建索引可以解決很多問題,但是這樣未來過于“勞師動眾!”,因為基本上ES的應用場景都是基于海量數據的索引,重建索引可能耗費大量時間,無異于殺雞取卵,得不償失。
有沒有更好的解決方案 ?
那必須有啊!關注我就對了。
同類問題還包括如之前提到的,“yyyy-MM-dd HH:mm:ss”而導致的類型不匹配問題,在處理此一類問題時,皆可采用runtime_fields來解決。
案例:
我們沿用之前“yyyy-MM-dd HH:mm:ss 是時間類型?別再錯下去了!”提到的不是時間類型導致的錯誤的案例,如果沒看過之前的文章,戳:傳送門
假如生產環境我們有如下索引,存儲了一些地震的經緯度以及發生時間和描述等信息:
需求:
寫一個查詢滿足以下要求
- 1:按星期分桶統計地震數據
- 2:輸出星期一至星期日中平均地震等級 沒有數據的不顯示
- 3:返回平均地震等級最大的一個 是星期幾
- 4:進階問題 每個星期的平均地震等級
- 5:進階問題 平均地震等級最大的是哪個星期
這是我為Elastic認證考試出的一道模擬題,在此我們簡化題目,只看前兩個問題
但是,因為不可描述的原因,time字段有的數據存儲成為了yyyy-MM-dd HH:mm:ss,有的存儲成為了時間戳,最終time字段的類型并沒有像預想的那樣為date類型他們可能來自于同一個公司的不同部門,所以導致了這種問題。
面臨的問題
此時,面對上述需求,存在兩個問題
- time字段為text而非date類型,而導致我們無法使用日期時間類型的所有函數。
- 數據存儲的格式不統一,有時間戳,有"yyyy-MM-dd HH:mm:ss",無法統一計算。
解決方案:runtime_fields
運行時字段是在執行查詢時動態對字段類型和索引重新定義的字段。runtime_fields具備以下特點:
- 在不重新索引數據的情況下向現有文檔添加字段
- 在不了解數據結構的情況下開始處理數據
- 在查詢時覆蓋從索引字段返回的值
- 為特定用途定義字段而不修改底層架構
由于運行時字段未編入索引,因此添加運行時字段不會增加索引大小。直接在索引映射中定義運行時字段,可以節省存儲成本并提高預處理速度。
如果將運行時字段設為索引字段,則無需修改任何引用運行時字段的查詢。而且可以引用字段是運行時字段的一些索引,以及字段是索引字段的其他索引。可以靈活地選擇要索引哪些字段以及保留哪些字段作為運行時字段。
就其核心而言,運行時字段最重要的好處是能夠在提取文檔后將字段添加到文檔中。此功能簡化了映射決策,所以不需要預先決定如何解析數據,并且可以使用運行時字段隨時修改映射。使用運行時字段索引更小且更快的預處理信息,這結合使用更少的資源并降低運營成本。
上述問題,解決方案代碼如下:
GET task1/_search {"size": 0,"runtime_mappings": {"day_of_week": {"type": "keyword","script": "emit(doc['time'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL,Locale.ROOT))"},"time": {"type": "date","format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}},"aggs": {//1:按照周統計地震信息,也就是每周有幾天地震了"week_agg": {"date_histogram": {"field": "time","calendar_interval": "week"},"aggs": {"week_avg_magnitude": {"avg": {"field": "magnitude"}}}},//一周中的每一天的震級"day_of_week_magnitude":{"terms": {"field": "day_of_week"},"aggs": {//2: 一周中每一天的平均地震等級"day_of_week_avg_magnitude": {"avg": {"field": "magnitude"}}}}} }此段代碼中,其核心代碼如下
"runtime_mappings": {"day_of_week": {"type": "keyword","script": "emit(doc['time'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL,Locale.ROOT))"},"time": {"type": "date","format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}}其在runtime_mappings 中定義了兩個“新字段”,即day_of_week和time,其中day_of_week利用運行時字段中執行腳本進行動態計算,從而得出每天分別是一周內的星期幾。這種用法可用于各種其他復雜的運算。
而time字段則是對原有字段進行重新映射,改變其原有字段的類型和其他屬性,如format,使其原本不支持的時間類型變為支持。
總結
以上是生活随笔為你收集整理的不必Reindex,利用runtime_fields优雅地解决字段类型错误问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【逻辑题】猜猜她的生日
- 下一篇: 计算机语言求公因子,学好PLC必须掌握的