我本人接觸Elasticsearch(一下簡稱ES)有挺長一段時間了,本文結合本身的一些項目經驗,給你總結幾個實際項目中比較容易踩到的坑。但願讀者可以避免犯這樣的錯誤。html
在咱們的項目中,索引下通常都會存在一個時間的字段,這個字段能夠用來排序,或者作時間範圍查詢,或者聚合的場景等都會用到。數組
ES底層默認採用UTC時間格式,而中國的時間(CST)是緩存
CST=UTC+8
因此實際的項目中常常遇到查詢結果和本身指望的不一致。關於時區的問題以及如何解決,我以前專門寫了一篇文章,感興趣的能夠看看:app
ES系列之一文帶你避開日期類型存在的坑elasticsearch
ES自己支持咱們在寫入一個索引的時候,能夠不爲該索引設置任何的mappings。這種狀況下,ES會爲索引根據寫入的字段值,"推斷"該字段的類型。分佈式
看起來彷佛不錯,可是根據個人經驗,仍是建議應該明確的爲本身的索引定義mappings。ide
由於有時候ES "推斷"出來的結果並不必定是咱們想要的,而後給咱們帶來一些頭疼的問題。ui
我仍是給你舉個例子:this
寫入一個名爲message
的索引,.net
PUT /message/_doc/1 { "head": "message001", "body": "2020-05-30" }
而後繼續再插入一條,
PUT /message/_doc/2 { "head": "message002", "body": "this is an emergency" }
這裏就報錯了,錯誤的內容是提示咱們,body這個字段沒法被解析。產生這個問題的緣由是當咱們寫入第一條文檔的時候,ES "擅自作主"把body
這個字段標記成日期類型了,而後寫入文檔2的時候不是日期字符串,因此沒法解析。
能夠用下面的命令看下索引的mapping,證明咱們的猜測:
GET message/_mapping
在這個示例中,咱們若是明確的定義body
爲text類型就不會有這樣的問題了。
咱們通常都須要爲索引設置分片的數量,具體設置成多少須要根據你的項目實際狀況來定。
通常來講,單個 Shard 的建議最大大小是 20G 左右,最大不要超過 50G。
單個shard過大,或者shard太小致使shard數量太多,都會影響查詢的效率。
舉個例子,好比你預估索引的大小是100G,這個時候分片是3~5比較好。
若是你的索引是天天增量比較大的場景,好比日誌類,訂單類的索引,可能你首先要把根據日期來新建不一樣的索引,根據時間的數據規模選擇按天,周,甚至月來建索引。而後這些索引使用相同的分片設置。
ES某些場景下的聚合結果是不許確的,計算的結果只是告訴你一個大概的分佈狀況,並非精確的。
若是你不瞭解這個狀況,可能會在實際的項目中犯錯誤。
我曾經寫過一篇文章,對這個坑有過詳細的分析以及閉坑指南,有興趣能夠看看這篇文章:
在分桶聚合的場景下,大多數時候對單個字段的聚合查詢很是快的,若是是多個字段嵌套聚合。有可能撐爆內存,引起OOM。看下面一個例子。
假設咱們有個不少電影數據的索引,有個字段是數組,保存演員的名字。
{ "actors" : [ "Fred Jones", "Mary Jane", "Elizabeth Worthing" ] }
而後,咱們但願查詢出演影片最多的10個演員,以及他們合做最多的5位演員,可使用下面這個聚合,
{ "aggs" : { "actors" : { "terms" : { "field" : "actors", "size" : 10 }, "aggs" : { "costars" : { "terms" : { "field" : "actors", "size" : 5 } } } } } }
結果返回前10位演員,以及與他們合做最多的5位演員。可是就是這樣一個簡單的查詢,可能致使OOM。
咱們能夠想象下在內存中構建一個樹來表示這個 嵌套terms 分桶聚合。 首先actors 聚合會構建樹的第一層,每一個演員都有一個桶。
而後,第一層的每一個節點之下, costar 聚合會構建第二層,每一個聯合演員一個桶,若是你學過排列組合,應該知道這實際上要構建n的平方個分桶。(n是每一個影片中演員的數量)
假如平均每部影片(文檔)有 10 名演員,每部影片就會生成
10^2
100個桶。若是總共有 20000 部影片,粗率計算就會生成 2000000 個桶。這個引發內存爆炸就不奇怪了。
上面產生問題的根源在於ES對於這種嵌套聚合默認使用了深度優先規則,即先構建完整的樹,再篩選符合條件的結果。
ES容許咱們使用一種廣度優先的模式來進行這種場景的聚合,這種策略的工做方式有些不一樣,它先執行第一層聚合, 再繼續下一層聚合以前會先作修剪。
要使用廣度優先,只需簡單 的經過參數 collect 開啓,
{ "aggs" : { "actors" : { "terms" : { "field" : "actors", "size" : 10, "collect_mode" : "breadth_first" }, "aggs" : { "costars" : { "terms" : { "field" : "actors", "size" : 5 } } } } } }
廣度優先僅僅適用於每一個組的聚合數量遠遠小於當前總組數的狀況下,由於廣度優先會在內存中緩存裁剪後的僅僅須要緩存的每一個組的全部數據,以便於它的子聚合分組查詢能夠複用上級聚合的數據。
咱們給索引建模時,要儘可能避免mapping中的包含的字段過多。
過多的字段一個是難以維護,當存在成千上百個字段時,很難有人真正明確每一個字段的含義。另一個致使的問題是,當咱們須要更新文檔的時候,ES會在不一樣的節點同步這些更新。過多的字段意味着更新變慢。
若是咱們的業務場景確實須要不少字段,應該充分利用ES的dynamic templates
機制,提早定義好字段映射的規則,這樣一些字段就沒有必要在mapping裏定義好。
不過不管如何,都應該儘可能保持你的mapping字段足夠小。
Elasticsearch 是一個分佈式可擴展的實時搜索和分析引擎。這樣的神器若是用好了讓你的工做事半功倍,可是若是沒用好可能又會給你帶來很多的困擾。
先寫這麼多吧,後續若是工做中踩到新的坑再跟你們分享。
參考: