Elasticsearch:在不停机的情况下优化 Elasticsearch Reindex

实现零停机、高效率和成功迁移更新的指南。更多阅读:Elasticsearch:如何轻松安全地对实时 Elasticsearch 索引 reindex 你的数据。

在使用 Elasticsearch 的时候,总会有需要修改索引映射的时候,遇到这种情况,我们只能做 _reindex。 事实上,这是一项相当昂贵的操作,因为根据数据量和分片数量,完成索引的完整复制可能需要长达几个小时的时间。

花费的时间并不是一个大问题,但更严重的是,它会影响生产环境的性能甚至功能。

相信大家都明白,数据迁移会消耗大量的硬盘资源,肯定会影响性能,但是功能呢?

我们以常规的 _reindex 为例。 假设我们在索引上创建了一个别名。 如果我们没有别名,我们就有大麻烦了。

常规 _reindex 过程分为两个步骤。

  1. 调用 _reindex 命令开始数据迁移。
  2. 数据迁移完成后,调用 _aliases 命令进行新旧索引切换。

步骤 2 之后,新索引正式运行,并将负责所有读写请求。 然而,这只是一个完美的理想场景,事实上,事情不会那样发展。

下面是一个正常的场景。

实际上,在数据迁移期间或者切换别名之前,客户端会不断向原来的索引写入数据,而这些新的变化并不会迁移到新的索引中,从而导致数据不一致。

对于客户端来说,感觉是更改别名后,刚才所做的所有更改都会消失。 此外,正如我刚才提到的,一个大的索引迁移可能需要几个小时,所以客户的感受一定是显而易见的。

那么该怎么办?

Reindex 的正确流程

上述流程对原始流程进行了两处更改。

  1. _reindex 必须使用外部类型(external type)
  2. 切换别名后再次需要 _reindex。

我们来解释一下外部类型的概念。

默认情况下,_reindex 是内部的,这种数据迁移是通过使用原始索引覆盖新索引来完成的,并删除文档的 _version,因此新索引中的所有文档重新开始。

如果使用外部类型,则数据迁移时文档的 _version 会被带入新索引,那么如果新旧索引的 _id 冲突,则会比较 _version。 只有当原始文档的版本大于目标文档时才会被覆盖。

有点抽象? 让我们举个例子。

假设原始索引有一个如下所示的文档,Elasticsearch 元数据位于下划线开头。

PUT test/_doc/1
{
  "data": "Hello Elastic"
}

{
    "_id": "1",
    "_version": 1,
    "data": "Hello Elastic"
}

我们再次运行上面的命令一次以使得它的版本号码变为 2:

假如我们使用如下的命令来进行 reindex:

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test_reindexed"
  }
}

我们查看 test_reindexed 的内容:

GET test_reindexed/_doc/1

从上面的输出中,我们可以看出来无论之前的 version 号码是多少,在 reindex 之后,它的版本在新的索引中是 1,也即回到最初的版本。

上面的命令是 reindex 在默认时的表现。它相当于如下的命令格式:

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test_reindexed",
    "version_type": "internal"
  }
}

在进行迁移的时候,我们可以把  version_type 设置为 external。那么它的命令格式是这样的:

DELETE test_reindexed

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test_reindexed",
    "version_type": "external"
  }
}

运行完上面的命令后,我们可以查看 test_reindexd 里的数据:

GET test_reindexed/_doc/1

从上面的结果中,我们可以看出来,在新的索引中,它的 version 不再是之前 version_type 为 internal 时的情况。它的版本号现在是 2,而不是之前的 1。

当我们进行外部数据迁移时,_version: 2 也会被写入到新索引中。 如果有人在数据迁移期间将原始文档更改为 Hello Search,那么完整的文档将如下所示。

PUT test/_doc/1
{
  "data": "Hello Search"
}

那么,ID 为 1 的文档的版本会变为 3.

{
    "_id": "1",
    "_version": 3,
    "data": "Hello Search"
}

重做 _reindex 将发现 3 > 3,因此它将被 Hello Search 覆盖。

我们再次重新进行 reindex,我们会发现由于 _version 的值变为 3,它是大于 test_reindexed 索引中的版本号码 2:

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test_reindexed",
    "version_type": "external"
  }
}

我们再次查看最新的文档的值:

从上面的过程中,我们可以看出来:如果在迁移的过程中,或者我们再次运行 reindex,如果 test 中的值有变化,那么再次运行 reindex 后,它的值也会被更新到新的索引中。

那么,如果第二个 _reindex 有人修改了新索引中的文档怎么办? 例如,如果有人在新索引中将 Hello Elatic 更改为 Hello Elasticsearch,是否会被旧值覆盖? 整个过程如下所示。

答案是否定的,因为原始版本必须大于要覆盖的新版本才可以覆盖。

我们可以做如下的练习:

我们把 test_reindexed 中的文档的值修改为 Hello Elasticsearch:

PUT test_reindexed/_doc/1
{
  "data": "Hello Elasticsearch"
}

我们可以通过如下命令来查看它的值:

GET test_reindexed/_doc/1

我们再次确认 test 中的版本号码:

GET test/_doc/1

我们可以看到它们的版本号码是一致的。我们运行如下的 reindex:

POST _reindex
{
  "source": {
    "index": "test"
  },
  "dest": {
    "index": "test_reindexed",
    "version_type": "external"
  }
}

很显然,由于 test 文档中版本号要低于 test_reindexed 中的版本号,那么我们的文档不会被更新。

还有一个问题。

虽然我们会进行第二次 _reindex 来修补数据,但是如果修补时间很长,对于用户来说仍然会不一致。比如,在我们第一次的时候有多次更新,从而使得 test 中的 version 号码比较高。在切换之后,在 test_reindexed 中的文档更新过一次。那么在第二次 reindex 的过程中极有可能把 test 中的文档覆盖最新的数据中,从而造成数据的丢失。

有两种方法可以缩短重新索引时间。

  1. 尽可能减少第一次 _reindex 的时间。
  2. 提前过滤补丁数据。

关于第一点,_reindex 过程是由 Elasticsearch 控制的,我们还能做些什么来提高效率呢? 嘿,有。

我们可以修改新索引的设置,以尽量减少数据迁移过程中的 IO 开销。

refresh_interval = -1
number_of_replicas = 0

这非常简单。 首先,关闭 refresh_interval 的目的是让数据迁移期间只专注于写入 Translog,而不是在 Lucene 上花费额外的磁盘 IO。

其次,关闭 number_of_replicas 可以减少集群必须处理的额外数据复制开销。

另一方面,除了减少第一次 _reindex 的时间之外,还可以通过一些数据过滤来减少第二次 _reindex 的数据量。

例如,在 _reindex 期间引入数据的最后更新时间是一种可能的解决方案。 假设每个文档都有一个 updated_at 字段,那么在 _reindex 的查询中添加以下条件就会有效。

{
    "range": {
        "updated_at": { "gte": "now-1d"}
    }
}

结论

基于上述细节,让我们列出重建索引的理想流程。

  1. 创建目标索引。
  2. 更新目标索引的设置。 (refresh_interval = -1 且 number_of_replicas = 0)
  3. 使用外部类型进行 _reindex。
  4. 将别名从原始索引切换到目标索引。
  5. 使用外部类型再次执行 _reindex,最好进行额外的过滤。
  6. 再次更新目标索引设置。 (refresh_interval = null 且 number_of_replicas = null)

根据官方文档,设置为 null 可以恢复原来的设置。

因为 _reindex 是不可避免的,所以了解如何在不停机的情况下执行 _reindex 很重要。

事实上,利用 Elasticsearch 的流式索引,有更优雅的方法来完成它。 然而,流式索引的用例有很多限制,因此在实践中更常见的是使用常规索引。

本文提供了一个完整的过程来尽可能快地执行 _reindex 并最大限度地减少数据不一致的时间。 然而,所有这些都假设别名已正确创建,如果没有正确创建,则需要更多额外的步骤。 我觉得缺少别名已经违反了 Elasticsearch 的最佳实践,因此本文不会专门讨论这种情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/275330.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

go语言,ent库与gorm库,插入一条null值的time数据

情景介绍 使用go语言,我需要保存xxxTime的字段至数据库中,这个字段可能为空,也可能是一段时间。我采取的是统一先赋值为空,若有需要,则再进行插入(需要根据另一个字段判断是否插入) 在我的数据…

PTS 3.0:可观测加持的下一代性能测试服务

作者:肖长军(穹谷) 大家好,我是来自阿里云云原生应用平台的肖长军,花名穹谷,我此次分享的主题是《可观测加持的下一代性能测试服务》。提到性能测试大家并不陌生,性能测试已成为评估系统能力、…

使用rsync构建镜像网站

实验环境 某公司在深圳、北京两地各放置了一台网站服务器,分别应对南北大区内不断增长的客户访问需求,两台服务器的网站文档必须保持一致,如图12.3所示,同步链路已通过VPN专用线路实现。 需求描述 > 服务器 A(北京…

SpringBoot多线程与任务调度总结

一、前言 多线程与任务调度是java开发中必须掌握的技能,在springBoot的开发中,多线程和任务调度变得越来越简单。实现方式可以通过实现ApplicationRunner接口,重新run的方法实现多线程。任务调度则可以使用Scheduled注解 二、使用示例 Slf…

linux如何清理磁盘,使得数据难以恢复

sda 是硬盘,sda1 和 sda2 是硬盘的两个分区。centos-root 是一个逻辑卷,挂载在根目录 /。 /dev/sda 是硬盘,/dev/sda1 和 /dev/sda2 是硬盘的两个分区。 [rootnode2 ~]# dd if/dev/urandom of/dev/sda bs4M这个命令将从 /dev/urandom 读取随…

【软件工程大题】数据流图_DFD图_精简易上手

数据流图(DFD)是一种图形化技术,它描绘信息流和数据从输人移动到输出的过程中所经受的变换。 首先给出一个数据流图样例 基本的四种图形 直角矩形:代表源点或终点,一般来说,是人,如例图的仓库管理员和采购员圆形(也可以画成圆角矩形):是处理,一般来说,是动作,是动词名词的形式…

<JavaEE> TCP 的通信机制(二) -- 连接管理(三次握手和四次挥手)

目录 TCP的通信机制的核心特性 三、连接管理 1)什么是连接管理? 2)“三次握手”建立连接 1> 什么是“三次握手”? 2> “三次握手”的核心作用是什么? 3)“四次挥手”断开连接 1> 什么是“…

vue动态路由,三级及以上路由,地址跳转,但是页面不显示

vue动态路由的时候,一级,二级路由都正常展示,但是三级,四级,五级等就只看到地址跳转了,但是页面并没有跳转,原因是共用了一个<router-view></router-view> import Layout from /layout import Vue from vue import Router from vue-router import db from /utils/…

工具系列:TensorFlow决策森林_(8)组合决策森林和神经网络模型

文章目录 介绍安装 TensorFlow Decision Forests导入库数据集模型结构模型训练评估决策森林下一步是什么&#xff1f; 介绍 欢迎来到TensorFlow Decision Forests&#xff08;TF-DF&#xff09;的模型组合教程。本教程将向您展示如何使用通用的预处理层和Keras函数式API将多个…

linux 网络工具(一)

linux 网络工具 1. nmcli命令1.1 介绍1.2 networking 网络控制1.3 connection 连接管理1.4 device 设备管理1.5 nmcli 返回状态码 2. ifcfg命令家族2.1 ifconfig2.2 route2.3 netstat 3. 静态路由CentosUbuntu - netplanUbuntu - network-manager 1. nmcli命令 1.1 介绍 RHEL…

使用机器学习进行语法错误检测/纠正

francescofranco_39234 一、说明 一般的学习&#xff0c;特别是深度学习&#xff0c;促进了自然语言处理。各种模型使人们能够执行机器翻译、文本摘要和情感分析——仅举几个用例。今天&#xff0c;我们将研究另一个流行的用途&#xff1a;我们将使用Gramformer构建一个用于机器…

安卓全球定位系统RTK测量仪 手持GPS北斗定位仪可用于国土电力

RTK&#xff0c;英文全名叫做Real-time kinematic&#xff0c;也就是实时动态。这是一个简称&#xff0c;全称是RTK&#xff08;Real-time kinematic&#xff0c;实时动态&#xff09;载波相位差分技术。 RTK定位是一种高精度的全球卫星导航技术&#xff0c;是实时运用技术&…

springcloud之通过openfeign优化服务调用方式

写在前面 源码 。 在前面的文章中我们实际上已经完成了优惠券模块微服务化的改造&#xff0c;但是其中还是有比较多可以优化和增强的地方&#xff0c;本文就先来对服务间的通信方式进行优化&#xff0c;具体就是使用openfeign来替换调原来的webclient。下面我们就开始吧&#…

低代码平台在金融银行中的应用场景

随着数字化转型的推进&#xff0c;商业银行越来越重视技术在业务发展中的作用。在这个背景下&#xff0c;白码低代码平台作为一种新型的开发方式&#xff0c;正逐渐受到广大商业银行的关注和应用。白码低代码平台能够快速构建各类应用程序&#xff0c;提高开发效率&#xff0c;…

网络攻防中应该掌握的进阶工具udp2raw,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS

网络攻防中应该掌握的进阶工具udp2raw,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS。 udp2raw tunnel,通过raw socket给UDP包加上TCP或ICMP header,进而绕过UDP屏蔽或QoS,或在UDP不稳定的环境下提升稳定性。可以有效防止在使用kcptun或者finalspeed的…

TiDB 7.5 LTS 发版丨提升规模化场景下关键应用的稳定性和成本的灵活性

互联网时代&#xff0c;数据的迅猛增长给数据库带来了可扩展性的挑战&#xff0c;Gen AI 带来的数据暴增更加剧了这种挑战。传统的数据分片已经不能承载新时代数据暴增的需求&#xff0c;更简单且具有前瞻性的方法则是采用原生分布式数据库来解决扩展性问题。在这种规模化场景的…

MYSQL一一外键约束

概念&#xff1a;外键用来让两张表的数据之间建立联系&#xff0c;从而保证数据的一致性和完整性 建立外键&#xff1a; ①这是在建立表的时候建立外键的方法 ②这是在建立完表之后建立外键的方法&#xff1a; 删除外键&#xff1a; 现在有一张员工表&#xff08;emp&#xf…

Linux基本指令(二)

目录 &#x1f4d5;前言 &#x1f4d5;echo 输出重定向&#xff08;>&#xff09; 追加重定向(>>) 输入重定向&#xff08;<&#xff09; &#x1f4d5;more &#x1f4d5;less &#x1f4d5;head &#x1f4d5;tail 查看大文本的中间部分 &#x1f4d5…

Java 8 中的 Stream 轻松遍历树形结构!

可能平常会遇到一些需求&#xff0c;比如构建菜单&#xff0c;构建树形结构&#xff0c;数据库一般就使用父id来表示&#xff0c;为了降低数据库的查询压力&#xff0c;我们可以使用Java8中的Stream流一次性把数据查出来&#xff0c;然后通过流式处理&#xff0c;我们一起来看看…

深入ArkUI:深入实战组件text和text input

文章目录 Text组件介绍Text组件的属性方法Text:文本显示组件4.3TextInput组件实战案例:图片宽度控制页面本文总结要点回顾在今天的课程中,我们将深入学习ArkUI提供的基础组件,着重探讨text和text input两个组件。 Text组件介绍 Text组件是一个用于显示文本的组件,其主要作…