搜索系统实现

业务场景

随着公司业务的发展,数据量的不断增长,以前各业务线对于搜索数据维护的能力显得有些捉襟见肘。同时由于早期没有进行架构方面的规划,业务线各自维护es作为搜索引擎,随之而来的问题如单点、版本不统一、没有很好的规划分片数据等,对于高可用以及实时响应带来了挑战,同时也增加了运维成本。

目前有些数据搜索依旧在数据库进行的,随着数据量增加以及查询维度的提升,单个数据库的读写能力有限,未来存在分片的可能性,对数据一片片查询,对数据库返回结果在内存聚合的方式,缺点显而易见,同时增加了开发的复杂性,对数据库的性能也带来了不必要的负担。

基于上述的原因,遂决定建立通用的搜索服务来为业务线提供统一的入口。同时提供了高可用、以及良好的响应时间。

随之而来的问题

数据更新是一个比较重要的问题,之前解决的方案是每次有数据修改,同时修改对应记录的sync_status状态,然后由定时任务扫描sync_status,将修改记录同步到es。定时任务存在的问题:

  • sync_status = 0查询会进行全表扫描,即使创建索引效率不会有大的提升
  • 定时任务频率问题,同步频率过低,会造成数据可搜索窗口期过大,频率过高,加上全表扫描会对数据库带来过大的压力

减少对业务的影响,要做到零耦合,不侵入现有业务。

当变更数据量比较大的时候,写入es效率限制,可能会导致数据变更到可索引的窗口期增大。

针对同一条数据的修改,可能存在较早修改覆盖最新修改的可能,所以针对一条数据的修改应该是串行的。

开源搜索引擎的选择

因为之前使用的es搜索引擎,所以在选择上更偏向于该方案。同时也做了简单的调研。

查看了主流的Java搜索引擎方案,主要是solr和es。具体的比较网上文章比较多,就不多做介绍,我总结了我选择es的几点:

  • es更轻量级、更易用,安装、配置方便,不需要依赖第三方包
  • 可以根据服务器数量调整分片位置,只需要前期根据数据量增长,指定合理的分片数
  • 跟其他如logstash、kibana等方便组合,后续可以作为日志平台

最终方案

最终经过考虑采用了如下图的解决方案:

为了解决定时轮询扫表带来的数据库压力,我们通过定于mysql的binlog,我们采用阿里的canal框架,订阅响应的表的binlog,同样也做到了对业务的零侵入。

解析binlog之后,引入mq来解决上游流量过大给下游服务带来的压力。同时我们es的批量写入功能,防止过多的单个请求。

对于数据覆盖问题,暂时的想法是,记录某条记录的最后修改时间,这个在项目上线后看下效果,是否对性能有影响。

项目采用CQRS架构,将读、写服务分离,将业务复杂性放到Read Server中,Write Server负责数据修改、删除,同时方便以后更好扩展。Read Server对外提供Rest接口,通过Eureka进行客户端负载均衡。Write Server负责消费MQ中的消息进行解析,将数据保存到ES。

总结

该方案仅为初期设计方案,可能还需要逐步演进。后面出现的问题,会进行更新,该方案也仅供参考。

坚持原创技术分享,更多深度分析、实践代码,您的支持将鼓励我继续创作!