Elasticsearch 基础:数据索引流程

ES 数据索引流程

ES 的数据从接收、存储到能够支持检索是一个相对比较复杂的过程,这一过程中的每一步都是为了性能、分布式支持、并行、高可用和可拓展等特点作出的设计。

整体流程

  1. 通过接口接收数据
  2. 数据路由寻址
  3. 数据索引
  4. 让数据支持检索

后面我们来详细了解下整个流程。

通过接口接收数据

ES 会通过 POST/PUT _docPOST _bulk 等接受数据,区别只是前者一次性只会发送/接收一条数据,而后者会一次性处理多条数据。在 bulk 的处理中,ES 会自行对数据列表进行遍历,并按单条数据的方式继续后续的操作。

较大的集群,可能会有独立的协调节点(coordinate node)和/或数据预处理节点(ingest node),但是任何节点接受了数据之后都会遵循一样的流程——对数据进行分析、转发等。

数据处理及数据流

数据收到之后可能会根据配置(index templatedefault_pipeline 等)而会被转发给以下三个目标之一:

  1. 指定索引
    1. 当没有别的配置时,数据会直接写入指定索引进行后续的 mapping 等操作
    2. 如果写入目标是索引别名(alias),则会通过别名来寻找一个目标索引进行写入
    3. 如果写入的别名保护了多个索引,则会往写索引("is_write_index": true")中写入,如果没有写别名则会报错
  2. 数据处理管道(ingest pipeline
    1. 在接口中或者索引模板中可以指定数据处理管道,对写入数据进行预处理
    2. 当发现数据需要经过数据处理管道时,数据会被转发到带有数据处理功能的节点(node.roles = ["ingest"]
    3. 数据在经过了预处理之后再转发给指定的索引(某些 pipeline 会改变请求中的索引名称,进而影响到数据寻址)
  3. 数据流(data stream
    1. 数据流更像是一个由 ES 自己维护的,根据生命周期策略(ilm)进行滚动的一系列索引
    2. 对数据读/写方来说,他们制定的索引都是一个索引别名,具体的路由部分会由 ES 进行托管
    3. 这种索引模式比较新,具体的信息可以参考官方文档

路由

数据在接收之后 ES 会根据数据的一系列属性,如索引名称(别名)、主键(_id)或 router key 等给数据进行寻址,找到合适的放置数据的索引、分片、节点之后会把数据转发过去进行后续的操作。收到数据到数据转发之前可能会经历数据预处理(ingest pipeline),因为数据管道可能会对数据本身(包括索引名称)进行修改。如果数据中指定的索引不存在,配置中又没有禁止自动创建索引("action.auto_create_index": true),ES 会尝试先创建指定索引再做数据的后续处理。

为了保证数据写入速度,建议使用原生的(默认由 ES 自己计算出来的)_id 来存储,这样可能不太会造成数据倾斜。在真实业务使用时,可能会有定制化的路由策略,需要指定自己的路由键(如自定 _id、指定routing=fieldA等),这时就要注意, ES 在寻址时会需要对指定的字段进行计算,所以要合理的设计路由键,避免不必要的数据运算,同时要将数据尽量打散,避免数据倾斜。

索引

当数据寻址结束被发给目标主分片(primary shard)之后,就会进入索引流程,大致分为以下几个步骤:

  1. 数据校验并解析数据处理请求类型
  2. Segment 或者 TransLog 里通过 _id 找到完整的文档,如果找不到则跳过
  3. 将新接受的数据和从系统中找到的数据进行 merge,给数据版本设为 v1
  4. 解析整条数据,添加一些系统字段(如_id之类的)
  5. 根据当前数据状态更新 mapping
    1. 如果存在 dynamic mapping 则自动解析
    2. 如果存在 dynamic template 则按模板中的设置进行创建
    3. 如果存在其他配置,如 "dynamic mapping": false 之类的再按配置对冲突忽略或抛出异常
  6. SequcenceNumberService 获取 SequenceIDVersion
    1. SequenceID 用于初始化 LocalCheckPoint
    2. Version 是根据当前数据版本(v1)做 v1 + 1 操作防止并发写入带来的数据不一致
  7. _id 加锁准备写入 Lucene
    1. 这时会判断当前已存在的数据里是否和上一步中的 v2 冲突(版本大于等于 v2)。如果冲突则返回 2. 或者报错
    2. 调用 lucene 的新加(addDoucument)或者更新(updateDocument)接口写入数据,删除操作也是调用更新接口
    3. 为了保证数据删除的原子性,ES 会在删除之前对 refresh 操作加锁,等数据更新(添加)操作结束之后再释放
  8. 写入 TransLog
    1. 写完了 segment 之后,数据会以 K-V_id - Doc)的形式存在 TransLog 里面
    2. 这样如果存在类似 getById 的需求就可以直接从 TransLog 里获取完整的数据了
  9. 重建 bulkRequest
    1. 把里面的请求转化为只包含 indexdelete 的请求,用来转发给所有的副本分片
  10. flush translog
  11. 一般默认情况下,TransLog 会在数据写完之后进行落盘
  12. 如果对可靠性要求不高对写入速度要求比较高可以通过将 TransLog 落盘调整为异步,增大落盘间隔和单次落盘数据阈值等方式进行调整
    1. "index.translog.durability": "request" => "async"
    2. "index.translog.sync_interval": 5s => "30s"
    3. "index.translog.flush_threshold_size": "512mb" => "2G"
  13. 把 9. 构建的 bulkRequest 转发给副本
  14. 主分片会把 bulkRequest 转发给所有的副本,并等待所有副本的结果返回
  15. 如果某个副本执行失败,主分片会给 master 节点发请求,把这个副本标记为失败并移除
  16. bulkRequest 发送的同时,主分片也会把SequenceIDprimaryTermGlobalCheckPoint等信息一起发给副本
  17. 等待副本的返回
  18. 当所有副本都返回成功之后,主分片才会返回一个插入成功的 ack,同时更新主分片的LocalCheckPoint

支持搜索

数据写入分片(副本)之后也不能立刻支持搜索,而是需要先进行 refresh 操作之后才行。refresh 操作主要是用于将内存缓冲区中的数据进行段合并(segment merge),然后将合并后的结果写入磁盘里 (TransLog),对于某些通过 "index.store.preload": ["fieldA", "fieldB"] 设置所指定的字段是写入系统缓存,写入成功之后才能支持检索。

这种设计主要是为了保证数据的可靠性,因为写在内存缓冲区的数据可能会因为节点重启等原因丢失,而写入了磁盘之后再进行重启等就不太容易造成数据丢失。

ES 自己也会在后台持续的进行段合并,因为数据的检索是顺序的检索段(segment),更少数量的段在搜索中需要进行的上下文切换越少。

FAQ

  1. Q:对于数据插入来说,PUTPOST 有什么区别吗?
    1. A:根据原厂工程师的说法,他们设计这俩接口是想遵守 restful 语义的
      1. PUT 代表了添加数据,ES 会尝试自动设置一个 _id
      2. POST 代表了更新数据
      3. 但是这俩接口后续的处理逻辑是一样的,如果没有设置 _id,在 POST 请求中 ES 也会尝试设置一个;如果指定了 _idES 会直接用而不会重新计算 _id
  2. Q:当集群状态为不正常(Red)的时候,数据的读写受影响吗?
    1. A:集群状态红了代表至少有一个主分片无法成功放置(如果是副本无法放置则为 yellow
      1. 那么在这个分片正常分配之前,针对它的读写都会有影响
      2. 同时集群会积极的尝试对这个(些)分片进行修复,如给副本升级、重建数据等,这些操作也会挤占集群/节点的内存和 CPU 资源
      3. 所以结论是,当集群状态不正常时,可能对数据的读写有影响,但是根据访问量的大小以及数据所属的索引/分片的不同这个影响也会有所不同
  3. Q:7.11 里新加的 runtime_fields 呢?
    1. A:runtime_fields 相当于是对于每条数据通过 script 计算一个值出来进行后续的计算
    2. 在数据写入的时候这部分计算并不会执行,而是在数据请求的时候进行计算
    3. 所以这部分的召回会被认为是重的(expenisive),如果设置了 "search.allow_expensive_queries": false,针对 runtime_fields 的请求就会被 ES 拒绝
      1. 所有的 expensive queries 参考官方文档
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值