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