
背景
随着公司业务的高速发展以及数据爆炸式的增加,当前公司各产线都有关于搜索方面的需求,可是之前的搜索服务系统因为架构与业务上的设计,不能很好的知足各个业务线的指望,主要体现下面三个问题:html
- 不能支持对语句级别的搜索,大量业务相关的属性根本没法实现
- 没有任何搜索相关的指标评价体系
- 扩展性与维护性特别差
基于现状,对行业内的搜索服务作出充分调研,确认使用 Elasticsearch 作底层索引存储,同时从新设计现有搜索服务,使其知足业务方对维护性、定制化搜索排序方面的需求。node
总体技术架构
沪江搜索服务底层基于分布式搜索引擎 ElasticSearch,ElasticSearch 是一个基于 Lucene 构建的开源,分布式,Restful 搜索引擎;可以达到近实时搜索,稳定,可靠,快速响应的要求。算法

搜索服务总体分为5个子系统数据库
- 搜索服务( Search Server ) : 提供搜索与查询的功能
- 更新服务( Index Server ) : 提供增量更新与全量更新的功能
- Admin 控制台 : 提供 UI 界面,方便索引相关的维护操做
- ElasticSearch 存储系统 : 底层索引数据存储服务
- 监控平台: 提供基于 ELK 日志与 zabbix 的监控
外部系统接口设计

- 查询
- 查询接口提供 http 的调用方式,当出现跨机房访问的时候,请使用http接口,其他均可以使用 dubbo RPC 调用
- 增量更新
- 数据增量更新接口采用提供 MQ 的方式接入。当业务方出现数据更新的时候,只需将数据推送到对应的 MQ 通道中便可。更新服务会监听每一个业务方通道,及时将数据更新到 Elasticsearch 中
- 全量索引
- 更新服务会调用业务方提供的全量 Http 接口(该接口需提供分页查询等功能)
全量更新
众所周知,全量更新的功能在搜索服务中是必不可少的一环。它主要能解决如下三个问题缓存
- 业务方自己系统的故障,出现大量数据的丢失
- 业务高速发展产生增减字段或者修改分词算法等相关的需求
- 业务冷启动会有一次性导入大批量数据的需求
基于上面提到的问题,咱们与业务方合做实现了全量索引。可是在这个过程当中,咱们也发现一个通用的问题。在进行全量更新的时候,其实增量更新也在同时进行,若是这两种更新同时在进行的话,就会有遇到少许增量更新的数据丢失。好比说下面这个场景架构
- 业务方发现本身搜索业务 alias_A 数据大量数据丢失,因此进行索引重建。其中 alias_A 是别名,就是咱们一般说 alias ,可是底层真正的索引是index_201701011200 (建议:索引里面包含时间属性,这样就能知道是什么建立的)
- 首先建立一个新的索引 index_201706011200,而后从数据中拉出数据并插入ES 中,并记录时间戳T1,最后索引完成的时间戳为 T2 ,并切换搜索别名index_1 指向 index_201706011200。
- 索引建立成功以后的最新数据为T1这个时刻的,可是 T1 到 T2 这段时间的数据,并无获取出来。同时 index_201701011200 老索引还在继续消费 MQ 中的数据,包括 T1 到 T2 时间内的缺乏数据。
- 因此每次索引重建的时候,都会缺乏 T1 到 T2 时间内的数据。
最后,针对上面这个场景,咱们提出经过 zookeeper 分布式锁来暂停 index consumer 的消费,具体步骤以下异步
- 建立 new_index
- 获取该 index 对应的别名,来修改分布式锁的状态为 stop
- index consumer 监控 stop 状态,暂停索引数据的更新
- new_index 索引数据建立完毕,更新分布式锁状态为start
- index consumer 监控 start 状态,继续索引数据的更新

这样的话,咱们就不用担忧在建立索引的这段时间内,数据会有缺乏的问题。相信你们对于这种方式解决全量与增量更新数据有所体会。elasticsearch
集群无缝扩容
数据量爆炸式的增长,致使咱们 ES 集群最终仍是遇到了容量不足的问题。在此背景下,同时结合 ES 自己提供的无缝扩容功能,咱们最终决定对线上ES集群进行了在线的无缝扩容,将从原来的 3 台机器扩容为 5 台,具体步骤以下分布式
- 扩容前准备
- 目前咱们线上已经有 3 台机器正在运行着,其中 node1 为 master 节点,node2 和 node3 为data节点,节点通讯采用单播的形式而非广播的方式。
- 准备 2 台( node4 与 node5 )机器,其中机器自己配置与 ES 配置参数需保持一致
- 扩容中增长节点
- 启动 node4 与 node5 (注意一个一个启动),启动完成以后,查看node1,2,3,4,5 节点状态,正常状况下 node1,2,3 节点都已发现 node4 与 node5,而且各节点之间状态应该是一致的
- 重启 master node
- 修改 node1,2,3节点配置与 node4,5保持一致,而后顺序重启 node2与 node3 ,必定要优先重启 data node,最后咱们在重启 node1( master node).到此为止,咱们的线上 ES 集群就在线无缝的扩容完毕

部署优化
- 查询与更新服务分离
- 查询服务与更新服务在部署上进行物理隔离,这样能够隔离更新服务的不稳定对查询服务的影响
- 预留一半内存
- ES 底层存储引擎是基于 Lucene ,Lucene 的倒排索引是先在内存中生成,而后按期以段的形式异步刷新到磁盘上,同时操做系统也会把这些段文件缓存起来,以便更快的访问。因此Lucene的性能取决于和OS的交互,若是你把全部的内存都分配给 Elasticsearch,不留一点给 Lucene,那你的全文检索性能会不好的。全部官方建议,预留一半以上内存给 Lucene 使用
- 内存不要超过 32G
- 尽可能避免使用 wildcard
- 其实使用 wildcard 查询,有点相似于在数据库中使用左右通配符查询。(如:*foo*z这样的形式)
- 设置合理的刷新时间
- ES 中默认 index.refresh_interval 参数为1s。对于大多数搜索场景来讲,数据生效时间不须要这么及时,因此你们能够根据本身业务的容忍程度来调整
总结
本章主要介绍公司搜索服务的总体架构,重点对全量更新中数据一致性的问题, ES 在线扩容作了必定的阐述,同时列举了一些公司在部署 ES 上作的一些优化。本文主要目的,但愿你们经过阅读沪江搜索实践,可以给广大读者带来一些关于搭建一套通用搜索的建议ide