基于ES的高可用搜索服务架构

作者/分享人:light
查看本场Chat

搜索,对大家而言既耳熟能详又广为使用,且与我们日常生活密切相关,相信大家每天都离不开Google或百度等,电商像淘宝、京东、亚马逊都提供了非常强大的搜索功能。本文结合当前主流开源搜索产品ElasticSearch,从技术角度向大家介绍高可用搜索服务架构。本文主要内容如下:

  1. ES可以为搜索做什么?
    • 搜索框自动补全提示、导航筛选器、文档列表
  2. 全方位对比主流搜索框架:Solr vs ES
    • 直观印象:插件Head、Marvel
    • 二者近年发展对比
    • 二者使用综合对比
  3. 服务实现相关技术:
    • jetty、thrift、avro等简介
  4. 搜索高可用服务架构演变
    • 异地双集群热备(基于Zookeeper注册监听机制)架构
    • 索引数据分层效果对比

1. ES可以为搜索做什么?

这里我们以电商搜索为例,当前一个完整的搜索产品一般包括:搜索框自动补全提示、导航筛选器和商品列表三部分,如下两图所示。

enter image description here
图1 搜索框自动补全提示

图1所示为当用户在搜索框输入2个拼音字母“xx”时,搜索框内返回数据一般根据本站商品热销品类、品牌及热搜关键词及其拼音简写等组成的专有词库,可以使用ES本身提供的suggestion使用前缀匹配方式给出一系列可能满足用户意图的关键词列表。

enter image description here
图2 导航筛选器、文档列表

图2所示为在图1基础上,选择“休闲鞋”所得到的导航筛选器和商品列表页,其中导航筛选器中的“品牌”、“价格”、“尺码”等可以利用ES本身的Aggregation的文档聚合功能得到每个导航筛选属性项下每个属性值的相关商品个数;其中商品列表就可利用ES本身的QueryBuliders提供的queryStringQuery、termQuery或filteredQuery等各种形式的查询构造器,从索引库查询相关商品返回。

2. 全方位对比主流搜索框架:Solr vs ES

目前主流搜索技术,以开源界大名鼎鼎的Lucene为首,鉴于Lucene固有的一些缺憾:比如不支持分布式等,在Luene基础之上出现了2个面向企业级搜索应用:Solr/SolrCloud 和 ElacticSearch,下面给大家对比介绍二者。

2.1 直观印象认识

enter image description here
图3 SolrCloud集群

图3所示为一个包含"house"、"news"和"jiaju"三个索引,每个索引包含3个shard的SolrCloud集群,通过它,可以查看集群状态、查看索引配置及数据、优化索引及查看日志等。

相比较,ES主要依靠各种插件机制来完善其功能,下面给大家介绍2个广为使用的插件:head和marvel,一般二者结合可以更好的辅助运维人员监控ES集群。

enter image description here
图4 Head插件

通过head插件可以查看整个ES集群的健康状态(绿色:健康、黄色:危险、紫色:正在调整、红色:异常)、每个索引组成shard及replica、创建或删除索引、索引相关字段、浏览每个索引数据、根据条件组合查询等。

enter image description here
图5 Marvle插件

通过Marvel插件可以帮你查看某个时间点的索引doc查看数量、索引搜索请求QPS、索引更新QPS,还可以查看集群中每个索引节点的CPU、Load、内存及硬盘剩余空间使用情况。一般根据NodeServer的CPU及Load情况,插件中会有不用颜色提示,白色:正常、棕黄色:有压力、红色:危险。

2.2 Solr和ES今年发展情况

这里笔者借鉴DB-Engines中2016年12月份给出的2张图说明近几年二者发展情况,帮大家一目了然的看清其发展形势。

enter image description here
图6 DB-Engines中312个DB中前15位

enter image description here
图7 ES和Solr近4年发展形势

来自DB-Engines的上两图图6和图7给大家展示了ES和Solr在312个DB中的综合使用情况及二者近4年各自发展,从图中可以看出,2013、2014年在搜索中Sol r使用情况占绝对优势,不过我们可以看到ES迎头赶上的趋势,乃至2015年年底ES在市场使用率已经超过Solr,群众的眼光是雪亮的,笔者本人也是从最初的Solr使用者和支持者转向了ES使用者和支持者,从而更是深有体会。

2.3 Solr VS ES

笔者曾对Solr和ES专门做过对比实验,实验结果如下图8所示。

enter image description here
图8 Solr vs ES综合对比

集群搭建时: SolrCloud需要依赖第三方Web 容器 (Jetty、 Tomcat)、需要借助 Zookeeper 进行资源管理调度等;而 ES 本身不需要依赖第三方软件,仅仅做些简单配置即可完成集群搭建。生产环境下,集群基础环境所依赖的软件越多,其出错的可能性就越大,而且集搭建及维护更为麻烦。

2.4 ES重大版本变化

ES发展至今,最新版已经是Version5.1,纵观其发展历程,其中有2个版本值得大家重视:Version2.0和Version5.0。其中:Version2.0相比1.x已经在很多API层级不再兼容,这里简单说2点:

  1. Mapping中索引类型,已经不再有 index_analyzer类型;
  2. FilterBuilders.andFilter/orFilter改为由BoolQueryBuilder的must/should代替。

Version5.0相比较,变化更大,新版索引会大大减少索引占用空间、提高查询性能,而且之前过往的“ttl”特性已经取消等。

这里值得一提的是,ES自2.4版本后,不再有3.0、4.0,而是为了其系列产品ELKB统一版本号,直接将其版本号提升到5.0。

3. 相关实现技术:webservice/rpc

当前微服务架构主流实现技术包含两种:Webservice和RPC。对于搜索服务同样也不例外,实际使用时:一般直接面向终端的服务以WebService实现为主,后置服务多用RPC形式,这是因为前者使用方便,便于调试,而后者性能更优。

3.1 WebService

当前基于Java实现的WebService技术主要有两种:基于外置容器(tomcat/resin/jboss)的SpringMVC Rest WebService和基于Jetty内置容器的Servlet实现轻量级WebService。

enter image description here
图9 Jetty内置Servlet实现ws

如图9所示为笔者实现的一个多个ES集群健康状况监测的WebService,短短20多行代码,只要运行该当前应用即可启动服务,使用非常方便简单。相比较基于SpringMVC RestAPI,虽然SpringMVC封装的很好,使用也很简单,但毕竟需要外置Web容器才能启动相关服务,对运维来说不仅要监控应用服务,还要监控Web容器服务,增加了运维负担。因此这里建议读者朋友也使用更加轻量级的WS实现方式。

3.2 RPC

当前RPC常用实现技术主要包括以下几种:

  • Thrift/Nifty: Facebook开源的高效rpc方案,nifty是thrift和netty的整合,已逐渐替代thrift,通过IDL生成目标开发语言API代码,支持目前常见的各种开发语言。
  • Avro: hadoop衍生的序列化框架及服务, 性能可以媲美thrift, 同thrift类似,也通过IDL生成目标开发语言API代码,目前常用开发语言基本也都支持,但起步比thrift稍晚。IDL灵活性表现在不用像thrift每次调整API,都要生成目标代码。
  • Dubbo/Dubbox:前者淘宝开源,后者是当当做了进一步优化,目前好久没有更新。
  • JWS: jdk自带,比较轻量级,但缺乏高效的序列化支持,需要借助第三方,如果和probuf结合,性能会有很大提高。
  • Zero-ICE:比较完善,但比较重量级,淘宝数据库引擎MyCAT底层通信就用的它。 上述几种方案,笔者建议大家考虑前2种,虽然二者不像dubbo、zero-ice是一个完整的SOA解决方案,不支持集群负载等,但我们只需要通过第三方LVS/HAproxy或Zookeeper就可以很容易实现集群负载等功能。

4.搜索服务架构完善升级转变过程

搜索发展到现在,经历了很多架构演变以适应当前变化万千的业务需求和日益增长的海量数据,以满足其高效性和健壮性要求。

4.1 单一库

初始阶段,由于数据量和访问量都很小,一般单一索引库就可以满足相应业务需求。这种架构当前实际使用比较少,以Lucene为代表,一般用于研究及测试等。

4.2 一主多从(replica)

随着业务需求的复杂性和用户访问量的增长,单一索引库很容易引发资源不足瓶颈,这时传统意义上关系数据库一般都会考虑的主从架构、读写分离也随之引进。这种架构以Solr4.0版之前版本为代表,比如Solr3.6仅支持主从结构,master节点用于索引更新、replica节点用于索引查询,这种一主多从架构一定程度上解决了相当一部分问题。

4.3 数据分区shards及近实时索引

当数据量日益增大,单一索引结构毕竟容纳有限,而且我们需求层面也需要将刚刚发布的信息立马能够让用户检索到,这时前述架构已经不能满足需要,相应的Solr4.0所加入的SolrCloud以及LinkedIn推出的ElasticSearch就很好的解决了该问题,当前ES诞生之初就支持分布式Shards及索引实时更新,而SolrCloud也是借鉴ES功能点而逐渐完善的。

所谓Shards,即数据分区,就是将原来逻辑上、物理上均为一份的数据调整为逻辑上是一份、物理上程多份分散在多个服务器的一种模式,这样可以大大提高应对海量数据增长的需求。

所谓实时索引,也即Soft Commit(软提交,索引在内存)和Hard Commit(硬提交,索引在硬盘)的定时Merge,一旦内存中索引达到设定的阈值,则开始Merge到硬盘。一般会有2块内存互相扶助完成,系统启动之初,一块内存迎接最新索引数据,另一块闲置;当第一块索引达到内存阈值,则不再接受新的索引,而同时启动第二块内存迎接新索引,第一块索引开始merge到硬盘,然后第一块内存置空后,等待第二块内存达到阈值,开始迎接新一轮的索引请求,如此周而复始。一般借助MQ完成索引实时更新,目前常用MQ主要有RabbitMQ和Kafka。

4.4 多集群模式(异地双备等)

随着业务增长,系统稳定性和健壮性日益重要,尤其是如何应对突发不可抗拒的网络故障、硬件故障等,这时我们要想保证搜索服务的HA高可用,单纯依靠当前的开源软件可能无法满足需要,这里笔者以自己过往经验,给大家提供一种异地双备架构模式,如图9所示。

enter image description here
图9 异地双集群互备

如图9所示,搜索服务和索引更新服务都需要借助Zookeeper监听当前可用的ES集群;而ES集群监控检测服务则定期每隔K(K一般小于5)秒自动检测2个异地集群健康状况,根据每次探测结果进行相应Add/Del操作。

正常情况下,异地双备集群对于搜索服务可以起到分流作用;而对于索引更新服务,则需要完成对2个集群的并发异步写入,以保证2个异地集群数据保持一致。

一旦发生异常,比如,有某个ES集群出现短暂网络故障或硬件故障,这时ES集群检测服务就会立马删除其对应的服务,而搜索服务和索引更新服务也会做相应调整,从而保证顶多在故障发生的K秒时间内完成故障迁移,不至于对系统整体HA造成太大影响。

4.5 索引分层(数据量比较大,主要考虑性能)

随着业务稳定发展,对于时效性要求比较高的应用,如新闻APP等,一般几年前的新闻对用户意义不是很大,如果任由发展下去,搜索时往往会在一个大而全的索引库根据时间过滤只查找很少一部分数据,这种方式问题主要包括下属2方面:

  • 大索引一旦发生故障,很难恢复;
  • 大索引查找效率比较低。

这时,我们自然会想是不是可以考虑根据时间做一个只保留满足业务需求时间跨度最大的索引,过时的内容放在一个备用索引库,而且整体保持滚动更新。答案是肯定的,下图10所示即为我们做过的实验效果对比。

enter image description here
图10 索引分层效果对比

图10所示为我们做过的一个索引分层实验Metrics记录的时效性对比,最下面黄色曲线为实时索引分层后的搜索耗时,上面彩色曲线集即为未做分层索引的搜索耗时。从实验数据可以明显看出索引分层的效果。

5. 总结

本文既有对ES功能插件的使用说明,又有和同行竞争对手Solr的多方位对比,接着给出当前行业主流微服务技术实现方式:Webservice和RPC的多种实现方式,并给出简单比较,最后结合近年搜索发展中所遇到的问题,逐步给出一个可以解决实际问题的高可用搜索服务架构,这里的高可用重点在解决服务健壮性、容错性和高效性,希望对读者朋友有帮助!


本文首发于GitChat,未经授权不得转载,转载需与GitChat联系。

sokox
三分之二的内容在做比较
BIGBIGBOAT
讲的挺好的,到我不知道es怎样zk来监听
light: 谢谢,文中有个完整架构,到时我再详细说下
江伟
老师能详细说下es集群监控这块吗?比如怎么获取健康值和节点状态,发生异常后第一时间自动告警给相关人员
light: 嗯 好的 没问题
可乐
周三讲座时可否多讲讲ES在日志分析方面的实施案例?
张峰
几个问题请教下: 1.ES 的多个集群能否做到平滑切换,比如我希望在每天重建索引的时候一个ES集群先重建然后再重建另外一个;2.关于ES 的发布,现在偶尔会修改ES 的配置,这样每次都必须修改每个节点,有时候办法可以通过统一部署来解决修改配置问题吗?3.对于分词这块,你们是如何维护的?词库如何动态添加或发现新词?4.如果想深入了解底层机制,如何学习?多谢。
泛泛之辈
我们目前在用solr实现全文检索,每天大概几十t的数据量吧,但在实时检索上达不到,一般需要一天的时间才能把前天的数据都导入到solr中,如果采用es的话,能做到实时检索吗?看文章里es的导入速度更慢,另外,如果迁移的话,代价会很高吗?有文章说es的上手比较成本高。谢谢。
Levin
文中说当数据量变大时,加多一些索引。那么有没有这个数据量和索引什么配对比例性能最优, 或者说不同索引不同分片几种方案的对比得出最优方案,这方面有什么具体指标? 当遇到搜索性能瓶颈时,分析解决思路树,比如遇到查询慢了是该加索引,大索引,做多分片?
Levin
再问一个问题,上面说了不用springmvc,用了jetty。后端RPC最后选了哪个?之后跟zk,mq等等组件一起构建时候,你们的项目用了Spring cloud吗? 能否分享一点点service,后端架构细节,来几个无码大图也可以😄。
孙博
多讲讲es内部的一些实现,感觉偏运维多,希望多一些面向开发的。比如并发情况下跨多个索引进行查询性能调优
helloworld
搜索的话题很感兴趣,我比较推崇elastic公司的布道师medcl,能把elasticsearch的原理、应用、发展方向讲明白
超越
高可用性那部分,俩个ES集群怎么确保数据的一致性
诗卉
以前使用ES做过全文检索,觉得很吃机器配置,而且当时不少工具与版本号的兼容性也很差,后来就闲置了,来看看现在的发展
曹林华
你好,目前我们正在做拼音搜索。我们是在建立索引的时候,用ik和mecl分词器同时建立中文和拼音的2个字段,请问你们这个方案可行否?还有你们做拼音搜索的时候是怎么解决的?
邓超
请教几个问题 1. ES存储多对多的关系,在两类实体都可能被查询的情况下,怎么设计比较合理?比如说针对用户的标签系统。既可能会根据标签查用户,也可能会根据用户查标签,同时,可能还会根据打标签的时间来筛选标签。 2. 如果一个ES集群中有多个索引,是否有办法调节每个索引所占的资源。比如说。一个ES集群中,包含一个重要业务的索引,同时也包含了一个用于BI的索引。能否有办法限定BI的索引占用的资源比例。
MAO Xuchu
1)词库更新后 以前建的索引是不是要重新建?如果要,请问有什么注意事项? 2)类似于关系库的联合查询,在es中是如何实现的? 3)对于搜索结果的评分如何设计更加科学,能举例说明么?
赫阳: 问题收到,稍后8:30开始在群内解答
helloworld
PB级规模、上百类数据需要提供实时查询,怎么规划部署?单机用什么规格的CPU、多大容量内存、多大容量磁盘、什么类型磁盘?集群存储几个副本?
yuhi
请问是怎么保证两个机房的es的数据一致性的?我看到文章中的做法是双写,但是如何保证一个机房写入失败的情况的?如果数据量非常巨大的情况下有没有什么好的补偿机制?
微信扫描登录