微信扫描登录
或者
请输入您的邮件地址来登录或者创建帐号
提 交取 消
GITBOOK.CN需要您的浏览器打开cookies设置以支持登录功能

王成光:ES高可用搜索实战解析
作者:Chat 实录

12月28日周三晚8点30分,前网易技术专家、搜狐架构师王成光带来了“基于ES的高可用搜索服务架构”的主题交流。以下是主持人赫阳整理的问题精华,记录下了作者和读者之间问答精彩片段。


问:请问ES怎样ZK来监听?能详细说下ES集群监控这块吗?比如怎么获取健康值和节点状态,发生异常后第一时间自动告警给相关人员。

答:大家可以先参考下图:

借助ZK监控ES集群一般由2部分组成:

  1. ES集群健康监控服务:一般为一个WebService,此服务在启动之初,利用ES客户端集群管理API判定每个ES集群是否正常,将正常服务的ES集群在ZK中创建一个与其相对应的指定Path,即完成ES集群服务注册;然后由运维帮忙设定一个针对该WebService的心跳检测,例如每隔3秒使用ClusterStatsResponse探测一下所监控的每个ES集群的健康状况(绿:健康,黄:有潜在风险,红:异常,例如索引丢失、服务不存在),根据服务返回结果标识,一旦发现异常即可报警。

  2. 搜索服务/索引更新服务: 在系统启动之初,便开始监听来自ZK中代表该服务所用相关ES集群的特定Path,根据监听消息:Add或Delete,自动Add或Delete代表相关ES集群的客户端连接,从而可以保证在异地ES集群之一发生故障时,该集群对应客户端已经删除,而另一个热备ES集群还可以继续使用,而不影响现有线上搜索服务。


问:可否多讲讲ES在日志分析方面的实施案例?

答: ES在日志分析方面主要借助其比较强大的ELK组合,其典型应用实例可以参考这篇文章:http://www.importnew.com/20464.html,该文章介绍了ELK工作基本原理,最后以收集log4j日志为例介绍。

随着Beats发布及应用,现在ELKB组合更受欢迎,大家可以参考这篇文章对为什么引入Beats以及Beats不同类型做了更详细的介绍。


问: ES 的多个集群能否做到平滑切换,比如我希望在每天重建索引的时候一个ES集群先重建然后再重建另外一个。

答: ES多个集群想做到平滑切换,就要保证多个集群相同索引的数据一致性,多集群数据一致性保证:一般通过索引写入API的封装,比如,向2个ES集群的相同索引插入一条数据或一批数据,这时可使用JDK8提供的CompletableFuture异步请求,异步并发写到2个集群,以提高效率。

问:关于ES 的发布,现在偶尔会修改ES 的配置,这样每次都必须修改每个节点,有时候办法可以通过统一部署来解决修改配置问题吗?

答: ES集群搭建之初,一般全局性配置JVM对内存、脚本功能是否开启、默认Shard数量和Replica数量等在配置文件统一配置好即可。

ES一个特色在于,对于索引Mapping的动态配置,可以借助脚本,动态调整. 例如常用的创建、修改、删除索引://创建一条包含3个shard,每个shard包含1个replica的 空索引“light” curl -XPUT

'http://127.0.0.1:9200/light' -d '{  
    "settings" : {
          "index" : {
             "number_of_shards" : 3,
             "number_of_replicas" : 1
        }
    }
}'
//为该索引light增加一个字段“name”
curl -XPUT 'http://127.0.0.1:9200/light/tdoc/mapping' -d '{
        "tdoc" : {
        "properties":{
            "name" : {
                        "type" : "string",
                        "include_in_all" : false
                  }
        }
    }
    }'
//删除该索引
   curl -XDELETE 'http://127.0.0.1:9200/light/'

利用这些ES本身的语法,结合脚本,实现统一灵活部署。

问:对于分词这块,你们是如何维护的?词库如何动态添加或发现新词?

答: ES插件对中文分词支持比较好,比如 IK, mmseg, ansj等,如果想实时更新专有词库,目前可以考虑2种开源方案:

  • IK: 监听一个远程http访问的词库,定时默认每10秒根据其lastmodifytime是否更新来重新加载词库,实现词库近实时更新;
  • ANSJ: 通过借助Redis的pub-sub机制,实现新词添加或旧词删除。

问:如果想深入了解底层机制,如何学习?

答:一般先学会使用,然后带着问题逐渐深入源码,分析相关问题,从而加深理解。


问:我们目前在用solr实现全文检索,每天大概几十t的数据量吧,但在实时检索上达不到,一般需要一天的时间才能把前天的数据都导入到solr中?

答: 每天数据量几十T属于比较大的数据,全部写入一个索引中,索引会比较大,这时即使换成ES,实时检索效果也不见得有效果。因此存入索引时,是不是可以考虑做些优化,建议如下3点:

  1. 这些数据在存入ES时是不是可以考虑精简,只把涉及查询的字段存入索引;
  2. 存入ES的索引在mapping设置时,不要设置store属性为true,因为所有字段默认都会在_source中存在,再设置store属性为true,相当于存了2份数据;
  3. 一天数据量达到这几个级别,为提高效率,是不是可以考虑索引按小时存放,减小单个索引的数据量; 查询时,根据时间跨度,异步并发从多个小时段内的小索引查找再merge到一起。

问:如果采用ES的话,能做到实时检索吗?看文章里ES的导入速度更慢。另外,如果迁移的话,代价会很高吗?有文章说ES的上手比较成本高。

答: ES和Solr底层都是基于Lucene,其现有索引数据理论上是兼容的,对于迁移,把Solr中索引字段都变更到ES的mapping设置好,理论说代价不会太大,这个需要具体做下实验。ES和Solr比较,个人认为:感觉ES使用更简单,上手更快,ES集群搭建比SolrCloud集群搭建要简单很多,其官方提供的ES-client使用也非常全面。


问:文中说当数据量变大时,加多一些索引。那么有没有这个数据量和索引什么配对比例性能最优, 或者说不同索引不同分片几种方案的对比得出最优方案,这方面有什么具体指标? 当遇到搜索性能瓶颈时,分析解决思路树,比如遇到查询慢了是该加索引,大索引,做多分片?

答:索引拆分,这个需要根据具体业务来定,文中提到的方案,是因为一般新闻查询都有时效性要求,对于一些从来不会检索到的内容,如果还维持在一个大索引库里,就会造成这个大索库非常臃肿,查询效率低下,因此需要一个只保留满足业务需求最大时间跨度的新闻即可,而且需要滚动更新,每天将超时doc移植到过时索引库,以提高效率。

遇到慢查询:是该加索引,大索引,做多分片?这个问题 还要看总体数据量和查询业务需求,以及当前性能瓶颈,这个目前没有普适指标要求。不过一般而言,索引分片不易过多,也不易过少,一般单个shard索引数据量达到100G以上,就可以考虑是不是索引分层。

最新ES5.0会大大减少索引占用空间、提高查询性能,这个值得实验下。

问:上面说了不用springmvc,用了jetty。后端RPC最后选了哪个?之后跟ZK,MQ等等组件一起构建时候,你们的项目用了Spring cloud吗? 能否分享一点点service,后端架构细节,来几个无码大图也可以。

答:之所以不选择springmvc而实用Jetty实现 webservice,在于其更加轻量,简单易用。后端RPC,一般使用thrift或其结合netty的优化升级版nifty。SpringCloud是一个比较强大的开发工具,也相对比较重量级,有一定学习成本。SpringCloud结合SpringData大有一统天下之势,做了大量封装,让使用相关组件相对简单。具体应用中,是否选用SpringCloud、SpringData、SpringMVC要靠所在开发团队技术偏好,毕竟现在时团队并肩作战。

对于创业企业,要求短平快,一般尽量使用轻量级技术框架快速解决问题。


问:可否多讲讲ES内部的一些实现,感觉偏运维多,希望多一些面向开发的。比如并发情况下跨多个索引进行查询性能调优。

答: ES对于开发而言,使用比较多的功能模块一般集中在: TransportClient(ES连接客户端)、suggestion(建议)、aggregation(聚合)、QueryBuilders(查询构造)、SearchRequestBuilder(查询请求构造器)和ClusterAdminClient(集群管理),此外有关ES的字段类型以及索引实时更新原理(文中已经提到)。

并发情况下跨多个索引查询性能优化,建议如下:

  1. 一次跨索引检索请求,转化为应对多个索引的异步并发请求,可以参考前述提到的JDK8提供的CompletableFuture,这样对于多个索引请求,返回结果依赖于耗时最长的那个索引检索耗时。
  2. 对于比较复杂的查询请求,QPS比较高时,可以使用GuanvaCache本地缓存(限时限个数)相同条件的检索结果,为减少ES集群压力。 其中查询条件一般比较多,这里我们可以考虑使用MD5值对每次查询条件加密,以缩小GuavaCache中缓存key的大小。

问:高可用性那部分,俩个ES集群怎么确保数据的一致性?

答:多个集群相同索引的数据一致性:一般通过索引写入API的封装,比如,向2个ES集群的相同索引插入一条数据或一批数据,这时可可以在该次请求中:使用JDK8提供的CompletableFuture异步请求异步并发写到2个集群,以提高效率。

同时还需要在索引中增加一个“addTime”的时间戳,最好是形如:“20161228180629” 这样的形式,一旦有某个集群故障,回头弥补数据时,还可以从服务正常集群中,根据故障缺失时间段查找到相关doc,再补上。


问:目前我们正在做拼音搜索,是在建立索引的时候,用ik和mecl分词器同时建立中文和拼音的2个字段。请问你们这个方案可行否?还有你们做拼音搜索的时候是怎么解决的?

答:你的方案可行,我之前这么做过。

拼音搜索,一般考虑全拼和简拼,这里汉字转拼音,可以考虑使用pinyin4j的api,也可以考虑使用es的拼音插件


问:ES存储多对多的关系,在两类实体都可能被查询的情况下,怎么设计比较合理?比如说针对用户的标签系统。既可能会根据标签查用户,也可能会根据用户查标签,同时,可能还会根据打标签的时间来筛选标签。

答: ES支持关联查询,但其效率较低,尤其是针对大数据集,生产环境下实用还需要再完善。 对于这类关联实体查询,可以考虑像关系数据库设计那样,每个索引增加一个类似于外健的字段。比如你这里提到的用户标签系统: 设置2个独立的索引,标签索引(其中含有打标时间)和 用户标签索引(userId,tagId),就可以满足根据标签查用户,也可能会根据用户查标签。

对于根据打标签的时间来筛选标签: 这个需要根据打标时间再标签索引库首先筛选出满足要求的标签ID集合,再使用该tagId集合再像用户标签索引库过滤相关用户等。

问:如果一个ES集群中有多个索引,是否有办法调节每个索引所占的资源。比如说。一个ES集群中,包含一个重要业务的索引,同时也包含了一个用于BI的索引。能否有办法限定BI的索引占用的资源比例。

答:据我个人了解,目前还没有办法调节每个索引所占的资源。ES集群虽然可以包含多个索引,但一般不建议集群中含有过多索引,尤其是资源竞争比较强烈的不同业务类型的索引。可以按垂直业务拆分,为每个独立的垂直业务设立一个独立集群。


问:请问现在做多语言分词,有什么好的推荐插件,比如韩语或者泰语。另外汉语基础词库是否用IK自带的搜索词库就可以了,或者有什么更优的推荐吗?

答:多语言,尤其是小语种这块,了解不多。平时主要集中在中文和英语。对于中文分词,IK确实效果不错,尤其是结合前述说的ES-IK插件支持实时更新专有词词库。具体业务而言,IK不一定比其他分词器效果好,像jieba、Ansj、MMseg4j、paoding等,还有些根据条件随机场和隐马尔可夫模型算法的中文分词组件,效果都不错,具体使用哪个,需要结合业务,看实验效果。


问: ES可以基础数据的压栈么?比如只想保存某个key的100条?

答:应该可以满足你的需求,因为:ES本身对定义的字段类型就可以支持数组列表方式。此外,ES支持嵌套结构,Embedded类型索引数据,其中内嵌索引结构可以是一个独立的元数据信息。


问:词库更新后 以前建的索引是不是要重新建?如果要,请问有什么注意事项?

答:词库更新后,新词是不会对原有索引有效果的,新加入索引会有效果。如果重建索引,注意词库更新的时间,以及最好不要影响用户使用。重建索引,为避免影响用户使用体验,可以考虑 文中提到的 异地双主集群模式, 在客户端指定用户访问哪个es集群。这样,2个ES 集群,一个重建,一个正常使用;然后轮循操作,直到2个集群都完成索引重建。

问:类似于关系库的联合查询,在es中是如何实现的?

答: ES通过mapping来指定文档的nested-parent关系,实现关联,具体查询时指定has_childhas_parent父子查询。

问:对于搜索结果的评分如何设计更加科学,能举例说明么?

答: lucene默认评分或后加的 bm25 相关性评分 都是基于 tf/idf,更加科学合理的评分: 一般都利用ES简单查询处候选集,然后结合具体业务,使用特定的数学模型,计算相关性平分。


问: ES有成熟的离线建索引在线load的解决方案么?ES发生脑裂之后怎么恢复比较好呢?一般实时索引更新,都是结合MQ完成。之前有看过yarn启动es建索引之后把snapshot弄下来更新到生产环境的,但是这个项目好久没有人维护了,所以想问一下类似的方案有没有靠谱的。

答:可以理解是要实现实时索引更新的意思?一般实时索引更新,都是结合MQ完成。元数据生成方 将生成的最新doc原数据信息 发到指定的MQ,MQ消费端 异步对所接受的数据实时插入索引。一般我们实时索引都是通过MQ, 对于并发比较高的写,一般MQ消费端,定时每隔1、2秒集中写一次数据。


问: 我们用ES有段时间了,经常会在异常关闭之后导致分片不可以,有什么建议?另外,单节点最大写入能做到多少?

答:一般索引太大时,写入效率会大大受影响。所以文中曾提到对于数据量比较大的索引考虑索引拆分问题,单节点一般200G是没有问题的。


问: 个性化搜索一般是怎样的做法,比如用户搜 酒店,可能根据用户的画像拿到一些偏好,比如星级啊什么的,改写query的方式做个性化吗?

答:这个问题是搜索和推荐的结合,实际上搜索和推荐界限很模糊。这里建议:首先利用用户标签集合,从酒店标签索引中查询处酒店候选集,对候选集,在使用一定数学模型综合排序, 然后取topN 返回。关键看你们设计的数据结构。我出版的书中,最后一章是一个完整的用户画像实时更新实例,可以参考一下。


问:可以讲一讲ES的灾难恢复吗?

答: ES从诞生之初就支持shard+replia方式。ES灾难恢复:就是借助 shard + replica 模式, 数据冗余。

问:如何优化es实时索引的性能?

答:现在所谓的实时索引 准确讲 只能叫 近实时索引更新,SolrCloud和ES, 都是借助内存存索引soft commit和硬盘索引hardcommit相结合方式。一般默认每秒索引 刷新一次 实际中,考虑到集群压力问题,会设定大于1秒。实时性本身也跟索引规模有关,就是之前说的分片shard,索引拆分等,到一定阶段,都需要综合考虑。

问:在建立索引的过程中,如何减轻Elasticsearch在merge大的segments时对CPU资源的消耗?

答:一般设定merge临届条件 要根据服务器负载情况 设定一个宽松的范围,避免影响前端搜索性能

问: ES倒序索引的优势是什么?

答:搜索引擎从诞生至今,其速度优势就在于倒排索引,和传统关系DB相比较,其优势在于,要查找的关键词,事先已经知道存在于那些doc中。


问:ES在索引的时候原始数据一定不保存么? replica选主机制是怎样的?

答: replica选主机制 就是借助Zookeeper的选举机制。第一个问题中, 我详细介绍了使用ZK 的服务注册和监听工作流程。选举,前提也是首先完成服务注册,然后replia选主,一般会有多种方式,典型的按负载或轮循等。


问:公司使用 Sphinx和solr,目前查和排序有问题,现在想合并索引,请问一下有哪种较好的可行方案,Sphinx数据是从几十张表更新索引的,solr 是从一张大表更新索引。每次搜索同时搜索 Sphinx和solr这样排序就有问题第二页有可能会比第一页价格高。

答:因为这样排序,没有全局性。现在我们想合并使用一个索引, 目前项目使用solr, 我们是用solr好还是es好。因为ES dfs查询的原理: 向索引查找返回50个doc, ES 集群收到请求后,会同时向各个shard 请求,每个shard返回top50, 然后再对所取回的各个shard 的top50合并,统一排序,最后得到的top50才是最终结果。你们目前Sphinx索引来自几个表的数据,Solr来自一个大索引,是不是也可以考虑采用类似模式。

如果将2者合并到一起,可能你们会有很多额外开销,是不是可以考虑将2者得到的满足要起的结果,再次排序返回呢?这样就会避免你说的排序问题。


问:ES怎么做数据隔离和权限控制?

答: ES本身并没有数据隔离和权限控制,个需要借助额外功能设计。ES可以看作一个查询能力比较强大的NoSql。

Chat文章:《基于ES的高可用搜索服务架构》


在此感谢人民邮电出版社,为本场Chat的获奖读者提供了《自制搜索引擎》一书。

enter image description here


enter image description here

enter image description here