从服务器检索时出错dfdferh01_城运大数据中心|智能检索的道与术

1.需求篇:智能搜索的设计背景

城市管理数据涉及城市生产和生活的方方面面,这些数据来源各不相同,数据与数据之间关联性错综复杂。如何在海量的多维数据中进行高效的检索和分析,摆脱传统检索场景中条件设置繁琐、操作流程复杂的困境,是大数据检索需要直面的难题。在城运大数据中心数据检索中,我们加入了智能检索策略,简化检索流程,可以尽快检索到最大可能满足用户需求的有用信息,让检索更加「智能」。

4547492f13aad2e8082b781523f9c056.png

2.设计篇: 以需求为导向的原型设计

作为海量数据的检索窗口,我们希望尽可能的实现数据搜索的便捷与高效,具体表现在如下几个方面:
  • 摆脱传统政务系统给人的千篇一律、单调乏味的画风;

  • 简化非必须的操作与交互流程,减少用户的学习与使用成本;

  • 为用户提供良好的资源导航,实现一次检索即可获取到城运大数据中心所有资源,提升资源获取效率;

  • 对结果进行概括分类和可视化展示,使结果一目了然。

3.技术篇:全文检索技术调研与选型

城运大数据中心采用ElasticSearch(后文简称ES)进行数据管理和存储。ES是一种基于Lucene的分布式检索和数据分析引擎,内置丰富的检索过滤器和分析器,支持多种基于倒排索引的全文检索方案,社区比较活跃,迭代迅速。我们针对社区提供的主流方案进行了梳理、分析和选型,并最终制定了一个最适合智能检索业务场景的方案。

MatchQuery:最常用的Full Text Query
  • MatchQuery会针对用户输入的关键词,默认基于顶层的分词器(我们用的IK)进行分词,也可以另行设置分词器,然后用分词结果去跟指定的字段内容匹配。MatchQuery会根据参数「operator」的设定进行严格匹配或部分匹配。MatchQuery的调参空间并不大,在默认的部分匹配模式下,返回的结果容易充斥很多不符合用户预期的结果,导致精确度降低。虽然有参数「minimum_should_match」控制匹配度下限,但使用起来仍有所不便,并不容易灵活控制。

d7048349ef5516b35bac6bc63c264121.png

copy_to+MatchQuery/QueryStringQuery

  • 由于智能搜索需要针对一个或多个字段展开检索,而Match的方式每次只能指定一个字段,因此该方案的思路是将每行结构化的数据合并成一个字段,并建立倒排索引,在此基础上采用Match进行检索。但在智能搜索实际的业务场景中,不同的用户可搜索的字段受权限约束各不相同,而COPY_TO的设置是在建立索引时就指定好,两者存在矛盾,因此并不适合应用在智能搜索的场景。
eb560c0ef94d445e6d5c0530c9b2fa72.png

Match_Pharse Query:短语匹配

  • 这是个很苛刻的检索器,不仅会对查询字符串解析成一个词项列表,同时分词后的词组均需要在结果中命中,影响召回率的两个因素分别是分词在原文中出现的顺序,以及用于配置分词之间间距长度的「slop」变量。总体来看不够灵活,它更适合做单个短语的精确匹配,在有些场景Match Pharse Query可以和Match Query联合使用,能起到兼顾召回率和准确率的效果。
8bceddbd533a14d3b768d508ea7972bf.png
ac506470526d7f24f82e370a5b77f357.png

Multi-Match Query:基于match的多字段检索

  • 相较于Match, Multi-Match支持多个字段的全文检索,并提供best_fields、most_fields、cross_fields三种匹配类型,同时可以按需提升某个字段的权重,在具体使用场景中需要做好容错,因为Multi-MatchQuery传入多字段时,字段类型需要约定一致,否则在检索中容易出现类型转换的问题。

Query_String Query:字符串查询

  • 默认对所有字段进行查询,也可以在每次检索时通过「fields」字段指定一个或多个字段。字段的类型可以不要求完全一致,不存在使用Multi_Match Query检索可能出现的类型转换问题,究其原因是因为ES能够针对识别到的分词格式,检索时根据不同的字段自动分配一个最适合它的查询器/过滤器。同时query也支持各种形式丰富的语法,例如支持正则表达式、范围查询和模糊匹配等。也正是因为Query_String Query的综合表现不错,而且兼具了以上几个方案的特性,我们最终选择它作为智能检索的主要检索方法。

a55ea939a0f87cf3d936d9f4df38b439.png

4.内功篇:性能优化之路

一个平台,除了颜值之外,影响用户体验的另一个重要因素就是性能,包括速度和准确性。做好智能检索,不仅要快,还要准。围绕着这个目标,我们从数据的写入和实时分析两个层面对ES进行了比较详细的测试和性能调优工作,并将调优方案应用于实际项目中,实现了亿级数据的实时写入和秒级检索。

服务器硬件配置

固定master节点,master和slave节点都存储数据。

3c0645fdf28857cadb4aa319ae186cc1.png

数据量
  • 数据字段设计了80个字段,实际写入74个,其中时间字段3个,int类型42个,全文检索类型3个,其余为keyword类型。每行数据约1.33kb;

  • 写入总量为10w的数据(0副本),每批次批量写入1000、10000、20000条数据,观察不同批量写入的耗时,和服务器负载。

 调优前后的数据写入性能表现  优化前:

4f6c5bb0caa7aec716dd28acfe7b1bb2.png

优化后: bfeae749a261d5dbcfdf537a0e763ac5.png

调优前后的数据检索性能表现

89da57909ee87a9f63ffac6771fa66f7.png

59814eec8a9898f659b8f9e66459509b.png

持续写入与多维分析环节的典型表现
  • 磁盘IO压力较大;

3b893b668350d92f123910c41be7b625.png
2363afce6cf2a61885e0b8699c39e704.png
  • 查看jvm统计,看到每隔5秒左右有一次full gc发生;

  • 在持续不断提交请求时,发现cpu占用持续走高。

01cb6f7192ccf6ab085d673cf2e31df4.png
f096dda459b5de6436be7d730ca608a1.png 我们的优化方案
#机械硬盘的并发IO性能较差,我们需要减少每个索引并发访问磁盘的线程数。index.merge.scheduler.max_thread_count: 1#增大这个参数可以允许translog在flush前存放更大的段(segment);更大的段的创建会减少flush的频率,并且更大的段合并越少,会减少磁盘IO,索引性能更高。index.translog.flush_threshold_size:1024#索引刷新的频率是指文档需要多长时间才能出现在搜索结果中,默认1s,时间越短检索的实时性越强,但写盘的频率会增大,故而增加了io的压力,同时容易产生大量小的索引段。index.refresh_interval:5s#因为每台服务器有两块磁盘。这里配置ES数据落盘的路径为两块盘挂载的路径,把两块盘都利用起来,读写并行度提升,所以可以提高读写效率,数据写入的速度会更快。path.data: /egova_data1,/egova_data2
f69632397377b048d6591cec52e58c28.png
#indexing buffer在为 doc 建立索引时使用,当缓冲满时会刷入磁盘,生成一个新的 segment, 这是除refresh_interval外另外一个刷新索引,生成新 segment 的机会. 每个 shard 有自己的 indexing buffer。默认是10%。indices.memory.index_buffer_size: 20%#已经索引好的文档会先存放在内存缓存中,等待被写到到段(segment)中。缓存满的时候会触发段刷盘(IO密集型工作)。默认最小缓存大小为48mb。indices.memory.min_index_buffer_size: 96mb#linux在内存不足的时候会写入swap分区,给性能带来影响,必须关闭。经过实验发现,在持续批量提交数据时,如果不关闭swap,cpu在io的等待时间会变得很长,导致cpu负载很高。swapoff -a#增大内存配置,留一半内存给操作系统,剩下一半给ES。-Xms16g -Xmx16g#CMS垃圾回收器改为G1垃圾回收器,更改后看到几乎没有再发生full gc。-XX:+UseG1GC-XX:MaxGCPauseMillis=200

使用默认垃圾回收器:

cacfcd539a8f3b5a2f03f5b361991ff1.png
更换为G1垃圾 回收,可以看到几乎没有再发生FULL GC:
bc0b38aedf195be4ee53747d5fe2d6c7.png
#log输出水平默认为trace,即查询超过500ms即为慢查询,就要打印日志,会给cpu和io带来一定的影响。这里把log的输出水平改为info,减轻服务器压力。logger.index_search_slowlog_rolling.level=infologger.index_indexing_slowlog.level=info#去掉内存和文件句柄的限制,锁定内存,不让jvm写入swap。bootstrap.memory_lock: truebootstrap.system_call_filter: false#压缩tcp传输时的数据,默认false。transport.tcp.compress: true#filedata cache的使用场景是一些聚合操作(包括排序),构建filedata cache是一个相对昂贵的操作,所以尽量让他保留在内存中,达到heap的30%自动清理旧cache。默认20%。Indices.fielddata.cache.size: 30%#设置ES内存断路器,当查询请求所需内存超过这个比例就直接失败。indices.breaker.fielddata.limit: 60%
总结
  • 和1000条一批(约1.3MB一次)的总量提交相比,10000条一批(约13MB一次)的总量提交对ES而言吞吐量要更高。但不代表一次提交的越多效率越高,建议每次批量提交的数据大小控制在5-15MB左右;

  • 在磁盘读写速率的测试过程中,通过观察CPU负载、磁盘IO的等待趋势、磁盘读写速率的测试,可以认定写入速度的瓶颈跟CPU、硬盘有很大的关系。因此建议在正式环境中配备更好的CPU,采用SSD(固态硬盘)。如果条件不允许,机械硬盘也要选择1w转以上;

  • 堆内存对于ES绝对重要,它被许多内存数据结构用来提供快速操作。除此之外,lucene也是一个非常重要的内存使用者。如果只是一味扩容堆内存,当配额超过32G时,将会导致压缩OOPS失效。因此堆内存大小建议为min(32, 宿主机物理内存/2)。ES社区中有人指出采用64G内存时性价比最高;

  • 如果服务器挂载多块硬盘,那就把ES的data路径分为多块挂载路径。实验证明,多块挂载路径可以提高写入速度;

  • 分片数并不是越多越好。因为每个分片本质上是一个lucene索引,因此都会消耗响应的文件句柄、内存和CPU。虽不建议分片数太多,但也不能太少,分片太少,会导致随着分片的膨胀写入越来越慢,甚至集群节点出现间歇性丢失的情况,不能充分发挥CPU和硬盘IO的能力。官方建议一个分片最大容量控制在30GB,ES社区中有人建议控制在50GB。我们通过对比测试,将分片容量控制在40GB;

  • 一些经验之谈:1)在高负载下进行ES删除索引操作,ES进程有一定几率宕掉;2)在ES重启时,不要尝试往里面写入数据;3)搭建ES集群时,一定要注意做好时间同步,否则集群容易丢失节点。

5.成果篇:更懂你的智能搜索

fc43e3736ba3d2f5094e8e971361e1c8.png

7cdcdff1ede78972c32abcc6471775c8.png

我们实现了类似百度的一站式智能检索展示平台,用户只需要输入几个关键词,就可以快速检索到所需的城市综合管理数据资源。系统采用了以Query_String Query为主、多种检索方式相结合的智能检索技术,提供给用户准确度高、关联度强的检索结果。系统还根据部署的硬件环境资源进行性能调优,最终达到亿级数据实时写入和秒级检索的性能指标。

End

1513d8f7cfb42cdebea8437e4b2131ef.png

往期回顾

可视化技术|城市运行中心的“颜值担当”

微信小程序"智慧市民通"全面上云实践

记一次用户无感的云服务平滑迁移

一条优美轨迹线的诞生日记

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值