20

Solr vs ElasticSearch,搜索技术哪家强

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzU5OTQ1MDEzMA%3D%3D&%3Bmid=2247487374&%3Bidx=1&%3Bsn=302a50e6d75de1823666075a033f7198
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

Solr和ElasticSearch到底有一些什么不同?我在网上搜索了一些文章,这些文章要么是列出一个表,详细地介绍两者什么功能有,什么功能没有(比较好的一篇博客 https://solr-vs-elasticsearch.com ),要么是从大类出发(其中比较好的一篇文章 https://logz.io/blog/solr-vs-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>

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中比较优秀的功能:

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

  2. 支持Index Sorting:我觉得这个是在排序场景下的杀手级功能。如果用户的请求都会带有某个field的排序条件,ElasticSearch可以在Segment中不是按doc ID排序,而是按照这个field排序。从而在查询过程中,能够扫描前n条就可以获得快速获得结果集,从而提前完成查询。

  3. 支持Index的life cycle管理:例如超过多少天,自动删除Index,如果Index很hot,则增加replica。或者定时对index做一个snapshot等等。

  4. 支持时序数据降精度:ElasticSearch针对时序数据领域的重磅功能。能够支持配置rollup background job,对一些field数据做聚合,比如把小时数据聚合成天的存储在另外field或者Index中。

  5. 支持触发器:当特定的条件满足时,可以做出一系列事件,比如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更火的原因吧。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK