ElasticSearch7
倒排索引
数据写入时,建立关键字和主键的映射,查询时–>关键字–>主键–>对应文章
分片
因为ES是以检索为主的产物,为了便于查询,会将数据进行分片,例如:根据性别分片,当查询女的时候就只需要去女性这个分片中查询,速度更快。
在创建index的时候可以指定分片数进行设置,一旦设定,无法修改
副本
为防止数据节点宕机或者挂掉,影响服务的使用,可以在创建inde的时候指定副本数目,如果设置为1时,则相当于在集群中,会有两份相同数据存在,当原始数据丢失时,可以通过副本进行数据恢复或者响应
可以通过setting方法修改副本数量
多分片数据路由
当有写数据进来时,会根据数据主键hash(id)来计算,当前数据存储在哪个节点
当用户读取数据时,因为在每个分片中都会存在一份数据,所以集群会轮询到指定节点,但是并不是一定在当前节点做数据返回,如果当前节点的负载比较高时,会协调一个负载较低的节点处理当前请求。
多分片多副本数据写入流程
- 客户端写入数据,随意访问集群中的节点(协调节点)
- 协调节点创建该数据的id信息,进行hash(id),查询到对应主数据节点
- 主数据节点进行数据保存,然后将数据发送至副本节点
- 副本节点保存数据,反馈结果给主数据节点
- 主数据节点反馈到客户端
- 客户端收到写入数据反馈,写入成功
在写入时,可以通过设置consistency属性设置,是否需要副本节点反馈,其参数选项有:
- one,只需要主节点写入成功即视为写入成功
- all,需要所有副本节点写入成功才视为写入成功
- quorum,大多数节点写入成功即视为写入成功,计算公式:
多分片多副本查询数据流程
- 客户端请求数据,随意访问集群中的节点(协调节点)
- 协调节点进行计算所在分片,并获取到所有的副本节点
- 为了负载均衡,回轮询所有节点
- 将请求转发到指定节点
- 该节点进行数据返回
关键字
- 词条:索引中最小存储和查询单元
- 词典:字典,词条的集合,使用B+、hashMap存储
- 倒排表:关键词出现在什么位置,和出现的频率,每条数据叫做倒排项
全文检索的大致流程
倒排查询的搜索过程:
- 词典中查询关键字是否存在,不存在直接返回
- 查询倒排表,关键词在倒排表中的指针,找到文档id的列表
- 找到对应的数据
- 进行返回
倒排索引早期的问题及好处
- 早期阶段,ES会为整个文档建立一个很大的倒排索引,并将其写入到磁盘,该倒排索引不会被修改,然后将其加载到内存中,因为不可边,所以不存在线程安全问题;一个大的倒排索引是允许被压缩的,这样在读到内存中,可以大量减少磁盘IO
- 问题:倒排索引是不允许被修改的,当更新频率较高时,就需要创建新的索引文件,重新建立倒排表,效率会降低,性能也会急剧下降
动态更新索引
如何在不变性的前提下,实现倒排索引的更新?
- 引入段的概念,通过新增段,来减少修改情况下建立整个索引表的问题
删除
在我们删除的时候,由于索引段不会被修改,所以我们在查询的时候还是会查询到被删除的文档。删除的过程是将要删除的文档使用.del标识,查询到结果后发现该文档被.del所标识,则将该文档从查询结果中剔除(逻辑删除)
合并倒排索引
当索引段数量过多时,会将索引段进行合并,在合并的过程中,会将删除的文档从磁盘中彻底删除(物理删除),可以通过refresh_interval来设置段合并的频率,单位是秒
真正的写入流程
- 客户端执行写入请求
- 协调节点找到主分片节点,和其他副本节点
- 将文件写入主分片节点,并在主分片的内存中建立索引生成index数据
- 将index数据封装成一个segment数据对象,并将操作写入translog中
- 根据设置的refresh_interval(默认1s)属性所指定的频率,将segment数据对象、translog刷新至OSCache中
- 根据flush(默认30m)属性所设定的值,将OSCache中的segment数据对象和translog文件flush到磁盘中
- 当磁盘中的segment文件越来越多时,会将segment文件进行合并
文本分析器
- 分词器
- 字符过滤器,在建立索引文件时,将文本进行转换
- token过滤器,按照特定的要求、特定的格式进行转化,比如转换大小写、删除指定字符
ES性能优化
由于elasticSearch是重度使用磁盘的
- 硬件方面
- 使用SSD(固态硬盘)
- 使用RID0,其本质就是,将数据存储到多快磁盘中,当有请求进来时,就可以多快磁盘并行处理
- 使用多快硬盘,可以通过配置path.data进行配置
- 尽可能的避免使用远程挂载
- 合理的设置分片策略
- 每一个分片可以视为一个搜索引擎,意味着内存、文件句柄消耗严重
- 如果每一个分片都分配到不同节点还好,如果一个节点中包含多个分片,则面临资源竞争
- 计算词条相关度是根据每个分片来的,如果分片多的话,会导致相关度计算存在误差
- 总的分片数一般不要超过节点数的3倍,例如3个分片+2个副本为3+(3*2)=9
- 节点数<=主分片数*(副本数-1)
- 推迟分片分配
- 默认情况下当节点宕机后,集群会重新分配分片,就需要大量的资源消耗,可以设置节点重新上线的时间,比如节点宕机后五分钟未上线才会重新分配分片
- 使用路由参数
- 不带路由(routing)参数,协调节点无法计算数据所属分片,只能将请求分发至所有节点,返回经过后进行聚合
- 带路由参数(routing),协调节点计算出所属分片,将查询结果返回
- 写入优化
- 加大translog flush,目的是为了降低Iops、write block
- 增加index Refresh,目的是减少segment merge 频率
- 调整bulk线程池和队列
- 优化节点间的任务分配
- 尽量批量数据提交
- 数据尽量不要超过100m
- 段合并
- 在后台定期进行段合并
- 减少副本的数量
- 可以在写入阶段,将副本数量设置为0,在数据写入完成后,再修改副本数量写入副本
- 调整ES的JVM
elasticsearch集群的master是如何选举的?
- ES中选举主要模块是ZenDiscovery模块负责的,主要包括Ping和Unicast
- ping 节点直接通过这个RPC来进行彼此通信
- unicast单播模块维护一个主机列表,来判断哪些主机需要ping通
- 对所有可成为master(配置文件中node.master=true)的节点进行排序(通过nodeId),每次选举的时候都把当前节点所知道的master进行排序,选出排为第一个的节点,当该节点收到的票数为一半以上,并且该节点的投票是自己,则当前节点为master,若未达到一半以上,则重复以上步骤
- master的指责是集群、节点、索引的管理,不负责文档级别的管理,数据是有data节点进行管理的
有哪些情况可能出现脑裂问题
- 由于网络问题,集群之间的网络延迟,从节点无法访问到master,导致从节点重新选举出新的master
- 节点负载,当节点既是数据节点又是主节点时,访问量较大时,可能出现响应延迟,从节点无法及时访问到master,并认为master节点挂点了,选举出新的master
- 内存回收,当主节点也是数据几点时,进程占用较大,发生GC时,会出现响应延迟,从节点无法及时访问,从新选举出新的主几点
如何避免脑裂问题
- 减少误判,discovery.zen.ping_timeout的默认超时时间是3s,可以将该时间设置的大一点,减少误判的概率
- 增加触发选举节点的数量,官方推荐(n/2)+1,当有一半以上的节点都认为主节点挂了,才会进行选举
- 角色分离,将主节点和数据节点分离,避免出现负载或者GC所导致的响应延迟
ElasticSearch 是如何保证读写一致的
- 可以使用版本号,使用乐观锁,确保新版本的数据不会被旧版本的数据覆盖掉
- 在写操作的过程中,一致性级别包括quorume/one/all,可以使用高级别的写入策略来保证数据的一致性
- quorume 为默认配置,表示当大多数的节点写入成功即认为成功
- one 当有一个节点写入成功则认为写入成功
- all 表示需要所有的节点都需要写入成功
- 对于读操作时,可以使用replication为sync,表示主副分片都返回,来保证数据为最新的
ElasticSearch8
- 新版本采用JDK17 ,并默认包含,也提供了兼容jdk8的版本
- ES8中废除了type的概念
- ES8中默认开启安全认证的
- 提供了同步和异步API
- 提供了EQL(事件查询语句 )
- 在ES6.3以后就支持了SQL操作,Kibana中我们使用的是DSL
- ES提供了实用lambda的相关API
cursor(游标)
当我们使用sql进行查询时,假如总共有3天数据,但是我们的查询语句只需要返回2条数据,则剩下的这条数据就会进入cursor中,在后续查询中,可以直接使用cursor进行查询,只可以取用一次,多次使用时,结果是返回空,游标是消耗性能的,可以使用close进行关闭
![image.png](https://img-blog.csdnimg.cn/img_convert/5a01ca4ebad05c8ff2d3221dc27205d5.png#clientId=u24d706fe-2394-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=470&id=u7de01b5c&margin=[object Object]&name=image.png&originHeight=940&originWidth=2862&originalType=binary&ratio=1&rotation=0&showTitle=false&size=1307933&status=done&style=none&taskId=u62092005-a052-4450-afb1-93b0b874c3a&title=&width=1431)
权重
- ES中查询到的结果是按照score进行排序的,可进行设置权重查询自己想要的那条数据
- 可以使用explan查询权重计算详情
NLP自然语言处理
- 下载openNLP
- 下载NLP预处理模型包,输入对应的语句,可进行预处理的分析
性能优化
- 页缓存
- 操作系统级别的缓存
- ES是实用java语言编写的,java无法直接操作OS的文件,所以OS将文件缓存到内存中
- 一般情况下我们在设置ES程序时,ES的内存要小于操作系统的一半,这样就有更大的内存进行缓存文件
- 分片集请求缓存(节点请求缓存)
- 在查询的时候指定request_cache=true ,会将我们的查询结果缓存至该节点的 内存中
- 默认是不开启缓存
- 查询缓存
- 将某一些功能进行缓存,缓存的管理更为精细,相比较于分片集请求缓存,该缓存可以在不同的查询中重复使用
- 减少内存堆
- 如果将ES的内存堆设置的特别大时,我们的内存中会存储大量的索引数据
- 由于ES会将一些查询缓存存入系统内存中,则当ES的内存越大,系统可用内存就会变小
- 官方推荐堆内存不要超过系统内存的一半,并且不要超过30G
- 数据分层
- 热数据,将热点数据存储至性能较好的节点中
- 温数据,有些数据访问频率没有那么高时,将其放至容量较大的节点,速度一般的节点
- 冷数据快照,部分历史数据如果访问频率特别低时,可以将数据迁移至外部存储系统,并做快照处理
- 在7.1.2后续版本,ES可以直接查询对快照数据进行查询,不用拉取过来再查询了