前言
Solr和ElasticSearch到底有一些什麼不同?我在網上搜尋了一些文章,這些文章要麼是列出一個表,詳細地介紹兩者什麼功能有,什麼功能沒有(比較好的一篇
部落格),要麼是從大類出發(其中比較好的一篇
文章),比較兩者的關注度,社群等等。但看完這些文章,還是沒法解決我心中的疑惑。最近由于項目的原因,Solr和ElasticSearch都有所使用。最近又把solr和ElasticSearch的官方文檔都過了一遍。對兩者有了一些淺顯的見解。是以在這裡想跟大家分享下我的一些看法。在這篇文章中,我不會列出一個清單來說明兩者的異同,而是抽出我覺得比較重要的幾點來講一講。本文的對比基于Solr7.3和ElasticSearch7.4。兩者都在快速疊代,可能有一些最新的進展我沒有考慮到,同時我接觸搜尋引擎的時間不長,難免有一些錯誤或者我沒關注到的點,歡迎大家指正。
相同點
兩者都基于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在最開始的時候是單機版的,并不是分布式架構。當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>
Solr的整條讀寫鍊路都是通過這種配置檔案定義的,定制化非常自由,以至于如果配置不好,會把正常的讀寫鍊路給配置沒掉。是以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更火的原因吧。