日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET Core接入ElasticSearch 7.5

發布時間:2023/12/4 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET Core接入ElasticSearch 7.5 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

寫在前面

最近一段時間,團隊在升級ElasticSearch(以下簡稱ES),從ES 2.2升級到ES 7.5。也是這段時間,我從零開始,逐步的了解了ES,中間也踩了不少坑,所以特地梳理和總結一下相關的技術點。

?

ES小趣聞:

多年前,一個叫做Shay Banon的剛結婚不久的開發者,由于妻子要去倫敦學習廚師,他便跟著也去了。在他找工作的過程中,為了給妻子構建一個食譜的搜索引擎,他開始使用Lucene進行嘗試。直接基于Lucene工作會比較困難,所以Shay開始抽象Lucene代碼以便可以在應用中添加搜索功能。他發布了他的第一個開源項目,叫做“Compass”。后來Shay找到一份工作,這份工作處在高性能和內存數據網格的分布式環境中,因此高性能的、實時的、分布式的搜索引擎也是理所當然需要的。然后他決定重寫Compass庫使其成為一個獨立的服務叫做Elasticsearch。Shay的妻子依舊等待著她的食譜搜索……

?

由此看見,一個成功的男人背后總是站著一個女人,所以程序員們要早點找到對象,可程序員找到女朋友又談何容易,程序猿注定悲傷-_-||。

ElasticSearch前期準備

EElasticsearch是一個開源的分布式、RESTful 風格的搜索和數據分析引擎,ES底層基于開源庫Apache Lucene,不過Lucene使用門檻太高,ES隱藏了Lucene使用時的復雜性,使得分布式實時文檔搜索、實時分析引擎、高擴展性變得更加容易。

安裝

安裝ES,首先要配置Java SDK,然后配置一下環境變量即可。然后再從官網下載ES安裝包,可以選用默認配置,點擊下一步—>安裝。

在瀏覽器上輸入http://localhost:9200/,顯示如下文本,就意味著安裝成功了。

?

{

?"name" : "XXXXXXXXXX",

?"cluster_name" : "elasticsearch",

?"cluster_uuid" : "mB04ov3OTvSz7OSe0GtZ_A",

?"version" : {

??"number" : "7.5.2",

??"build_flavor" : "unknown",

??"build_type" : "unknown",

??"build_hash" : "8bec50e1e0ad29dad5653712cf3bb580cd1afcdf",

??"build_date" : "2020-01-15T12:11:52.313576Z",

??"build_snapshot" : false,

??"lucene_version" : "8.3.0",

??"minimum_wire_compatibility_version" : "6.8.0",

??"minimum_index_compatibility_version" : "6.0.0-beta1"

?},

? "tagline" : "You Know, for Search"

}

?

部分基本概念

節點 & 集群

集群由多個節點組成,其中一個節點為主節點,主節點由內部選舉算法選舉產生。當然主節點是相對的,是相對于內部而言的。ES去中心化,這是相對于外部而言的,從邏輯上說,與任何一個節點的的通信和與集群通信是沒有區別的。如下圖所示。

索引

索引保存相關數據的地方,是指向一個或者多個物理分片的邏輯命名空間 。另外,每個Index的名字必須是小寫。

文檔

Document的核心元數據有三個:_index、_type(7.X已經弱化了,8.0開始就會移除)、_id。Document 使用 JSON 格式表示。

分片

一個分片是一個底層的工作單元,它僅保存了全部數據中的一部分。我們的文檔被存儲和索引到分片內,但是應用程序是直接與索引而不是與分片進行交互。

Elasticsearch 是利用分片將數據分發到集群內各處的。分片是數據的容器,文檔保存在分片內,分片又被分配到集群內的各個節點里。當你的集群規模擴大或者縮小時, Elasticsearch 會自動的在各節點中遷移分片,使得數據仍然均勻分布在集群里。

一個分片可以是主分片或者副本分片。索引內任意一個文檔都歸屬于一個主分片,所以主分片的數目決定著索引能夠保存的最大數據量。

一個副本分片只是一個主分片的拷貝。副本分片作為硬件故障時保護數據不丟失的冗余備份,并為搜索和返回文檔等讀操作提供服務。

在索引建立的時候就已經確定了主分片數,但是副本分片數可以隨時修改。

理論上一個主分片最大能夠存儲Integer.MAX_VALUE^128 個文檔。

寫操作探討

文檔會被保存到主分片,那么在多個分片的情況下是如何寫入和精確搜索的。實際上這是通過以下公式確定的:

shard = hash(routing) % number_of_primary_shards

以上的routing的值是一個任意的字符串,它默認被設置成文檔的_id字段,但是也可以被設置成其他指定的值。這個routing字符串會被傳入到一個哈希函數(Hash Function)來得到一個數字,然后該數字會和索引中的主要分片數進行模運算來得到余數。這個余數的范圍應該總是在0和number_of_primary_shards - 1之間,它就是一份文檔被存儲到的分片的號碼。

這就解釋了為什么索引中的主要分片數量只能在索引創建時被指定,并且將來都不能在被更改:如果主要分片數量在索引創建后改變了,那么之前的所有路由結果都會變地不正確,從而導致文檔不能被正確地獲取。那么如何水平擴展呢,可以移步Designing for scale。

所有的文檔API(get, index, delete, bulk, update)都接受一個routing參數,它用來定制從文檔到分片的映射。一個特定的routing值能夠確保所有相關文檔 - 比如屬于相同用戶的所有文檔 - 都會被存儲在相同的分片上。

寫操作原理圖:寫入的請求流程如圖所示(此圖源自《Elasticsearch權威指南》):寫入到磁盤流程如下圖所示:由此可見ES的實時并非是完全的實時,而是一種準實時(Near-Real-Time)。

讀操作探討

讀操作分為兩個階段,查詢階段(Query Phrase)以及聚合提取階段(Fetch Phrase)。

查詢階段

協調節點接受到讀請求,并將請求分配到相應的分片上(有可能是主分片或是副本分片,這個機制后續會提及),默認情況下,每個分片創建10個結果(僅包含 document_id 和 Scores)的優先級隊列,并以相關性排序,返回給協調節點。

?

查詢階段如果不特殊指定,落入的分片有可能是 primary 也有可能是 replicas,這個根據協調節點的負載均衡算法來確定。

?

聚合提取階段

假設查詢落入的分片數為 N,那么聚合階段就是對 N*10 個結果集進行排序,然后再通過已經拿到的 document_id 查到對應的 document 并組裝到隊列里,組裝完畢后將有序的數據返回給客戶端。

  • 客戶端發送請求到任意一個Node,成為Coordinating node

  • Coordinating node對Document進行路由,將請求轉發到對應的Node上,此時會使用Round-Robin隨機輪詢算法,在Primary Shard以及其所有Replica中隨機選擇一個,讓讀請求負載均衡

  • 接收請求的node返回Document給Coordinating node

  • Coordinating node返回Document給客戶端

ElasticSearch實戰

ES在.NET平臺上的官方客戶端是NEST,以下操作都是基于該package的。

常用操作

以下操作均基于ES-Head,該工具是一個Chrome插件,非常簡單實用,而且可以在GitHub上搜到源碼,方便個性化開發。

寫入數據

返回的數據中,可以看到Id是一段字符串,這是因為在寫入的過程中并沒有指定,所以會由ES默認生成。當然可以指定:

更新數據

_version值會隨著操作次數,逐漸迭代。

刪除數據

cluster

查詢操作:

cluster

項目升級過程中遇到的問題

分頁查詢過慢

初次的查詢使用了深度分頁(from-size)查詢,當數據達到百萬千萬級別時,已經慢的讓人忍無可忍。所謂深度查詢就是涉及到大量 shard 的查詢時,直接跳頁到幾千甚至上萬頁的數據,協調節點就有宕機的風險,畢竟協調節點需要將大量數據匯總起來進行排序,耗費大量的內存和 CPU 資源。所以慎用!盡可能用 Scroll API ,即只允許拿到下一頁的信息,不允許跳頁的情況出現,會避免這種情況的發生。

后來改用了快照分頁(scroll),整個查詢過程非常穩定,方差幾乎可以忽略。該查詢會自動返回一個_scroll_id,通過這個id(經過base64編碼)可以繼續查詢。查詢語句如下:http://localhost:9200/_search/scroll?scroll=1m&scroll_id=c2MkjsjskMkkssllasKKKOzM0NDg1ODpksksks5566HHsaskLLLqi692215。這個語句雖然很快,但是無法做到跳頁查詢,只能一頁一頁的查詢。

快照分頁參考代碼如下:

?

var searchResponse = client.Search(p =>

p.Query(t =>t.Bool(l => l.Filter(f => f.DateRange(m => m.GreaterThanOrEquals(startTime).Field(d => d.PostDate))))).From(0).Size(Configurations.SyncSize).Index("archive").Sort(s => s.Ascending(a => a.PostDate)).Scroll("60s"));

while(某條件)

{

searchResponse = client.Scroll<ElasticsearchTransaction>("60s", searchResponse.ScrollId);//跳出循環的條件

}

?

模糊查詢

該場景涉及到多個字段的模糊查詢,當然,這種查詢是十分消耗效率的,使用的時候要慎重,同時還要控制模糊關鍵字的數量,以盡可能在滿足業務的情況下,提升查詢效率,參考代碼如下:

?

public static List<IHit> GetDataByFuzzy(ElasticClient client920>0) {

string[] fieldList = {"filed1","filed2","filed3","filed4","filed5","filed6","filed7","filed8","filed9" };string term = string.Concat("*", string.Join("* *", "i u a n".Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)), "*"); var result = client9200.Search<TModel>(p => p.Query(q => q.Bool(b=>b.Must(t=>t.QueryString(c => c.Fields(fieldList).Query(term).Boost(1.1).Fuzziness(Fuzziness.Auto).MinimumShouldMatch(2).FuzzyRewrite(MultiTermQueryRewrite.ConstantScoreBoolean).TieBreaker(1).Lenient())).Filter(f=>f.Term(t=>t.Field(d=>d.AccountKey).Value("123456789"))))).ScriptFields(sf => sf.ScriptField("datetime1",sc => sc.Source("doc['datetime1'].value == null?doc['datetime2'].value: doc['datetime1'].value"))).Source(true).Index("archive").From(0).Size(10000).Sort(s => s.Descending(a => a.CreateDate)));return result.Hits.Select(p=>p.Source).ToList();

}

?

關于排序

在本次的ES優化升級過程中,關于排序的操作可以說是很糾結的。按照業務要求,要根據兩個時間類型的字段進行排序,如果某個為空,就按照不為空的排序,使得其排序結果達到穿插的效果,而不是像SQL語句那樣order field1, field2的排序結果那樣。

找出解決方案的過程很痛苦,因為官方的demo無法運行,經過多方嘗試,終于在查看ElasticSearch源代碼的情況下,找到了解決方案。

Github地址:https://github.com/elastic/elasticsearch/blob/master/server/src/main/java/org/elasticsearch/script/JodaCompatibleZonedDateTime.java,第411行

查詢語句如下:

?

{

"from": 0, "query": {"bool": {"filter": [{"term": {"UserId": {"value": "123456789"}}}]} }, "size": 10, "sort": [{"_script": {"script": {"source": "doc.DateTime1.empty?doc.DateTime2.value.toInstant().toEpochMilli():doc.DateTime1.value.toInstant().toEpochMilli()"},"type": "number","order": "desc"}} ]

}

?

C#參考代碼如下:

?

var searchResponse = _elasticsearchClient.Search(s => s

.Query(q => q.Bool(b => b.Filter(m => m.Term(t => t.Field(f => f.UserId).Value(userId)),m => m.QueryString(qs => qs.Fields(fieldList).Query(term.PreProcessQueryString()))))).Index(indexName).ScriptFields(sf => sf.Source(true).Sort(s=>s.Script(sr=>sr.Script(doc => doc.Source("doc.DateTime1.empty ? doc.DateTime2.value.toInstant().toEpochMilli() : doc.DateTime1.value.toInstant().toEpochMilli()")))).From(startIndex).Size(pageSize)); ?

參考鏈接:

https://www.dazhuanlan.com/2020/02/13/5e44f118b75cb/ https://www.toutiao.com/i6824365055832752653

總結

以上是生活随笔為你收集整理的.NET Core接入ElasticSearch 7.5的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。