一、什么是Ealsticsearch(ES)
??Elasticsearch是一个基于Lucene的高度可伸缩的分布式的开源全文搜索和分析引擎,可以快速、实时地存储、搜索和分析大量数据,它通常用作底层引擎/技术,为具有复杂搜索特性和需求的应用程序提供支持,ElasticSearch是用java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。
为什么ES搜索这么快?我们先要了解一下它的存储结构(倒排索引),我们来理解一下什么它到底是什么?
??首先我们先梳理下什么索引,比如一本书,书的目录页,有章节,章节名称,我们想看哪个章节,我们通过目录页,查到对应章节和页码,就能定位到具体的章节内容,通过目录页的章节名称查到章节的页码,进而看到章节内容,这个过程就是一个索引的过程,那么什么是倒排索引呢?
? 比如查询《java编程思想》这本书的文章,翻开书本可以看到目录页,记录这个章节名字和章节地址页码,通过查询章节名字“继承”可以定位到“继承”这篇章节的具体地址,查看到文章的内容,我们可以看到文章内容中包含很多“对象”这个词。那么如果我们要在这本书中查询所有包含有“对象”这个词的文章,那该怎么办呢?
??按照现在的索引方式无疑大海捞针,假设我们有一个“对象”--→文章的映射关系,不就可以了吗?类似这样的反向建立映射关系的就叫倒排索引。
词典和倒排文件是Lucene的两种基本数据结构,但是存储方式不同,词典在内存中存储,倒排文件在磁盘上,从而可以使ES的检索效率非常高。
四、搜索中台设计
三个服务search-admin、search-sync、search-app来进行Elasticsearch的后台管理,对接外部系统做数据上报以及暴露接口供外部服务查询。
封装RestHighLevelClient,提供类Mybatis-plus的参数拼接语法,构建com.noahgroup.framework.search.app.api.vo.req.SearchParam查询参数,通过FeignClient或restTemplate方式调用
查询接入commonSearchClient调用
大部分参考的mybatis-plus的使用方法,基本可以按照写mybatis-plus的方式去构建一个查询,但由于SQL与DSL结构上的差别,条件拼接与mybaties等封装方法有所不同,and、or、not等逻辑方法是与其之前的逻辑方法做对应and、or、not逻辑,即调整and、or、not顺序会导致逻辑变更
数据上报流程采用MQ异步通知的方式上报数据,在数据量不大的情况下,基本可以实现准实时的数据同步,MQ同步数据上报都ES后,就可以通过搜索查询数据了,在异常情况下,做了数据的补偿操作,每日定时去补偿数据,来保证数据的准确性。
数据加工过程主要是从数据组装策略、补偿接口策略以及同步策略三部分组成
数据组装策略
主键更新:更加主键对数据进行更新,一般在一对一、多对一的情况下使用;
接口反查组装:根据业务的复杂性来使用,很多场景数据都是有依赖性的,比如:产品策略信息进行同步时,策略信息冗余了产品信息、项目信息、合同信息,在刷新策略信息的时候,我们需要通过策略主键来查询它的产品、项目、合同信息进行组合后同步数据。
外健更新:根据一个条件字段,可以使用外建查询更新,一般是没有主键的情况,可以在一条数据更新多个索引的情况下进行使用;
补偿接口调用策略
标记位、分页补偿接口,数据进行补偿时调用的接口,根据updatedDt时间来更新当天更新的数据,做到每日的数据补偿,保证数据的准确性。
数据加工流程图如下:
ES的数据索引mapping创建完以后,没有mysql那么灵活,mysql可以通过DML、DDL对数据和结构进行修改,一般ES索引的mapping创建好了以后,很多操作都是没有办法实现的,比如字段创建好了以后就无法去修改它的名称和属性,所以我们在创建索引的时候,一定要确认好index的mappings结构类型,如果后续要改就比较麻烦,如果只是简单的加字段还较简单,但是改动较大,那就只能重新建立索引做数据迁移了。
搜索中台设计了2张表t_index_base、t_index_field_mapping来管理索引index和mappings的关系,可以根据这2表来通过程序来完成索引的创建,如果需要创建索引,只需要在表中维护对应关系即可
客户数据:采用的方式多多一的上报策略,聚合了客户数据、资产数据、服务数据、mgm数据,在CRM多维度客户查询问题,为数据拆分打下基础,满足了CRM多样化的需求
产品查询中心,支持分词搜索,接口性能95线从5秒+降低到300ms以内,客户体验提升明显
公募交易数据上报,1500万数据导入,es性能平稳
对ES进行性能优化,如果对Es的存储结构和写入流程不清楚,那就很难对其做优化了,接下来介绍一下ES的存储结构和数据的写入流程,因为ES存储天然分片的,支持动态扩容,非常适合做数据分析和存储检索的
Elasticsearch集群模式数据存储,如存储2个分片,一个副本,存储结构如下图,分片是为了提高可处理数据的容量和易于进行水平扩展,副本是为了提高集群的稳定性和提高并发量
ES的流程拆解:
1)将数据写到内存缓存区。
2)然后将数据写到translog缓存区。
3)每隔1s数据从buffer中refresh到FileSystemCache中,生成segment,一旦生成segment,就能通过索引查询到了。
4)refresh完,memory buffer就清空了。
5)每隔5s中,translog 从buffer flush到磁盘中。
6)定期/定量从FileSystemCache中,结合translog内容flush index到磁盘中
流程图如下:
相关概念
refresh:es接收数据请求时先存入内存中,默认每隔一秒会从内存buffer中将数据写入filesystem cache,这个过程叫做refresh;
fsync:translog会每隔5秒或者在一个变更请求完成之后执行一次fsync操作,将translog从缓存刷入磁盘,这个操作比较耗时,如果对数据一致性要求不是跟高时建议将索引改为异步,如果节点宕机时会有5秒数据丢失;
flush:es默认每隔30分钟会将filesystem cache中的数据刷入磁盘同时清空translog日志文件,这个过程叫做flush。
segment file:存储倒排索引的文件,每个segment本质上就是一个倒排索引,每秒都会生成一个segment文件,当文件过多时es会自动进行segment merge(合并文件),合并时会同时将已经标注删除的文档物理删除;
commit point: 记录当前所有可用的segment,每个commit point都会维护一个.del文件(es删除数据本质是不属于物理删除),当es做删改操作时首先会在.del文件中声明某个document已经被删除,文件内记录了在某个segment内某个文档已经被删除,当查询请求过来时在segment中被删除的文件是能够查出来的,但是当返回结果时会根据commit point维护的那个.del文件把已经删除的文档过滤掉;
translog日志文件: 为了防止elasticsearch宕机造成数据丢失保证可靠存储,es会将每次写入数据同时写到translog日志中(图中会有详解)。
常规建议
1、不要返回数据量非常大的结果集
2、避免出现大文档,即单条索引记录的体积不要过大。默认情况下,?http.max_content_length设置为100mb,限制单个索引记录的大小不能超过100mb。
优化索引保存速度
1、尽量使用批请求bulk
2、采用从多个线程或进程发送数据到ES
3、增加索引刷新时间大小index.refresh_interval,默认1s刷新一次,设置为-1表示关闭索引刷新。
4、初始加载数据时禁用副本,将index.number_of_replicas 设置为0。
一般我们在进行大量数据的同步任务和加载的时候,可以先设置index.refresh_interval=-1,index.number_of_replicas=0,关闭自动刷新并将索引的副本数设置为0。待完成数据同步后,再调整回正常值。
5、分配足够的内存给文件系统缓存
6、使用自动生成id
7、使用更快的硬件,比如使用SSD固态硬盘
8、索引缓冲区大小index_buffer_size,索引缓冲区index.memory.index_buffer_size默认值是 10%,例如,如果 JVM 10GB 的内存,它将为索引缓冲区提供 1GB。
9、使用跨集群复制,避免争抢资源。
1、优化索引文档结构,避免使用连接查询
2、搜索尽可能少的字段
可以通过copy_to将多个字段的值合并到一个字段,这样减少搜索过程匹配的字段。
3、预处理索引数据,减少查询过程中的计算消耗
4、考虑将索引的mapping中的标识符字段(如id字段)设置为keyword类型
numeric 类型适合范围查询range queries
keyword 类型适合等值查询term queries.
当然,你也可以使用多字段multi-field适配多种场景下的查询。
避免使用脚本
5、尽量避免使用脚本排序,脚本计算得分,脚本聚合查询。
8、合并只读索引
9、热身全局序数,会占用部分JVM 堆空间,可以优化聚合查询性能。
10、预热文件系统缓存
如果重新启动运行 Elasticsearch 的机器,文件系统缓存将是空的,因此操作系统将索引的热点区域加载到内存中需要一些时间,以便快速搜索操作。 您可以使用 index.store.preload 设置根据文件扩展名明确告诉操作系统哪些文件应该立即加载到内存中。
11、设置索引存储时的排序方式加快连接查询性能。
12、使用首选项帮助优化缓存的使用。
13、主要是集群中各个节点上的缓存配置可能存在差异,通过首选项的配置可以统一配置、优化缓存的使用。
设置正确的副本数来提高吞吐量。
那么正确的副本数量是多少?如果您的集群有 num_nodes 个节点、num_primaries 主分片,并且您希望最多同时处理 max_failures 个节点故障,那么适合您的副本数为 max(max_failures, ceil(num_nodes / num_primaries) - 1)。
14、使用更高性能的查询API。比如多用filter少用query。
15、使用constant_keyword字段类型提升filter速度。