背景: 有1億多的使用者畫像中數倉需要匯入ES。大多數字段都是sql統計資料,無法區分哪些發生了變化,所以不能增量更新。只能每天全量刷資料。在刷資料的過程中出現了更新緩慢、記憶體問題。於是做了一些寫入優化。
解決方案:
1. 讀資料
首先要從數倉讀取出資料到記憶體。然後再組裝物件去ES刷資料欄位比較多而且都需要查詢。嘗試了一下,即使limit 10,也需要耗時2分鐘。所以第一步導資料不能直接查詢。採用的是數倉到分散式檔案系統分片儲存。這一步已經有現成工具。1億資料匯入到分片耗時3分鐘左右
2.組裝資料
將分片的資料讀到java記憶體中。再構造請求引數刷ES
`問題:1.刷資料ES報413錯誤。ES建議每次bulk5~15M資料,這裡我每次批量提交5000條,bulk的時候發生的413 requets too large錯誤,google了一下,說是索引的時候段合併記憶體不夠。於是調整indices.breaker.fielddata.limit為60%,增大堆記憶體,結果沒什麼用;也有說要調整 client_max_body_size 的,但是我們的es是雲服務,沒法改配置引數最終加大es的記憶體為16G,不再報這個錯誤。
2.之前寫業務程式碼資料量一般不是很大,採用的是一次性把資料讀取到記憶體中。再做業務處理。但是這次在資料塞到一半的資料,先是系統響應變慢了,後來測試環境的系統掛了。通過過命令排查,發現List物件佔用了很多空間。於是複查程式碼。發現是for迴圈一直往list填物件導致的記憶體洩露。於是限制了單個檔案大小為20M,一個檔案一個檔案地處理。 `
3.提高es索引效率
剛開始刷資料預計需要20個小時。今天的資料如果明天才更新完,意義不大。於是想辦法提高索引效率。網上都說"refresh_interval": "-1";調整number_of_replicas=0。我調整了結果沒什麼變化。於是採用多執行緒刷資料
問題:1.一開始使用size為20的無界佇列,導致耗盡資源,任務執行緒佔用的記憶體佔用了80+%的記憶體,其他任務可能被拖垮。後來執行緒的核心執行緒數和最大執行緒數統一設定為10。並採用future模式,一個任務完成後再去新增其他任務。解決了執行緒耗盡資源和記憶體的問題。
用htop檢視刷資料機器的效能
可以看到開啟的10個執行緒佔用42%記憶體。主執行緒cpu偶爾接近100%,這不是io密集型嗎?怎麼會耗cpu。cpu變高可能是複雜的技術或者死迴圈。這裡迴圈每次讀取量有50000條,並且組裝物件的邏輯。而且有10個執行緒,猜想可能是這個原因。
ES的索引速率
成果
最後原來需要20小時才能完成的刷資料任務,只耗時約100分鐘。當然中間遇到的坑不止這些