現在的位置: 首頁 > 搜索技術 > 正文

Solr和ElasticSearch是什麼?有什麼相同點和不同點

2020年02月12日 搜索技術 ⁄ 共 8480字 ⁄ 字型大小 評論關閉

  Solr和ElasticSearch到底有一些什麼不同?我在網上搜索了一些文章,這些文章要麼是列出一個表,詳細地介紹兩者什麼功能有,什麼功能沒有(比較好的一篇博客 https://solr-vs-elasticsearch.com ),要麼是從大類出發(其中比較好的一篇文章 https://logz.io/blog/solr-vs-elasticsearch),比較兩者的關注度,社區等等。但看完這些文章,還是沒法解決我心中的疑惑。最近由於項目的原因,Solr和ElasticSearch都有所使用。最近又把solr和ElasticSearch的官方文檔都過了一遍。對兩者有了一些淺顯的見解。所以在這裡想跟大家分享下我的一些看法。在這篇文章中,我不會列出一個列表來說明兩者的異同,而是抽出我覺得比較重要的幾點來講一講。本文的對比基於Solr7.3和ElasticSearch7.4。兩者都在快速迭代,可能有一些最新的進展我沒有考慮到,同時我接觸搜索引擎的時間不長,難免有一些錯誤或者我沒關注到的點,歡迎大家指正。

  Solr和ElasticSearch相同點

  兩者都基於Lucene實現

  這看起來像一句廢話,因為知道這兩個產品的人,一定知道Solr和ElasticSearch都是基於Luence實現的。但其實這句話蘊含了兩層意思。

  第一層是 Luence支持的功能他們都支持 ,只是概念和用法上稍有不同。Luence這個引擎已經非常強大,我們在使用搜索引擎時看到的絕大部分功能,Luence都可以實現。比如索引時的分詞,查詢時的切面(Facet),高亮,拼寫檢查,搜索建議,相似頁面等等,其實這些都是Luence中實現的功能,Solr和ElasticSearch都只不過做了包裝而已。Luence中不僅有倒排索引,還有TermVector這種每個文檔的正排索引,還有DocValue這種列存結構,不同的數據類型支持了不同的搜索要求。舉例來說,對於按某一個列排序的需求,如果只有倒排索引,拿到每個doc的ID之後再去分別做seek取出列值來做排序,DocValue這種按列存的結構可以很快地取出對應document的列值,而減少磁碟seek操作。同樣,在TermVector中記錄了一個doc中每個term的位置,這是實現關鍵詞高亮的基礎。

  這句話第二層意思是 Luence做不到的事情,Solr和ElasticSearch必須自己想辦法實現 。比如Luence是不支持Document的部分更新的。做為一個資料庫+搜索引擎的混合體,Solr和ElasticSearch如果也不支持Document的部分更新肯定說不過去,所以他們兩者都實現了先把原來的文檔讀出來再寫的邏輯。當然,要讀出原來的文檔就要求文檔的原始數據都在,因此Solr中的部分更新要求schema中所有欄位要麼是stored,要麼有docvalue。而ElasticSearch中則比較激進,每個Document默認都會帶_source欄位,原始文檔都會存在裡面,不用任何配置就可以支持部分更新。另外一個Luence做不了的事情就是文檔的立即可見性。Luence的Document一定要commit刷成Segment後,才能被搜索到。對於一個搜索引擎來講,這無可厚非,寫入的數據過段時間才能被查到非常正常。但是很多人是把Solr和ElasticSearch當資料庫用的,如果寫入的數據不能立刻可見,這對一個資料庫來說是不可以接受的。所以Solr和ElasticSearch都實現了一個「Realtime Get」功能,即寫入的數據,如果是用id去查,是立馬可以查出來的。實現原理也很簡單,Solr/ES里在index前都會先寫log,面對Get請求時,Luence中不可見的數據,可以查log。另外一個有意思的例子是翻頁。在Luence3.5之前是沒有SearchAfter這個介面的,要實現翻頁,Solr的早期版本只能全查出來,然後跳過offset條數據。可想而知,如果用戶深翻,性能有多差。而當Luence開始支持SearchAfter,支持從某一個文檔開始搜索,Solr也開始支持Cursor功能,給上次翻頁的最後一個文檔做個標記,下一頁從這個文檔開始查,大大優化了翻頁功能。

  不止於搜索

  除了單純基於Luence提供的搜索功能,Solr和ElasticSearch還提供了豐富的能力。比如說ElasticSearch的Aggregation能力。ElasticSearch一定是對他的Aggregation功能非常的自信,因為在官方文檔中,Aggregation是介紹完基本概念,安裝部署後的第一章。用戶完全可以把ElasticSearch當成一個分析引擎來用,各種avg,sum,count以及各種聚合方式,都可以實現。Solr也同樣提供了Analytics Component。兩者的分析功能我都沒有使用過,因此我沒有發言權說誰的功能更加強大。ElasticSearch還提供了一個強大的scripting,可以用ES支持的幾種腳本語言來寫一些複雜的求值函數。Solr中也有streamExpression,自定義了豐富的語法和函數讓用戶直接寫表達式來查詢。當然,人見人愛的SQL功能也必不可少,Solr和ElasticSearch均支持SQL和JDBC訪問。兩者支持的SQL功能可能存在一些差異,在這裡我沒有細究。

  分散式架構和高可用

  Solr(Cloud)和ElasticSearch都是分散式架構,支持對索引進行分片,將索引分布到不同伺服器上來實現scale out。同時,他們都支持給每個Shard來設置多個replica來實現高可用。Shard的多個replica都是主從架構,相當於一寫多讀。主從之間都是同步複製。因此在寫入時,一定需要找到主replica,而讀的時候,隨機選擇replica都能讀到最新數據。同時,Solr和ElasticSearch還支持集群間複製,但兩者的實現稍微有些不同,Solr的集群間複製採用推的方式,而ElasticSearch採用的是拉。Solr支持雙向複製,從而能夠實現雙活,而ElasticSearch只能實現主向從的複製來做主備。此外,一些資料庫常用的運維功能,如snapshot,backup&restore,兩者都支持。

  Solr和ElasticSearch不同點

  分散式的設計理念不同

  Solr在最開始的時候是單機版的,並不是分散式架構。當SolrCloud出現時,才有了分散式架構。Solr選用了Zookeeper來做分散式架構下的協調者。Solr中每一個節點都是對等的,Zookeeper的使用主要是用來存分片的路由信息,以及各個replica之間,overseer節點的搶主。Solr中唯一一個不同的角色就是overseer,相當於Solr集群中的master角色,所有的Collection creation等admin操作,都需要進過overseer節點。同時,overseer節點可以根據AutoScalling框架的配置,在節點丟失和新節點加入時,做一些預設的操作。比如節點丟失時,為該節點上的replica自動再add一個新的replica,保持可用replica數為固定值。AutoScalling框架是Solr 7.0才加入的,從這裡開始,Solr才有真正意義上的自動運維,在這之前,Solr的自動運維能力比較弱,就連balance集群,都需要手工去操作。

  ElasticSearch從一出生就是分散式架構設計。與Solr不同的是,ElasticSearch並沒有依賴其他產品來做分散式,而是自研了一套Zen Discovery協議來做分散式協調。因此,ElasticSearch不像Solr(Cloud)那樣依賴一套Zookeeper集群。Zen Discovery把節點發現,選主,廣播等事情全都幹完了。相對於Solr,ElasticSearch的角色更加豐富,除了有與Solr overseer節點類似的master節點,還可以配置只負責index寫入過程中處理複雜ingest pipeline(比如分詞,變換等等)的ingest node,以及不存數據,只負責Coordination(接受用戶請求,發送到對應replica,然後聚合返回客戶端)的node。當然,Solr也可以通過AutoScalling配置某個節點不放任何replica來達到Coordination node的效果,不過這個配置就複雜的多了。ElasticSearch自動運維能力比較強,通過簡單配置,就可以實現集群的自動balance等運維操作。當節點宕機時,master node也會提升從replica為主,並增加一個replica來保持replica可用數。這些都是ElasticSearch的默認行為,而在Solr中,則需要自己去定義AutoScalling的框架,來配置這些行為。

  Solr能夠深度定製而ElasticSearch更重於開箱即用

  最近我solr和ElasticSearch的兩個客戶端都使用過,給我的感覺是ElasticSearch更加簡單易用。有很多的功能ElasticSearch都已經內置,不要通過配置去定義。舉個例子來說,在Solr中我要想定義一個名字為name,類型為string的field,我需要在managed_schema(xml)中配置兩個東西:

  < fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true" />

  < field name="name" type="string" indexed="true" stored="true" required="true" multiValued="false" />

  也就是說,Solr預設的欄位類型仍然需要自己去定義使用Solr中的那個類,比如上面的第一行定義了string這個類型使用了solr.StrField這個類。然後我才能指定id field的type為string。如果我想惡作劇把string定義為solr.IntPointField(int類型)來迷惑大家可以嗎?當然可以。而在ElasticSearch中,各種類型都已經預定義好,我們只需要一個json的mapping來指定field的類型就好(keyword即不分詞的string)。

  PUT my_index

  {

  "mappings": {

  "properties": {

  "name": {

  "type": "keyword"

  } } }}

  Solr的配置感覺上更加Geek,因為他一般都是直接配置java類。而ElasticSearch包裝更好,各種類型都已經預製好。Solr的讀寫鏈路都可以深度定製,你可以在讀寫鏈路上增加各種Processor和Component來添加各種不同的功能。你甚至可以定義處理你請求的handler類。

  < searchComponent name="terms" class="solr.TermsComponent"/>

  < requestHandler name="/terms" class="solr.SearchHandler" startup="lazy">

  < lst name="defaults">

  < bool name="terms">true< /bool>

  < bool name="distrib">false< /bool>

  < /lst>

  < arr name="components">

  < str>terms< /str>

  < /arr>

  < /requestHandler>

  比如上面定義"/terms"路徑將由solr.SearchHandler這個類來處理,同時在Search的component中加入了一個叫做solr.TermsComponent的組件。 如果你願意的話,你可以為每個Collection在訪問「/terms」這個路徑時,提供完全不同的行為。

  再比如下面的配置可以定義一個文檔在寫入時需要經過的Processor(如果熟悉HBase的話,你可以認為就是HBase的Coprocessor)。

  < updateRequestProcessorChain name="add-unknown-fields-to-the-schema" default="${update.autoCreateFields:true}" processor="uuid,remove-blank,field-name-mutating,parse-boolean,parse-long,parse-double,parse-date,add-schema-fields">

  < processor class="solr.LogUpdateProcessorFactory"/>

  < processor class="solr.DistributedUpdateProcessorFactory"/>

  < processor class="solr.RunUpdateProcessorFactory"/>

  < /updateRequestProcessorChain>

  S olr的整條讀寫鏈路都是通過這種配置文件定義的,定製化非常自由,以至於如果配置不好,會把正常的讀寫鏈路給配置沒掉。 所以Solr在文檔中警告,如果你在使用的updateRequestProcessorChain中沒有配置RunUpdateProcessorFactory的話,寫入請求是不會真正被執行的……

  而ElasticSearch很多功能都是開箱即用,並不需要用戶去配置。Solr的配置太過於靈活,給了用戶很多犯錯誤的可能,而ElasticSearch的設計哲學是盡量減少用戶犯錯的可能,ES對運行環境還做了諸多限制,以避免運行過程中出現一些莫名其妙的錯誤,因為很多用戶並不是這些領域的專家,他們沒法從這些錯誤中找到原因。比如說ElasticSearch在啟動時會做memory check,系統是否限制了file descriptor數等等,甚至如果運行的是某個有已知bug的JVM版本,ElasticSearch也會拒絕啟動。ElasticSearch在啟動時還會用JarHell去檢查載入的類里是否有同名類,我曾經在自己的測試工程中集成了ElasticSearch想來啟動一個本地的ES集群,著實被JarHell噁心了一把,在一個大的Java工程里,各種不同的依賴,不出現同名類確實太難。花了半天按照JarHell的報錯提示去exclude依賴之後終於放棄,JarHell檢查還不能被關閉,我只能fake了一個空的JarHell類繞過檢查。總而言之,ES降低了使用門檻,同時還極力避免用戶犯錯,對新手友好。

  Solr支持HDFS存儲而ElasticSearch不能

  Solr能夠支持HDFS做為存儲是Solr的一大特點,on HDFS帶來了存儲計算分離的優勢。比如說,在普通存儲上,move一個replica從一個節點到另外一個節點意味著大量的數據拷貝。而如果on HDFS的話,move一個replica並不需要移動任何數據,每個節點都可以讀到HDFS上的內容,另外一個節點只需要打開HDFS上的數據即可。當Solr on HDFS後,配上AutoScalling框架,其實只需要一個主replica即可(如果不是想分散讀壓力的話)。因為一台node掛掉之後,Shard在另外一個節點快速上線,不需要拷貝數據。當我要balance整個集群時,整個過程也非常快,因為只有邏輯上的shard在節點中流動,而數據在HDFS是不需要移動的。在這個點上,ElasticSearch沒有on HDFS的能力,因此這些他都做不到。

  Solr支持Shard split而ElasticSearch不能

  雖然說Solr/ES的shard是hash分片(根據doc id或者用戶自定義的field),天生就可以分散熱點。但是仍然可能存在某一些doc比較熱,需要分散熱點。或者說如果集群中加入一批新的機器,需要分更多的shard才能保證Collection能夠利用到集群中的每一台。而Solr是支持Shard做split操作,能夠將一個Shard分成多個。而ElasticSearch卻不可以做shard的split,如果一個Index想要更多的shard,只能新建一個擁有更多shard的index,然後將數據遷移過去。為什麼ElasticSearch不能做這樣的操作?這是他們的路由策略決定的。Solr的分片定義了hash的範圍,split時可以將範圍分半,切分出兩個子shard來分別負責這兩段範圍。而ElasticSearch的路由是hash後再對shard取mod,來決定落在哪個shard上,這導致如果新加了shard,取mod就會亂掉。ElasticSearch的shard不能分裂以為這在做規劃時需要非常小心地預估自己的Index將來有多少數據,需要多少個shard。一旦估算錯誤,後期遷移需要大量拷貝數據。

  ES支持功能和生態更為豐富

  ElasticSearch的功能豐富程度確實令人咋舌,畢竟後面有一家非常強大的商業公司。為了吸引客戶,什麼事情都幹得出來(褒義)。ELK套間在Log處理這塊已經是業界通用的解決方案。同時,Elastic公司還通過X-Pack給不同層級的用戶提供了尤為豐富的功能。而Solr背後雖然有一些商業化公司,比如LucidWorks,但總的來說還是沒有Elastic知名,提供的解決方案也比較有限。我在這裡列舉一些ElasticSearch中比較優秀的功能:

  對JSON友好:支持nested field,天生和JSON契合,而Solr中只支持nested docment。

  支持Index Sorting:我覺得這個是在排序場景下的殺手級功能。如果用戶的請求都會帶有某個field的排序條件,ElasticSearch可以在Segment中不是按doc ID排序,而是按照這個field排序。從而在查詢過程中,能夠掃描前n條就可以獲得快速獲得結果集,從而提前完成查詢。

  支持Index的life cycle管理:例如超過多少天,自動刪除Index,如果Index很hot,則增加replica。或者定時對index做一個snapshot等等。

  支持時序數據降精度:ElasticSearch針對時序數據領域的重磅功能。能夠支持配置rollup background job,對一些field數據做聚合,比如把小時數據聚合成天的存儲在另外field或者Index中。

  支持觸發器:當特定的條件滿足時,可以做出一系列事件,比如curl一個網頁,從而實現類似資料庫觸發器的功能。

  總結

  總的來說,我感覺,ElasticSerach更像一個商業產品,而Solr更像一個軟體。Solr的定製能力更強,幾乎什麼都可以配置。對於開發者來說,要實現一個新的功能,可以不用動Solr核心代碼,而給Solr增加一些Processer和Component,然後通過xml配置伺服器的行為。同時,Solr還提供了BlobStore,可以上傳Jar代碼來在Solr集群中部署這些新的插件(像不像HBase的Coprocessor?)。並且Solr獨家的On HDFS能力為Solr提供了存儲計算分離的便捷性,可以做到shard的自由移動而不用搬遷數據。

  Solr還支持分裂shard,這些能力都對運維友好,能夠在擴容、宕機恢復方面會有更大的靈活性。而ElasticSearch竭盡全力地降低用戶的使用門檻,用戶可以非常快的上手。同時堅持不引入像Zookeeper這樣的額外組件,也是為了降低部署難度。畢竟,ElasticSearch後面有一家強大的商業公司,從客戶需求出發,在ElasticSearch上做了非常多豐富的功能,建立起了非常完整的生態,並擁有眾多客戶案例。對於一般的客戶來說,一般會選擇一個大而全的產品去滿足他們的需求,因為在技術棧中引入一個新產品,本身學習成本和運維成本都會比較大。

  而ElasticSearch更加地契合了這些用戶的需求,ElasticSearch入門簡單,不僅可以搜索,還可以分析,同時可以處理時序數據,還在X-Pack中支持機器學習等等功能。我想,這也是目前ElasticSearch更火的原因吧。

抱歉!評論已關閉.