乐趣区

关于lucene:再聊聊ES索引文档的流程

文档索引步骤

  1. 客户端向 node1 发送新建,查问或删除申请。
  2. 节点应用文档的_id 确定文档属于分片 0,申请会被转发到 node3,因为分片 0 的主分片目前被调配在 node3 上
  3. node3 在主分片下面执行申请,如果胜利了,它会将申请并行转化到 node1 与 node2 的正本分片上,一旦所有的正本分片都报告胜利,node3 将向协调节点报告胜利,协调节点向客户端报告胜利。

下面是按单个文档操作的,多个文档在应用 bulk 操作时和下面流程差不多,这里不再多说。

文档索引过程详解

整体流程图。

  • 协调节点默认应用文档 ID 参加计算(也反对通过 routing),以便为路由提供适合的分片。

    shard = hash(document_id) % (num_of_primary_shards)
    
  • 当分片所在的节点接管到来自协调节点的申请后,会将申请写入到 memory buffer,而后定时(默认 1 秒)写入到 filesystem cache(操作系统文件缓存,这里不禁 JVM 治理),从 memory buffer 到 filesystem cache 的过程就叫 refresh。须要留神的是数据写入 memory buffer 后并不以马上被检索,而只有通过 refresh 写入到 filesystem caceh 后也就是写入到 segment 之后,此时文档才能够被检索到。
  • 因为 memory buffer 与 filesystem cache 都还未写入磁盘所以会有失落的可能。ES 是通过 translog 机制来保证数据的可靠性的。在接管到申请后,同时也会写入到 translog,当 filesystem cache 中的数据写入到磁盘后才会革除 translog 里的数据,这个过程称 flush。flush 是定时触发(默认 30 分钟)或 translog 变得太大(默认为 512MS)。

并发下的 update 流程

ES 应用版本号这种乐观锁的机制解决并发批改问题,ES 保障了一个老版本的数据永远无奈重写或笼罩更新版本的数据,如果因版本号抵触批改失败能够应用 retry_on_conflict 参数设定重试次数。流程如下:

  1. 收到 update 申请后,从 segment 或者 translog 中读取同 id 的 Doc,并获取此时版本号。
  2. 将第 1 步版本号对应的全量 Doc 和申请中的局部字段合并为一个残缺的 Doc,同时更新内存中的 versionMap。这个时候 update 申请相当于一个 index 申请了。
  3. 加锁。
  4. 再次从 versionMap 中读取该 id 最大版本号,如果 versionMap 没有,则从 segment 或 translog 里读。
  5. 查看版本号是否抵触(查看第 1 步与第 4 步的版本号是否雷同,雷同为不抵触,不雷同为抵触),如果抵触则回退到开始的 update doc 阶段从新执行;如果没抵触则执行最新的 add 申请。
  6. 在 index doc 阶段,首先将 version+1,再将 doc 退出到 lucene 中,lucene 会先删除同 id 下已存在的 doc id,而后再减少新 doc,写入 lucene 胜利后,将更新后的版本号更新到 versionMap。
  7. 开释锁,局部更新流程完结。

ES 里的 translog

ES 为了缩小磁盘 IO 保障读写性能,个别是每隔一段时间(比方 5 分钟)才会将 segment 写入磁盘长久化,对于还未 flush 到磁盘的数据,如果产生宕机或掉电,那么内存里的数据是会失落的,ES 是如何保证数据的可靠性的呢?这里咱们来说下 translog。

在每个 shard 中,写入流程分两局部,先写入 lucene,再写入到 translog。写完 lucene 文件创建好索引后,此时索引还在内存里,接着去写 translog,写完 translog 后,刷新 translog 数据到磁盘上 (这个是可配置的,可能会立刻 flush 到磁盘也可能距离一段时间再 flush),写磁盘胜利后,申请返回用户。这里有几个关键点:

  1. 一是和数据库不同,数据库是先写 commitlog,而后再写内存,而 ES 是先写内存再写 translog,一种可能起因是 lucene 内存写入有很简单的逻辑,容易失败,比方分词,字段长度超限等,为了防止 translog 里有大量有效记录,就将写内存放到了后面
  2. 二是写入内存后,并不是可搜寻的,须要通过 refresh 将内存的对象转换成残缺的 segment 后,而后再次 reopen 后能力被搜寻,个别这个工夫设置为 1 秒,这也是 ES 被称为 NRT(near real time) 的起因
  3. 三是当 ES 作为 nosql 数据库时,查问形式是 getDocById,这种查问能够间接从 translog 中查问(translog 是以 key/value 模式写入的,key 是_id,value 是 Doc 内容),这时就成了 RT 实时零碎了。
  4. 四是每隔一段较长时间,比方 30 分钟后,lucene 会将内存中生成的 segment 刷新到磁盘上,刷新后的索引文件曾经长久化了,历史的 translog 会被清掉

个性总结

  • 可靠性:因为 lucene 的设计不思考可靠性,在 ES 中通过 replica 和 translog 两套机制保证数据的可靠性
  • 一致性:lucence 中的 flush 锁只保障 update 接口里的 delete 和 add 两头不会 flush,然而 add 实现后依然有可能立刻产生 flush,导致 segment 可读,这样就没法保障 primary 和其余 replica 能够同一时间 flush,进而呈现查问不稳固的状况,这里只能实现最终一致性。
  • 原子性:add 与 delete 都是间接调用 lucene 的接口,是原子的。当局部更新时,应用 version 和锁保障更新是原子的。
  • 隔离性:依然采纳 version 和部分锁来保住更新的是特定版本的数据
  • 实时性:应用定期 refresh segment 到内存,并且 reopen segment 形式保障搜寻能够在较短时间(比方 1 秒)内被搜寻到。通过将未刷新到磁盘数据记入 translog,保障对未提交数据能够通过 ID 实时拜访到
退出移动版