分布式搜索分析引擎ES

基本概念和原理

es是实时的分布式搜索分析引擎:

  • 实时表现在新增到ES中的数据1s中就可以被检索到,这种新增数据对搜索的可见性成为“准实时搜索”。
  • 分布式意味着可以动态调整集群规模,弹性扩容,支持上百个节点,相比 HDFS 等上千台的集群,更适合中等数据量的业务,不适合存储海量数据
  • 分析表现在,ES除了搜索,还提供聚合功能,可以进行数据分析,统计,生成指标数据等

Luence是java编写的全文搜索框架,用于处理纯文本的数据,但它只是个库,只提供建立索引,执行搜索等接口,不包含分布式服务

全文:对全部的文本内容进行分析,建立索引,使之可以被搜索,成为全文

索引结构:

  • 各种文本内容存储在文档中,一般使用JSON作为文档的序列化格式,在创建索引时,需要描述文档中每个字段的数据类型,并且可能需要指定不同的分析器
  • 在存储结构上,由_index,_type和_id唯一标志一个文档
    • _index指向一个或多个物理分片的逻辑命名空间
    • _type类型用于区分同一个集合中的不同细分,在不同的细分中,数据的整体模式是相同或相似的,不适合完全不同类型的数据,多个_type可以在相同的索引中存在,只要他们的字段不冲突,
    • _id文档标记符由系统自动生成或使用者提供

注意:很多人会将_index理解为数据库,将_type理解为表,实际上这是完全不同的概念,不用的type下的字段不能冲突,删除整个_type也不会释放空间,在实际应用中,数据模型不同,有不同_type 需求的时候,我们应该建立单独的索引,而不是在 索引下使用不同的 type。删除过期老化的数据时,最好以索引为单位,而不是_type和_id

在 ES 6.x 版本中, 1个索引只允许存在 type ,未来的7.x 本将完全删除一type 概念

分片:

分布式思想

  • 在分布式系统中,单机无法存储规模巨大的数据,要依靠大规模集群处理和存储这些数据,一般通过增加机器数量来提高系统水平扩展能力,因此,需要将数据分成若干小块分配到各个机器上,然后再通过某种路由策略找到某个数据块所在的位置。
  • 除了将数据分片以提高水平扩展能力,分布式存储还会把数据复制成多个副本,放置到不同的机器中,来增加系统可用性,同时数据副本还可以是读操作并发执行,分担集群压力,但是多数据副本也会带来一致性的问题
  • 分布式的集群方式大致可以分为主从(Master-Slave)模式和无主模式,主从模式可以简化系统设计,部分操作仅由Master执行,并负责维护集群元信息,缺点是Master节点存在单点故障,需要解决灾备问题,并且集群规模会受限于Master节点的管理能力。

ES数据分片和数据副本的关系如下

分片是底层的基本读写单位,分片的目的,是分割巨大索引,让读写可以并行操作,由多台机器共同完成,读写请求最终落到某个分片上,分片可以独立执行读写工作。ES利用分片将数据分发到集群内各处,分片是数据的容器,文档保存在分片内,不会跨分片存储。分片又被分配到集群内的各个节点力,当集群规模扩大或缩小时,es会自动在各节点中迁移分片,使数据仍然均匀分布在集群里。

索引与分片的关系如下

  • 一个ES索引包含很多分片
  • 一个分片是一个Lucene的索引,本身就是一个完成的搜索引擎,可以独立执行建立索引和搜索任务。
  • Lucene索引又由很多分段组成,每个分段都是一个倒排索引,ES每次refresh都会生成一个新的分段
  • 一个分段segment包含若干文档的数据,在每个分段内部,文档的不同字段被单独建立索引
  • 每个字段的值由若干词(Term)组成,Term是原文本内容经过分词器处理和语言处理后的最终结果

主分片数量不可以修改,副本分片数可以随时修改,5.x~6.x版本之后,ES支持在一定条件的限制下,对某个索引的主分片进行拆分(Split)或缩小(Shrink),但是我们仍然要在一开始就尽量规划好主分片的数量:

  1. 先依据硬件情况订好单个分片容量
  2. 再依据业务场景预估数据量和增长量,再除以单个分片容量

分片数不够时,可以考虑新建索引,搜索 个有 50 个分片的索引与搜索 50 个每个都有1个分片的索引完全等价,或者使用_splitAPI 来拆分索引

注意:在实际应用中,我们不应该向当个索引持续写数据,知道分片巨大无比,否则会在数据老化后难以删除,另外,以_id为单位删除文档不会立刻释放空间,删除的doc只在lucene分段合并时才会真正从磁盘中删除

即使手动触发分段合并,可能也会因为分段巨大而导致再合并过程中磁盘空间不足,因此建议周期性创建索引,例如每天创建一个,例如website_20230710,然后创建一个名为website的索引别名来关联这些索引,这样对于业务方来说,读取时使用的名称不变,当需要删除数据的时候,可以直接删除整个索引。

索引别名:就像一个快捷方式或者软链接,不同的是它可以指向一个或者多个索引,可以用于实现索引分组,或者索引见的无缝切换

动态更新索引

为文档建立索引,使其每个字段都可以被搜索,通过关键词检索文档内容,会使用倒排索引的数据结构。倒排索引一旦被写入文件后就具有不变性,不变性具有许多好处:对文件的访问不需要加锁, 读取索引 才可以被文件系统缓存等

索引如何更新?让新添加的文档可以被搜索到

新增的内容写到一个新的倒排索引中,查询时,每个倒排索引(segment)都被轮流查询,查询完再对结果进行合并

标记删除:由于分段的不变性,更新,删除等操作实际上是将数据标记为删除,记录到单独的位置,因此删除部分数据不会释放磁盘空间

近实时搜索:

写数据原理:

  1. 先写入内存buffer,此时buffer里面的数据是搜索不到的,同时记录事务日志translog。如果buffer快满了,或者默认每隔1s,同时buffer里面有数据,就会把内存buffer数据refresh到一个新的segment file中,清空buffer,但是此时数据不是直接进入segment file 磁盘文件,而是先进入操作系统缓存,这个segment file中就存储最近1秒内 buffer中写入的数据。可供查询。以此来实现近实时搜索
  2. 重复1写入数据,translog会不断变大,当translog达到一定长度,就会触发commit操作
  3. commit操作第一步就是将buffer中现有的数据refresh到操作系统缓存中,清空buffer,然后将一个commit point写入磁盘文件,里面标志着这个commit point对应的所有segment file,同时强行将操作系统缓存目前所有的数据都fsync到磁盘文件中去,最后清空现有translog日志文件,重启一个新的translog
  4. 这个commit操作叫做flush。默认30分钟自动执行一次flush,但如果translog过大,也会触发flush。flush操作就对应着commit的全过程,我们可以通过es api,手动执行flush操作,手动将os  cache中数据fsync强刷到磁盘上去
  5. translog其实也是先写入os cache的,默认每隔5秒刷一次到磁盘中去,所以默认情况下,可能有5s的数据会仅仅停留在buffer或者translog文件的os cache中,如果此时机器挂了,会丢失5秒钟的数据。但是这样性能比较好,最多丢5秒的数据。也可以将translog设置成每次写操作必须是直接fsync到磁盘,但是性能会差很多

translog:每次对ES进行操作时,都会记录事务日志,当ES启动时,重放translog中所有在最后一次提交后发生的变更操作

段合并

每次refresh都会创建一个segment,这会导致分段数量太多,从而导致:1消耗文件句柄内存,2 每个搜索请求都需要轮流检查每个段,查询完再对结果进行合并,所以搜索会比较慢

策略:常用方案是选择大小相似的分段进行合并,在合并过程中,标记为删除的数据不会写入新分段,当合并过程结束,旧的分段数据被删除,标记删除的数据才从磁盘删除

问题:当段文件大小没有上限,巨大的段达到磁盘空间的一半时,剩余空间不足以进行新的的段合并过程,如果段文件设置一定上限不再合并,则对部分数据无法实现真正的物理删除操作

集群内部原理

ES集群使用的分布式的主从模式,从集群节点角色的角度划分,至少存在:主节点,数据节点,协调节点, 预处理节点,部落节点

集群节点角色

主节点 (master node)

职责:负责集群层面的相关操作,管理集群变更

配置:配置了node.master:true(默认)的节点,才具有被选举为Master的资格,主节点是唯一的,将从有资格成为master的节点中进行选举

主节点也可以配置为数据节点,通过node.data : false/true控制,生产环境应尽量分离主节点和数据节点

为了防止数据丢失,每个主节点应该知道有资格成为主节点的数量,默认为1,通过discovery.zen.minimum master nodes配置,原则上最小值为:(master eligible nodes I 2) + 1

数据节点(data node)

职责:负责保存数据,执CRUD,搜索,聚合等

一般情况下,数据读写流程只和数据节点交互,不会和主节点打交道(异常情况除外)

预处理节点 (Ingest node)

职责:预处理操作允许在索引文档之前,即写入数据之前,通过事先定义好的一系列processors(处理器)和管道(pipeline),对数据进行某种转换和富化,processors和pipeline拦截bulk和index请求,在应用相关操作后,将文档传回给index或bulk API

配置:默认在所有节点启用ingest,如果想要禁用ingest,可添加node.ingest:false,也可以通过配置

node.master : false

node . data : false

node.ingest : true

创建一个仅用于预处理的节点

协调节点 (Coordinating node)

作用:客户端请求可以发送到集群的任意节点,每个节点都知道任意文档所处的位置,然后转发这些请求,收集数据并返回给客户端,处理客户端请求的节点成为协调节点

协调节点将请求转发给保存数据的数据节点,每个数据节点在本地执行请求,并将结果返回给协调节点,协调节点收集完数据后,合并为单个全局结果,对结果收集和排序的过程可能需要很多CPU和内存资源

部落节点(Tribe node)

职责:部落功能允许部落节点在多个集群之间充当联合客户端

集群状态

集群状态元数据是全局信息,元数据包括:路由信息,配置信息

集群状态由主节点负责维护,如果主节点从数据节点接收更新,则将这些更新广播到集群的其他节点,让每个节点上的集群状态保持最新

集群扩容

当扩容集群,添加节点时,分片会均衡的分配到集群的各个节点,从而对索引和搜索过程进行负载均衡

主要内部模块简介

Cluster

cluster模块是主节点执行集群管理的封装实现,管理集群状态,维护集群层面的配置信息

功能:

  • 管理集群状态,将新生成的集群状态发布到集群所有节点
  • 调用allocation模块执行分片分配,决策哪些分片应该分配到哪个节点
  • 在集群个节点中直接迁移分片,保持数据平衡

allocation

封装了分片分配相关的功能和策略,包括主分片的分配和副本分片的分配,创建新索引,集群完全重启都需要分片分配的过程

Discovery

发现模块负责发现集群中的节点,以及选举主节点,当节点加入或者退出集群时,主节点会采取相应的行动

gateway

负责对收到Master广播下来的集群状态数据的持久化存储,并在集群完成重启时恢复它们

Indices

索引模块管理全局级的索引设置,不包括索引级的(索引设置分为全局级和每个索引级),它还封装了索引数据恢复功能,集群启动阶段需要的主分片恢复和副本分片恢复就是在这个模块实现的

HTTP

http模块允许通过JSON over HTTP的方式访问ES的API,该模块本质上是完全异步的

Transport

传输模块用于集群内节点之间的内部通信,节点到另一个节点的每个请求都使用传输模块,也是异步的

Engine

封装了对Lucene的操作及translog的调用,是对一个分片读写操作的最终提供者

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值