es推薦演算法
『壹』 es深入搜索之全文檢索
我們之前介紹過結構化搜索的簡單使用,接下來,我們來看怎樣在全文欄位中搜索最相關的文檔。
全文搜索包括兩個最重要的方面:
1. 查詢與結果的相關性,並根據相關性對結果進行排名。
2. 分析,將數據轉化為有區別的、規范化的的過程。
所有的查詢都或多或少的會進行相關度計算,但不是所有的查詢都會有分析階段,文本查詢可以分為兩個部分:
1. 基於詞項的查詢,如 term 或 fuzzy 這樣的查詢是沒有分析階段的。他們對單個詞項進行操作。
2. 基於全文的查詢,比如match, 它們會先了解欄位映射的信息,判斷欄位是否被分詞,是否是日期還是數字等, 再根據映射信息,構建要查詢的詞項列表,根據列表進行查詢。
匹配查詢 match 是個核心查詢。無論需要查詢什麼欄位, match 查詢都應該會是首選的查詢方式。使用方式如下:
es執行上列步驟的過程如下:
如果一次只能搜索一個詞語,那麼全文搜索會不太靈活,幸運的是 match 也支持多詞查詢 。
以上查詢其實先後執行了兩次 term 查詢,使用 bool 進行包含,然後將結果進行合並返回。
以上查詢其實會導致出現不相關的結果,我們只想搜索包含words1 和 words2 的文檔,而不是 or 的結果。match 查詢還可以接受 operator 操作符作為輸入參數,默認情況下該操作符是 or 。
這種操作還是有些不妥,在 and 和 or 中間選擇太過絕對,如果用戶給出了5個詞項,我們想只要滿足其中4 個 就表示匹配,match 也提供了 minimum_should_match 參數,他是一個最小匹配參數,我們可以控制滿足的詞項超過改值則表示匹配,最好是使用百分比,因為你也不知道用戶提供了多少個詞項。該參數的設置非常靈活,完整的信息參考文檔,請看 https://www.elastic.co/guide/en/elasticsearch/reference/5.6/query-dsl-minimum-should-match.html#query-dsl-minimum-should-match
如果我們使用 bool 查詢黑色、大屏、手機,其中should 語句匹配得越多表示文檔的相關度越高,但是我們想要手機所佔的權重比較大,內容包括手機的文檔排名靠前,可以使用 boost 設置相對權重,注意是相對權重,默認是1。
在說相關度被破壞的原因之前,我們先看看es對於相關度是如何計算的
es 的相似度演算法被定義為檢索詞頻率/反向文檔頻率, TF/IDF ,包括以下內容:
有時,我們索引了一些文檔,然後檢索發現有些相關度較低的返回排名靠前?
出現上述原因的情況是因為es由於性能原因,不會計算所有索引該文檔的節點的IDF,比如我們索引了10個文檔, 其中6個文檔中包含 foo ,而由於es是分布式的,一個索引會被分為多個分片,有可能分片一包含的5 個文檔,有 4 個包含foo, 而另外一個在分片二中,所以會導致結果有差異。
在實際應用中,不會出現該問題,因為本局和全局的IDF差異會隨著文檔數量的增加逐漸降低。如果想要自己處理該問題,可以在搜索請求之後增加 ?search_type=dfs_query_then_fetch ,他會使得es先計算各個分片的 IDF, 然後在求出全局的 IDF, 生產環境中不要使用。因為只要有足夠的數據就可以使得差異減少。
『貳』 ElasticSearch-工作流程
啟動過程
當ElasticSearch節點啟動時,使用廣播技術來發現同一集群中的其他節點(配置文件中的集群名稱)並於它們連接。集群中會有一個節點被選為管理節點(master node),負責集群的狀態管理以及在集群拓撲變化時做出反應,分發索引分片至集群的相應節點。
es寫數據
1)客戶端選擇一個node發送請求,這個node就是coordinating node(協調節點)
2)協調節點對document進行路由,將請求轉發給對應的node
3)node上的primary shard處理請求,然後將數據同步到replica shard
①先寫入內存,並將操作寫入translog(數據不能被搜索,translog會在每隔5秒或者每次寫入完成後寫入磁碟)
②es每隔1秒(配置)進行一個刷新(refresh),寫入內存到新數據被寫入文件緩存中,並構成一個segement(數據能被搜索,未寫入磁碟,可能丟失)
③每隔30分鍾或者translog大小達到閾值,觸發commit,執行fsync操作,當前translog被刪除
(merge:每次refresh都會生成一個segment,segment過多會消耗資源,搜索變慢。A和B兩個segment,小segmentC,A,B被讀到內存中和Cmerge,生產大segement D,觸發commit)
4)返回響應結果給客戶端
es刪除數據
磁碟上每個segment都有一個.del文件關聯,當發送刪除請求時,在.del中標記為刪除,文檔仍能夠被搜索到,但會從結果中過濾掉。merge時.del文件中標記但數據不會被包括在新的segment中
es讀數據
1)客戶端發送請求到協調節點
2)協調節點將請求轉發到對應的shard(通過對doc key進行哈希( Murmur哈希演算法 ),判斷出doc在哪個shard上,然後對該shard查詢)
3)每個shard將搜索結果(doc id)返回給協調節點,由協調節點進行數據的合並、排序、分頁等操作
4)協調節點根據doc id去各節點拉取document,返回給客戶端
es更新數據
創建新文檔時,es會為該文檔分配一個版本號。對文檔但每次更新都會產生一個新的版本號。當執行更新時,舊版本在.del文件中標記刪除,並且新版本在新segment中寫入索引
並發控制
基於樂觀鎖和版本號
master選舉
①如果集群中存在master,認可該master,加入集群
②如果集群中不存在master,從具有master資格的節點中選id最小的節點作為master
實時性(FileSystem Cache)
一個Index由若干segment組成,搜索時按segment搜索,索引一條segment後,每個段會通過fsync操作持久化到磁碟,而fsync 操作比較耗時。
es中新增的document會被收集到indexing buffer區後被重寫成一個segment,然後直接寫入FileSystem Cache中,只要sengment文件被寫入cache後,這個sengment就可以打開和查詢,從而確保在短時間內就可以搜到。
『叄』 es源碼筆記-7.x 選主流程
Discovery模塊負責發現集群中的節點,以及選擇主節點。ES支持多種不同Discovery類型選擇,內置的實現有兩種:Zen Discovery和Coordinator,其他的包括公有雲平台亞馬遜的EC2、谷歌的GCE等。
它假定所有節點都有一個唯一的ID,使用該ID對節點進行排序。任何時候的當前Leader都是參與集群的最高ID節點。該演算法的優點是易於實現。但是,當擁有最大ID的節點處於不穩定狀態的場景下會有問題。例如,Master負載過重而假死,集群擁有第二大ID的節點被選為新主,這時原來的Master恢復,再次被選為新主,然後又假死
ES 通過推遲選舉,直到當前的 Master 失效來解決上述問題,只要當前主節點不掛掉,就不重新選主。但是容易產生腦裂(雙主),為此,再通過「法定得票人數過半」解決腦裂問題
1、多數派原則:必須得到超過半數的選票才能成為master。
選出的leader一定擁有最新已提交數據:在raft中,數據更新的節點不會給數據舊的節點投選票,而當選需要多數派的選票,則當選人一定有最新已提交數據。在es中,version大的節點排序優先順序高,同樣用於保證這一點。
正確性論證:raft是一個被論證過正確性的演算法,而ES的演算法是一個沒有經過論證的演算法,只能在實踐中發現問題,做bug fix,這是我認為最大的不同。
是否有選舉周期term:raft引入了選舉周期的概念,每輪選舉term加1,保證了在同一個term下每個參與人只能投1票。ES在選舉時沒有term的概念,不能保證每輪每個節點只投一票。
選舉的傾向性:raft中只要一個節點擁有最新的已提交的數據,則有機會選舉成為master。在ES中,version相同時會按照NodeId排序,總是NodeId小的人優先順序高。
2、Paxos演算法
Paxos非常強大,尤其在什麼時機,以及如何進行選舉方面的靈活性比簡單的Bully演算法有很大的優勢,因為在現實生活中,存在比網路連接異常更多的故障模式。但 Paxos 實現起來非常復雜
本篇只討論內置的Zen Discovery
整體流程可以概括為:選舉臨時Master,如果本節點當選,則等待確立Master,如果其他節點當選,則嘗試加入集群,然後啟動節點失效探測器。
如果集群剛啟動則參與選主,否則加入集群
org.elasticsearch.node.Node.start()
選舉過程的實現位於 org.elasticsearch.discovery.zen.ZenDiscovery.findMaster() ,該函數查找當前集群的活躍 Master,或者從候選者中選擇新的Master。如果選主成功,則返回選定的Master,否則返回空
上面選擇臨時主節點非常簡單,
首先需要判斷當前候選者人數是否達到法定人數,否則選主失敗。
取列表中的最小值,比較函數通過compareNodes實現,只是對節點 ID 進行排序
選舉出的臨時Master有兩種情況:該臨時Master是本節點或非本節點。
(2)超時(默認為30秒,可配置)後還沒有滿足數量的join請求,則選舉失敗,需要進行新一輪選舉。
超時後直接return,當非臨時節點加入集群不成功時,重新發起選主流程
org.elasticsearch.discovery.zen.ZenDiscovery.innerJoinCluster()
(3)成功後發布新的clusterState。
實現如下:
submitStateUpdateTask最終通過TaskBatcher# submitTasks來提交任務。執行任務並發布集群狀態的總體過程在 MasterService#runTasks 方法中實現。
(2)向Master發送加入請求,並等待回復。超時時間默認為1分鍾(可配置),如果遇到異常,則默認重試3次(可配置)。這個步驟在joinElectedMaster方法中實現。
最終當選的Master會先發布集群狀態,才確認客戶的join請求,因此,joinElectedMaster返回代表收到了join請求的確認,並且已經收到了集群狀態。所以如果返回不成功,則重新發起選主流程
(3)檢查收到的集群狀態中的Master節點如果為空,或者當選的Master不是之前選擇的節點,則重新選舉。
1、es通過主從模式以及發現機制保證節點之間的負載均衡,但是es使用量的急劇增加暴露了很多問題,例如,Zen的minimum_master_nodes設置經常配置錯誤,這會使群集更容易出現裂腦和丟失數據的風險
2、7.x以上版本Coordinator提供了安全的亞秒級的master選舉時間,而Zen可能要花幾秒鍾來選擇一個新的master
3、es的master掛了,數據節點在這區間還能對外提供服務嗎?
參考
Elasticsearch分布式一致性原理剖析
『肆』 elasticSearch理論篇—索引、節點、分片
傳統我們檢索文章,是逐個遍歷找到對應關鍵詞的位置;
而倒排索引,是通過分詞策略,形成詞與文章的映射關系表,這種詞典+映射表即為倒排索引。
倒排索引的底層實現是基於:FST(Finite State Transcer)數據結構。
lucene [lu'sen] 從4+版本後開始大量使用的數據結構是FST。FST有兩個優點:
利用es的分片預分配。
不能,因為分片數是 文檔路由演算法 中重要的元素:
shard = hash(routing) % number_of_primary_shards
動態修改分片將意味著幾乎重新索引文檔數據,這是比僅僅將分片從一個節點復制到另一個節點更重量級的操作。
一個分片存在於單個節點,一個節點可以包含多個分片。
elasticSearch天然具有分布式的特徵,實現水平擴容時通過 分片預分配 。在創建索引時,選擇合適的分片數。
隨著數據量的增加,可以動態的增加節點數,elasticSearch將會自動將分片分配到新增的節點上,當重新分配完成時,每個分片將會有接近至少兩倍於之前的運算速度。
elasticSearch中新添加的索引默認被指定了5個主分片。這意味著我們最多可以將那個索引分散到5個節點上,每一個節點一個分片。
不能,一個分片並不是沒有代價的。
es適當的預分配是好的,但是上千個分片就有些糟糕。
ElasticSearch推薦的最大JVM堆空間是30~32G, 所以把你的分片最大容量限制為30GB, 然後再對分片數量做合理估算. 例如, 你認為你的數據能達到200GB, 推薦你最多分配7到8個分片。
在開始階段, 一個好的方案是根據你的節點數量按照1.5~3倍的原則來創建分片. 例如,如果你有3個節點, 則推薦你創建的分片數最多不超過9(3x3)個。當性能下降時,增加節點,ES會平衡分片的放置。
對於基於日期的索引需求, 並且對索引數據的搜索場景非常少. 也許這些索引量將達到成百上千, 但每個索引的數據量只有1GB甚至更小. 對於這種類似場景, 建議只需要為索引分配1個分片。如日誌管理就是一個日期的索引需求,日期索引會很多,但每個索引存放的日誌數據量就很少。
副本分片 可以實現高可用。當持有主分片的節點掛掉之後,一個副本分片將會晉升為主分片的角色。
在索引寫入時,副本分片做著和主分片相同的工作。新文檔首先被索引進主分片然後在同步到其他所有的副本分片。增加副本分片並不會增加索引容量。
副本分片可以服務於讀請求,如果索引偏向查詢,那麼可以通過增加副本的數目來提升查詢性能。但也要為此增加額外的硬體資源。
當使用上面配置時,每一個分片的副本分片數量為1個。
一個擁有兩個主分片一份副本的索引可以在四個節點中橫向擴展
Elasticsearch: 權威指南 » 數據建模 » 擴容設計 » 擴容的單元
面試官:Elasticsearch如何設計索引?滿分答案來了
elasticsearch 設置多少分片合適
新年手打,24道進階必備Elasticsearch 面試真題(建議收藏!)
『伍』 二十七、ElasticSearch聚合分析中的演算法講解
1、易並行聚合演算法,三角選擇原則,近似聚合演算法
(1)、易並行聚合演算法:比如max
(2)、不易的,如count(distinct)
(2)精準+大數據:hadoop,批處理,非實時,可以處理海量數據,保證精準,可能會跑幾個小時
(3)大數據+實時:es,不精準,近似估計,可能會有百分之幾的錯誤率
(4)、近似聚合演算法
如果採取近似估計的演算法:延時在100ms左右,0.5%錯誤
如果採取100%精準的演算法:延時一般在5s~幾十s,甚至幾十分鍾,幾小時, 0%錯誤
2、cardinality去重及演算法優化和HLL演算法分析
es,去重,cartinality metric,對每個bucket中的指定的field進行去重,取去重後的count,類似於count(distcint)
precision_threshold優化准確率和內存開銷可以提高去重性能
cardinality演算法,會佔用precision_threshold * 8 byte 內存消耗,100 * 8 = 800個位元組
HyperLogLog++ (HLL)演算法性能優化
默認情況下,發送一個cardinality請求的時候,會動態地對所有的field value,取hash值; 將取hash值的操作,前移到建立索引的時候
3、percentiles百分比演算法以及網站訪問時延統計
需求:比如有一個網站,記錄下了每次請求的訪問的耗時,需要統計tp50,tp90,tp99
tp50:50%的請求的耗時最長在多長時間
tp90:90%的請求的耗時最長在多長時間
tp99:99%的請求的耗時最長在多長時間
數據:
不同概率百分比之間的防問效率:
分組統計防問百分比,並計算平均值
4、percentile ranks網站訪問時延SLA統計
SLA:就是你提供的服務的標准
例以地區分組,計算以不同時間的響應百分比
5、doc value原理
(1)index-time生成
PUT/POST的時候,就會生成doc value數據,也就是正排索引
(2)核心原理與倒排索引類似
正排索引,也會寫入磁碟文件中,os cache先進行緩存,以提升訪問doc value正排索引的性能,如果os cache內存大小不足夠放得下整個正排索引,doc value,就會將doc value的數據寫入磁碟文件中
(3)性能問題:
es官方是建議,es大量是基於os cache來進行緩存和提升性能的,不建議用jvm內存來進行緩存,那樣會導致一定的gc開銷和oom問題給jvm更少的內存,給os cache更大的內存。
(4)、column壓縮
doc1: 550
doc2: 550
doc3: 500
合並相同值,550,doc1和doc2都保留一個550的標識即可
(1)所有值相同,直接保留單值
(2)少於256個值,使用table encoding模式:一種壓縮方式
(3)大於256個值,看有沒有最大公約數,有就除以最大公約數,然後保留這個最大公約數
(4)如果沒有最大公約數,採取offset結合壓縮的方式:
如果的確不需要doc value,比如聚合等操作,那麼可以禁用,減少磁碟空間佔用
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"my_field": {
"type": "keyword"
"doc_values": false
}
}
}
}
}
6、對於分詞的field執行aggregation,發現報錯。。。
如果直接對分詞field執行聚合,報錯,大概意思是說,你必須要打開fielddata,然後將正排索引數據載入到內存中,才可以對分詞的field執行聚合操作,而且會消耗很大的內存
給分詞的field,設置fielddata=true,發現可以執行
也可以用內置field不分詞,對string field進行聚合
7、分詞field+fielddata的工作原理
(1)、不分詞的所有field,可以執行聚合操作 --> 如果你的某個field不分詞,那麼在index-time時,就會自動生成doc value --> 針對這些不分詞的field執行聚合操作的時候,自動就會用doc value來執行
(2)、分詞field,是沒有doc value的。在index-time,如果某個field是分詞的,那麼是不會給它建立doc value正排索引的,因為分詞後,佔用的空間過於大,所以默認是不支持分詞field進行聚合的
fielddata載入到內存的過程是lazy載入的,對一個analzyed field執行聚合時,才會載入,而且是field-level載入的。一個index的一個field,所有doc都會被載入,而不是少數doc,不是index-time創建,是query-time創建
為什麼fielddata必須在內存?因為分詞的字元串,需要按照term進行聚合,需要執行更加復雜的演算法和操作,如果基於磁碟和os cache,那麼性能會很差。
8、fielddata相關優化配置
(1)、內存限制
indices.fielddata.cache.size: 20%,超出限制,清除內存已有fielddata數據,fielddata佔用的內存超出了這個比例的限制,那麼就清除掉內存中已有的fielddata數據
默認無限制,限制內存使用,但是會導致頻繁evict和reload,大量IO性能損耗,以及內存碎片和gc
(2)監控fielddata內存使用
GET /_stats/fielddata?fields=*
GET /_nodes/stats/indices/fielddata?fields=*
GET /_nodes/stats/indices/fielddata?level=indices&fields=*
(3)、circuit breaker斷路器
如果一次query load的feilddata超過總內存,就會oom --> 內存溢出
circuit breaker會估算query要載入的fielddata大小,如果超出總內存,就短路,query直接失敗
indices.breaker.fielddata.limit:fielddata的內存限制,默認60%
indices.breaker.request.limit:執行聚合的內存限制,默認40%
indices.breaker.total.limit:綜合上面兩個,限制在70%以內
(4)、fielddata filter的細粒度內存載入控制
min:僅僅載入至少在1%的doc中出現過的term對應的fielddata
比如說某個值,hello,總共有1000個doc,hello必須在10個doc中出現,那麼這個hello對應的fielddata才會載入到內存中來
min_segment_size:少於500 doc的segment不載入fielddata
載入fielddata的時候,也是按照segment去進行載入的,某個segment裡面的doc數量少於500個,那麼這個segment的fielddata就不載入
(5)、fielddata預載入
query-time的fielddata生成和載入到內存,變為index-time,建立倒排索引的時候,會同步生成fielddata並且載入到內存中來,這樣的話,對分詞field的聚合性能當然會大幅度增強
(6)、global ordinal序號標記預載入
有很多重復值的情況,會進行global ordinal標記
doc1: status1
doc2: status2
doc3: status2
doc4: status1
status1 --> 0 status2 --> 1
doc1: 0
doc2: 1
doc3: 1
doc4: 0
建立的fielddata也會是這個樣子的,這樣的好處就是減少重復字元串的出現的次數,減少內存的消耗
『陸』 es查詢數據的工作原理是什麼
查詢,GET某一條數據,寫入了某個document,這個document會自動給你分配一個全局唯一的id,doc id,同時也是根據doc id進行hash路由到對應的primary shard上面去。也可以手動指定doc id,比如用訂單id,用戶id。
我們可以通過doc id來查詢,會根據doc id進行hash,判斷出來當時把doc id分配到了哪個shard上面去,從那個shard去查詢
1)客戶端發送請求到任意一個node,成為coordinate node(協調結點)
2)coordinate node進行hash後對document進行路由,將請求轉發到對應的node,此時會使用round-robin 隨機輪詢演算法 ,在primary shard以及其所有replica node中 隨機選擇一個 ,讓讀請求負載均衡
3)接收請求的node返回document給coordinate node
4)coordinate node返回document給客戶端
es最強大的是做全文檢索,就是比如你有三條數據
java真好玩兒啊
java好難學啊
j2ee特別牛
你根據java關鍵詞來搜索,將包含java的document給搜索出來
es就會給你返回:java真好玩兒啊,java好難學啊
1)客戶端發送請求到一個coordinate node
2)協調節點 將搜索請求轉發到 所有的shard 對應的primary shard或replica shard
3)query phase: 每個shard將自己的搜索結果 (其實就是一些 doc id ), 返回給協調節點 ,由協調節點進行數據的 合並、排序、分頁 等操作,產出最終結果
4)fetch phase:接著由 協調節點,根據doc id去各個節點上拉取實際的document數據 ,最終返回給客戶端
尤其要注意的這里是先拿的id喲
『柒』 ES集群原理與搭建
查看集群健康狀況:URL+ /GET _cat/health
Cluster
代表一個集群,集群中有多個節點,其中有一個為主節點,這個主節點是可以通過選舉產生的,主從節點是對於集群內部來說的。es的一個概念就是去中心化,字面上理解就是無中心節點,這是對於集群外部來說的,因為從外部來看es集群,在邏輯上是個整體,你與任何一個節點的通信和與整個es集群通信是等價的。
Shards
代表索引分片,es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分布到不同的節點上。構成分布式搜索。分片的數量只能在索引創建前指定,並且索引創建後不能更改。
replicas
代表索引副本,es可以設置多個索引的副本,副本的作用一是提高系統的容錯性,當某個節點某個分片損壞或丟失時可以從副本中恢復。二是提高es的查詢效率,es會自動對搜索請求進行負載均衡。
Recovery
代表數據恢復或叫數據重新分布,es在有節點加入或退出時會根據機器的負載對索引分片進行重新分配,掛掉的節點重新啟動時也會進行數據恢復。
(2)、ES為什麼要實現集群
在單台ES伺服器節點上,隨著業務量的發展索引文件慢慢增多,會影響到效率和內存存儲問題等。
我們可以採用ES集群,將單個索引的分片到多個不同分布式物理機器上存儲,從而可以實現高可用、容錯性等。
ES集群中索引可能由多個分片構成,並且每個分片可以擁有多個副本。通過將一個單獨的索引分為多個分片,我們可以處理不能在一個單一的伺服器上面運行的大型索引,簡單的說就是索引的大小過大,導致效率問題。不能運行的原因可能是內存也可能是存儲。由於每個分片可以有多個副本,通過將副本分配到多個伺服器,可以提高查詢的負載能力。
(3)、ES是如何解決高並發
ES是一個分布式全文檢索框架,隱藏了復雜的處理機制,內部使用 分片機制、集群發現、分片負載均衡請求路由。
Shards 分片:代表索引分片,es可以把一個完整的索引分成多個分片,這樣的好處是可以把一個大的索引拆分成多個,分布到不同的節點上。構成分布式搜索。分片的數量只能在索引創建前指定,並且索引創建後不能更改。
Replicas分片:代表索引副本,es可以設置多個索引的副本,副本的作用一是提高系統的容錯性,當某個節點某個分片損壞或丟失時可以從副本中恢復。二是提高es的查詢效率,es會自動對搜索請求進行負載均衡。
1、每個索引會被分成多個分片shards進行存儲,默認創建索引是分配5個分片進行存儲。每個分片都會分布式部署在多個不同的節點上進行部署,該分片成為primary shards。
注意:索引的主分片primary shards定義好後,後面不能做修改。
2、為了實現高可用數據的高可用,主分片可以有對應的備分片replics shards,replic shards分片承載了負責容錯、以及請求的負載均衡。
注意: 每一個主分片為了實現高可用,都會有自己對應的備分片,主分片對應的備分片不能存放同一台伺服器上。主分片primary shards可以和其他replics shards存放在同一個node節點上。
3、documnet routing(數據路由)
當客戶端發起創建document的時候,es需要確定這個document放在該index哪個shard上。這個過程就是數據路由。
路由演算法:shard = hash(routing) % number_of_primary_shards
如果number_of_primary_shards在查詢的時候取余發生的變化,無法獲取到該數據
注意:索引的主分片數量定義好後,不能被修改
高可用視圖分析(下圖所示:上面的圖,如果節點1與節點2宕機了,es集群數據就不完整了。下面圖,如果節點1與節點2宕機了,es集群數據還是完整的)
(1)、伺服器環境
准備三台伺服器集群
| 伺服器名稱 | IP地址 |
| node-1 | 192.168.212.182 |
| node-2 | 192.168.212.183 |
| node-3 | 192.168.212.184 |
(2)、關閉防火牆
(3)、**** http://192.168.212.185:9200/_cat/nodes?pretty
*號表示為master節點
注意:
注意克隆data文件會導致數據不同步
報該錯誤解決辦法 :
failed to send join request to master
因為克隆導致data文件也克隆呢,直接清除每台伺服器data文件。
『捌』 ES 索引解析(倒排索引 | 正排索引)
何為倒排索引?首先要了解索引表:由關鍵詞為key,關鍵詞位置屬性為value組成的一張表。由於該表不是由key來確定value值,而是由value的屬性值來確定key的位置,所以稱為倒排索引,帶有倒排索引的文件稱為倒排文件。通俗的講倒排索引就好比書的目錄,通過目錄咱們可以准確的找到相應的數據。下面對lucene倒排索引的結構與演算法進行介紹。
對於獲取關鍵詞有兩種思路,1.根據空格分隔獲取所有的字元2.過濾文檔中沒有意義的詞,獲取其中的關鍵詞。除此以上還會對詞的時態,大小寫,同義詞,標點符號等做相應的處理,不同的分詞器對文檔索引的時候做的操作有所差異。
實例1:Tom lives in Zhangye,I live in Zhangye too.
關鍵詞1:[tom][live][in][zhangye][i][live][zhangye]
實例2:He once lived in Shanghai
關鍵詞2:[he][live][shanghai]
根據關鍵詞我們就可以確定關鍵詞所在的文章號,關鍵詞在文章中出現的頻次以及該關鍵詞在文章中出現的位置(根據上面獲取關鍵詞我們可以知道,索引的時候要麼索引所有字元,要麼索引關鍵詞,lucene採取的就是索引關鍵詞的方式,這樣會節省大量的空間),具體索引如下表:
1)詞典文件:每個關鍵詞以及指向頻率文件和位置文件的指針和filed(用於表達信息位置,每個關鍵詞都有一個或多個field)信息
2)頻率文件:關鍵詞在每個文件中出現頻率的文件
3)位置文件:關鍵詞所在文章中的位置文件
關鍵詞壓縮為<前綴長度,後綴>,例如:「我愛你中國」=》<3,中國>,另外對數字的壓縮,只記錄與上一個數字的差值,比如當前文章號是11890,上一個文章號是11870,壓縮後只需要報錯20,這樣就極大的縮小了存儲空間。
倒排索引服務於es查詢操作,對數據的聚合,排序則需要使用正排索引,下面我們介紹正排索引。
正排索引說白了就是document每個field的值的排序,其實就是doc values,舉例說明:
實例:
doc1: { "name": "張三", "age": 27,"sex":"男" }
doc2: { "name": "李四", "age": 30,"sex":「女」 }
正排索引:
document name age sex
doc1 jack 27 男
doc2 tom 30 女
正排索引使用場景是排序,聚合,過濾等
注意:
對於分詞的field進行聚合(aggregation)操作,需要將fielddata設置為true,否則會報錯提示你打開fielddata、將正排索引載入到內存中
doc values是被保存在磁碟上的,此時如果內存足夠,os會自動將其緩存在內存中,性能還是會很高;如果內存不足夠,os會將其寫入磁碟上。
到此對倒排索引與正排索引就介紹完畢了,如有幫助,請關注!謝謝!
『玖』 es使用與原理2 -- scoll技術,bouncing results,零停機重建索引等等
默認情況下,是按照_score降序排序的,我們也可以定製排序規則
Elasticsearch使用的是 term frequency/inverse document frequency演算法,簡稱為TF/IDF演算法
Term frequency(TF): 搜索文本中的各個詞條在field文本中出現了多少次,出現次數越多,就越相關
如:搜索請求:hello world
doc1:hello you, and world is very good
doc2:hello, how are you
doc1 肯定比doc2的評分高,因為hello world都在doc1中出現了。
Inverse document frequency(IDF): 搜索文本中的各個詞條在整個索引的所有文檔中出現了多少次,出現的次數越多,就越不相關
搜索請求:hello world
doc1:hello, today is very good
doc2:hi world, how are you
比如說,在index中有1萬條document,hello這個單詞在所有的document中,一共出現了1000次;world這個單詞在所有的document中,一共出現了100次
那最終的結果肯定是 word的得分所佔比更高
關於_score,ES還有一條規則。
Field-length norm:field長度,field越長,相關度越弱
搜索請求:hello world
doc1:{ "title": "hello article", "content": "babaaba 1萬個單詞" }
doc2:{ "title": "my article", "content": "blablabala 1萬個單詞,hi world" }
hello world在整個index中出現的次數是一樣多的。最終 doc1得分更高
搜索的時候,要依靠倒排索引;排序的時候,需要依靠正排索引,看到每個document的每個field,然後進行排序,所謂的正排索引,其實就是doc values,doc values 也可以供排序,聚合,過濾等操作使用。doc values是被保存在磁碟上的,此時如果內存足夠,os會自動將其緩存在內存中,性能還是會很高;如果內存不足夠,os會將其寫入磁碟上
正排索引如下:
倒排索引不可變的好處
想像一下有兩個文檔有同樣值的時間戳欄位,搜索結果用 timestamp 欄位來排序。 由於搜索請求是在所有有效的分片副本間輪詢的,那就有可能發生主分片處理請求時,這兩個文檔是一種順序, 而副本分片處理請求時又是另一種順序。
這就是所謂的 bouncing results 問題: 每次用戶刷新頁面,搜索結果表現是不同的順序。 讓同一個用戶始終使用同一個分片,這樣可以避免這種問題, 可以設置 preference 參數為一個特定的任意值比如用戶會話ID來解決。
如
如果一次性要查出來比如10萬條數據,那麼性能會很差,此時一般會採取用scoll滾動查詢,一批一批的查,直到所有數據都查詢完處理完。
scoll,看起來挺像分頁的,但是其實使用場景不一樣。分頁主要是用來一頁一頁搜索,給用戶看的;scoll主要是用來一批一批檢索數據,讓系統進行處理的
使用scoll滾動搜索,可以先搜索一批數據,然後下次再搜索一批數據,以此類推,直到搜索出全部的數據來
scoll搜索會在第一次搜索的時候,保存一個當時的視圖快照,之後只會基於該舊的視圖快照提供數據搜索,如果這個期間數據變更,是不會讓用戶看到的
採用基於_doc進行排序的方式,性能較高
每次發送scroll請求,我們還需要指定一個scoll參數,指定一個時間窗口,每次搜索請求只要在這個時間窗口內能完成就可以了
獲得的結果會有一個scoll_id,下一次再發送scoll請求的時候,必須帶上這個scoll_id
1 創建索引
2 修改索引
3 刪除索引
lucene是沒有type的概念的,在document中,實際上將type作為一個document的field來存儲,即_type,es通過_type來進行type的過濾和篩選
一個index中的多個type,實際上是放在一起存儲的,因此一個index下,不能有多個type重名,而類型或者其他設置不同的,因為那樣是無法處理的
比如
底層存儲是這樣的
將類似結構的type放在一個index下,這些type應該有多個field是相同的
假如說,你將兩個type的field完全不同,放在一個index下,那麼就每條數據都d會有大量field在底層的lucene中是空值,會有嚴重的性能問題
1、定製dynamic策略
true:遇到陌生欄位,就進行dynamic mapping
false:遇到陌生欄位,就忽略
strict:遇到陌生欄位,就報錯
2、定製自己的dynamic mapping template(type level)
上面的設置是/my_index/my_type 的欄位,如果是以_en結尾的,那麼就自動映射為string類型
一個field的設置是不能被修改的,如果要修改一個Field,那麼應該重新按照新的mapping,建立一個index,然後將數據批量查詢出來,重新用bulk api寫入index中。
批量查詢的時候,建議採用scroll api,並且採用多線程並發的方式來reindex數據,每次scoll就查詢指定日期的一段數據,交給一個線程即可。
(1)一開始,依靠dynamic mapping,插入數據,但是不小心有些數據是2017-01-01這種日期格式的,所以title這種field被自動映射為了date類型,實際上業務認為它應該是string類型的
(2)當後期向索引中加入string類型的title值的時候,就會報錯
(3)如果此時想修改title的類型,是不可能的
(4)此時,唯一的辦法,就是進行reindex,也就是說,重新建立一個索引,將舊索引的數據查詢出來,再導入新索引
(5)如果說舊索引的名字,是old_index,新索引的名字是new_index,終端java應用,已經在使用old_index在操作了,難道還要去停止java應用,修改使用的index為new_index,才重新啟動java應用嗎?這個過程中,就會導致java應用停機,可用性降低
(6)所以說,給java應用一個別名,這個別名是指向舊索引的,java應用先用著,java應用先用goods_index alias來操作,此時實際指向的是舊的my_index
(7)新建一個index,調整其title的類型為string
(8)使用scroll api將數據批量查詢出來
(9)採用bulk api將scoll查出來的一批數據,批量寫入新索引
(10)反復循環8~9,查詢一批又一批的數據出來,採取bulk api將每一批數據批量寫入新索引
(11)將goods_index alias切換到my_index_new上去,java應用會直接通過index別名使用新的索引中的數據,java應用程序不需要停機,零提交,高可用
(12)直接通過goods_index別名來查詢,是否ok
現有流程的問題,每次都必須等待fsync將segment刷入磁碟,才能將segment打開供search使用,這樣的話,從一個document寫入,到它可以被搜索,可能會超過1分鍾!!!這就不是近實時的搜索了!!!主要瓶頸在於fsync實際發生磁碟IO寫數據進磁碟,是很耗時的。
『拾』 es使用與原理6 -- 聚合分析剖析
有些聚合分析的演算法,是很容易就可以並行的,比如說max
有些聚合分析的演算法,是不好並行的,比如說,count(distinct),並不是說,在每個node上,直接就出一些distinct value,就可以的,因為數據可能會很多,假設圖中的協調節點3百萬個數據去重後還剩下100萬distinct的數據,那麼內存需要來存儲這100萬條數據,這是不可能的
es會採取近似聚合的方式,就是採用在每個node上進行近估計的方式,得到最終的結論,cuont(distcint),100萬,1050萬/95萬 --> 5%左右的錯誤率
近似估計後的結果,不完全准確,但是速度會很快,一般會達到完全精準的演算法的性能的數十倍
precision_threshold優化准確率和內存開銷
brand去重,如果brand的unique value,在100個以內,小米,長虹,三星,TCL,HTL。。。
在多少個unique value以內,cardinality,幾乎保證100%准確
cardinality演算法,會佔用precision_threshold * 8 byte 內存消耗,100 * 8 = 800個位元組
佔用內存很小。。。而且unique value如果的確在值以內,那麼可以確保100%准確
100,數百萬的unique value,錯誤率在5%以內
precision_threshold,值設置的越大,佔用內存越大,1000 * 8 = 8000 / 1000 = 8KB,可以確保更多unique value的場景下,100%的准確
field,去重,count,這時候,unique value,10000,precision_threshold=10000,10000 * 8 = 80000個byte,80KB
doc value正排索引
搜索+聚合 是怎麼實現的?
假設是倒排索引實現的
倒排索引來實現是非常不現實的,因為我們搜索的那個欄位search_field 有可能是分詞的,這就需要去掃描整個索引才能實現聚合操作,效率是及其低下的。
正排索引結構:
doc2: agg1
doc3: agg2
1萬個doc --> 搜 -> 可能跟搜索到10000次,就搜索完了,就找到了1萬個doc的聚合field的所有值了,然後就可以執行分組聚合操作了
doc value原理
1、doc value原理
(1)index-time生成
PUT/POST的時候,就會生成doc value數據,也就是正排索引
(2)核心原理與倒排索引類似
正排索引,也會寫入磁碟文件中,然後呢,os cache先進行緩存,以提升訪問doc value正排索引的性能
如果os cache內存大小不足夠放得下整個正排索引,doc value,就會將doc value的數據寫入磁碟文件中
(3)性能問題:給jvm更少內存,64g伺服器,給jvm最多16g
es官方是建議,es大量是基於os cache來進行緩存和提升性能的,不建議用jvm內存來進行緩存,那樣會導致一定的gc開銷和oom問題
給jvm更少的內存,給os cache更大的內存
64g伺服器,給jvm最多16g,幾十個g的內存給os cache
os cache可以提升doc value和倒排索引的緩存和查詢效率
2、column壓縮
doc1: 550
doc2: 550
doc3: 500
合並相同值,550,doc1和doc2都保留一個550的標識即可
(1)所有值相同,直接保留單值
(2)少於256個值,使用table encoding模式:一種壓縮方式
(3)大於256個值,看有沒有最大公約數,有就除以最大公約數,然後保留這個最大公約數
重點:
對分詞的field,直接執行聚合操作,會報錯,大概意思是說,你必須要打開fielddata,然後將正排索引數據載入到內存中,才可以對分詞的field執行聚合操作,而且會消耗很大的內存
先修改 欄位的fielddata屬性為true,再查 就能查找到數據
當然,我們也可以使用內置field(keyword)不分詞,對string field進行聚合,如果對不分詞的field執行聚合操作,直接就可以執行,不需要設置fieldata=true
分詞field+fielddata的工作原理
doc value --> 不分詞的所有field,可以執行聚合操作 --> 如果你的某個field不分詞,那麼在index-time,就會自動生成doc value --> 針對這些不分詞的field執行聚合操作的時候,自動就會用doc value來執行
分詞field,是沒有doc value的。。。在index-time,如果某個field是分詞的,那麼是不會給它建立doc value正排索引的,因為分詞後,佔用的空間過於大,所以默認是不支持分詞field進行聚合的
分詞field默認沒有doc value,所以直接對分詞field執行聚合操作,是會報錯的
對於分詞field,必須打開和使用fielddata,完全存在於純內存中。。。結構和doc value類似。。。如果是ngram或者是大量term,那麼必將佔用大量的內存。。。
如果一定要對分詞的field執行聚合,那麼必須將fielddata=true,然後es就會在執行聚合操作的時候,現場將field對應的數據,建立一份fielddata正排索引,fielddata正排索引的結構跟doc value是類似的,
但是只會講fielddata正排索引載入到內存中來,然後基於內存中的fielddata正排索引執行分詞field的聚合操作
如果直接對分詞field執行聚合,報錯,才會讓我們開啟fielddata=true,告訴我們,會將fielddata uninverted index,正排索引,載入到內存,會耗費內存空間
為什麼fielddata必須在內存?因為大家自己思考一下,分詞的字元串,需要按照term進行聚合,需要執行更加復雜的演算法和操作,如果基於磁碟和os cache,那麼性能會很差
我們是不是可以預先生成載入fielddata到內存中來???
query-time的fielddata生成和載入到內存,變為index-time,建立倒排索引的時候,會同步生成fielddata並且載入到內存中來,這樣的話,對分詞field的聚合性能當然會大幅度增強