保护私人版权,尊重他人版权。转载请注明出处并附带页面链接
本文主要介绍在实际项目中应用ElasticSearch全文搜索的方案设计及一些心路历程。
以下表述中,ElasticSearch统一简称为ES
ES使用场景
ES可以作为传统数据库的一个补充,处理全文检索,相关度排名,海量数据的近实时处理。在电商网站,日志数据分析等方面有成熟的应用体现。
实际业务系统背景
实际项目中,有管理端订单搜索的功能。搜索分两类,一类是以课程名称为代表的模糊搜索,一类是以订单所属团队的精确搜索。模糊搜索采用的是传统的like,精确搜索采用的是传统的whereIn。
mysql搭配ES实现搜索的方案
订单数据记录到mysql的同时,实时同步到ES。所谓“术业有专攻”,查询订单号的工作交给ES,剩余数据查询任务仍保留给mysql。
数据同步
有代表性的同步方案有以下几种:
方案 | 优点 | 缺点 |
---|---|---|
在业务系统里做双写 | 显式定制同步的场景 | 增加业务上的维护成本 |
用阿里的canal之类的框架来做数据库binlog到ES的同步 | 代码入侵性低 | 需要额外开发比较多,依赖也比较多 |
实践中采用的是方案1,出发点主要是:先尝试用普通的方案,试错成本低,积累足够经验且更深入了解ES后再尝试高级的方案。
结合当前项目用的lumen框架及eloquent的ORM的实际情况,实现中利用eloquent内置的模型事件,在saved事件触发后记录要同步的订单号。
结合具体的eloquent特性,在数据库事务中会触发模型的saved事件,为了保证同步到ES中的数据是最新的,当前采用的策略是:logic层统一只记录可能要同步的订单号,controller层才触发同步到ES的操作。
放controller层触发是为了挑选一个安逸的后面没有数据操作的环节,适用于当前的业务系统里,待探索更优雅的处理方案。
分页方案
ES提供了两种代表性的分页方式,分别是
分页方式 | 原理 | 优点 | 缺点 |
---|---|---|---|
深度分页(from-size) | 查询前X条数据,截断前Y条,只返回Y到X的数据 | 实时性 | 浪费了(X-Y)条数据。分页的偏移值越大,执行分页查询时间就会越长 |
快照分页(scroll) | 维护了一份当前索引段的快照信息,这个快照信息是执行这个scroll查询时的快照。在这个查询后的任何新索引进来的数据,都不会在这个快照中查询到。记录一个读取的位置,保证下一次快速继续读取 | 适合数据量大的查询 | 非实时性 |
实践中采用的是from-size分页方案,主要是考虑到实际系统中产品要求的实时性。至于该方案中分页偏移量大时的缺陷,ES本身也设计好了一个max_result_window属性。max_result_windows指的是最大窗口大小,限制size-from的分页方式查询的最大条数。
技术限制的问题,换了个思路试图从产品业务上解决,参考了baidu和google的方式,在分页跳到比较后的页数时,提示“为显示相关度最高的信息,我们向你展示最近X条数据”拒绝用户非常规的查看。这点改动也得到了深明大义的产品经理认可,毕竟翻到比较后的页数查看是超低频价值低的操作。具体的X数值是和ES设置的max_result_windows有关,默认值是10000。
性能测试结果
仅对比查出订单号及总条数的查询,其余的从表数据查询由于都是相同的sql故不求和起来比较总时间。
- ES混合mysql的方案
搜索“夏令营” | 不搜索关键词 |
---|---|
43 | 25 |
39 | 36 |
37 | 25 |
44 | 24 |
38 | 25 |
41 | 29 |
37 | 35 |
39 | 30 |
42 | 27 |
38 | 25 |
- 纯mysql的方案
搜索“夏令营” | 不搜索关键词 |
---|---|
317 | 225 |
325 | 219 |
308 | 210 |
311 | 209 |
309 | 217 |
323 | 221 |
318 | 214 |
312 | 225 |
330 | 218 |
314 | 212 |
纯sql的耗时主要是在两个方面:
- like ‘%X%’的方式没有利用到索引,扫描范围是全表
- 由于业务需要,whereIn的范围较多
ES的引入改善的方面:
- 对于精确搜索的结构化数据也能利用到ES内置的索引匹配到。
- 对于模糊匹配的非结构化数据也能利用分词的优越性快速索引到。