背景

众所周知,Elasticsearch是⼀个实时的分布式搜索引擎,为⽤户提供搜索服务。当我们决定存储某种数据,在创建索引的时候就需要将数据结构,即Mapping确定下来,于此同时索引的设定和很多固定配置将不能改变。

那如果后续业务发生变化,需要改变数据结构或者更换ES更换分词器怎么办呢?为此,Elastic团队提供了很多通过辅助⼯具来帮助开发⼈员进⾏重建索引的方案。

如果对 reindex API 不熟悉,那么在遇到重构的时候,必然事倍功半,效率低下。反之,就可以方便地进行索引重构,省时省力。

步骤

假设之前我们已经存在一个blog索引,因为更换分词器需要对该索引中的数据进行重建索引,以便支持业务使用新的分词规则搜索数据,并且尽可能使这个变化对外服务没有感知,大概分为以下几个步骤:​

  • 新增⼀个索引blog_lastest,Mapping数据结构与blog索引一致
  • blog数据同步至blog_lastest
  • 删除blog索引
  • 数据同步后给blog_lastest添加别名blog

新建索引

在这里推荐一个ES管理工具Kibana,主要针对数据的探索、可视化和分析。

put /blog_lastest/
{
"mappings":{
"properties":{
"title":{
"type":"text",
"analyzer":"ik_max_word"
},
"author":{
"type":"keyword",
"fields":{
"seg":{
"type":"text",
"analyzer":"ik_max_word"
}
}
}
}
}
}

将旧索引数据copy到新索引

同步等待

接⼝将会在 reindex 结束后返回

POST /_reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest"
}
}

kibana 中的使用如下所示

当然高版本(7.1.1)中,ES都有提供对应的Java REST Client,比如

ReindexRequest reindexRequest = new ReindexRequest();
reindexRequest.setSourceIndices("blog").setSource.setDestIndex("blog_lastest");
TaskSubmissionResponse taskSubmissionResponse = client.submitReindexTask(reindexRequest, RequestOptions.DEFAULT);

为了防止赘述,接下来举例全部以kibana中请求介绍,如果有需要用Java REST Client,可以自行去ES官网查看。

异步执⾏

如果 reindex 时间过⻓,建议加上 wait_for_completion=false 的参数条件,这样 reindex 将直接返回 taskId

POST /_reindex?wait_for_completion=false
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest"
}
}

返回:

{
"task" : "dpBihNSMQfSlboMGlTgCBA:4728038"
}

op_type 参数

op_type 参数控制着写入数据的冲突处理方式,如果把 op_type 设置为 create【默认值】,在 _reindex API 中,表示写入时只在 dest index中添加不存在的 doucment,如果相同的 document 已经存在,则会报 version confilct 的错误,那么索引操作就会失败。【这种方式与使用 _create API 时效果一致】

POST _reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
}
}

如果这样设置了,也就不存在更新数据的场景了【冲突数据无法写入】,我们也可以把 op_type 设置为 index,表示所有的数据全部重新索引创建。

conflicts 配置

默认情况下,当发生 version conflict 的时候,_reindex 会被 abort,任务终止【此时数据还没有 reindex 完成】,在返回体中的 failures 指标中会包含冲突的数据【有时候数据会非常多】,除非把 conflicts 设置为 proceed

关于 abort 的说明,如果产生了 abort,已经执行的数据【例如更新写入的】仍然存在于目标索引,此时任务终止,还会有数据没有被执行,也就是漏数了。换句话说,该执行过程不会回滚,只会终止。如果设置了 proceed,任务在检测到数据冲突的情况下,不会终止,会跳过冲突数据继续执行,直到所有数据执行完成,此时不会漏掉正常的数据,只会漏掉有冲突的数据。

POST _reindex
{
"source": {
"index": "blog"
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
},
"conflicts": "proceed"
}

我们可以故意把 op_type 设置为 create,人为制造数据冲突的场景,测试时更容易观察到冲突现象。

如果把 conflicts 设置为 proceed,在返回体结果中不会再出现 failures 的信息,但是通过 version_conflicts 指标可以看到具体的数量。

批次大小配置

当你发现reindex的速度有些慢的时候,可以在 query 参数的同一层次【即 source 参数中】添加 size 参数,表示 scroll size 的大小【会影响批次的次数,进而影响整体的速度】,如果不显式设置,默认是一批 1000 条数据,在一开始的简单示例中也看到了。

如下,设置 scroll size 为 5000:

POST /_reindex?wait_for_completion=false
{
"source": {
"index": "blog",
"size":5000
},
"dest": {
"index": "blog_lastest",
"op_type": "create"
},
"conflicts": "proceed"
}

测试后,速度达到了 30 分钟 500 万左右,明显提升了很多。

根据taskId可以实时查看任务的执行状态

一般来说,如果我们的 source index 很大【比如几百万数据量】,则可能需要比较长的时间来完成 _reindex 的工作,可能需要几十分钟。而在此期间不可能一直等待结果返回,可以去做其它事情,如果中途需要查看进度,可以通过 _tasks API 进行查看。

GET /_tasks/{taskId}

返回:

{
"completed" : false,
"task" : {
"node" : "dpBihNSMQfSlboMGlTgCBA",
"id" : 4704218,
"type" : "transport",
"action" : "indices:data/write/reindex",
……
}

当执行完毕时,completed为true

查看任务进度以及取消任务,除了根据taskId查看以外,我们还可以通过查看所有的任务中筛选本次reindex的任务。

GET _tasks?detailed=true&actions=*reindex

返回结果:

{
"nodes" : {
"dpBihNSMQfSlboMGlTgCBA" : {
"name" : "node-16111-9210",
"transport_address" : "192.168.XXX.XXX:9310",
"host" : "192.168.XXX.XXX",
"ip" : "192.168.16.111:9310",
"roles" : [
"ingest",
"master"
],
"attributes" : {
"xpack.installed" : "true",
"transform.node" : "false"
},
"tasks" : {
"dpBihNSMQfSlboMGlTgCBA:6629305" : {
"node" : "dpBihNSMQfSlboMGlTgCBA",
"id" : 6629305,
"type" : "transport",
"action" : "indices:data/write/reindex",
"status" : {
"total" : 8361421,
"updated" : 0,
"created" : 254006,
"deleted" : 0,
"batches" : 743,
"version_conflicts" : 3455994,
"noops" : 0,
"retries" : {
"bulk" : 0,
"search" : 0
},
"throttled_millis" : 0,
"requests_per_second" : -1.0,
"throttled_until_millis" : 0
},
"description" : "reindex from [blog] to [blog_lastest][_doc]",
"start_time_in_millis" : 1609338953464,
"running_time_in_nanos" : 1276738396689,
"cancellable" : true,
"headers" : { }
}
}
}
}
}

注意观察里面的几个重要指标,例如从 description 中可以看到任务描述,从 tasks 中可以找到任务的 id【例如 dpBihNSMQfSlboMGlTgCBA:6629305】,从 cancellable 可以判断任务是否支持取消操作。

这个 API 其实就是模糊匹配,同理也可以查询其它类型的任务信息,例如使用 GET _tasks?detailed=true&actions=*byquery 查看查询请求的状态。

当集群的任务太多时我们就可以根据task_id,也就是上面提到GET /_tasks/task_id 方式更加准确地查询指定任务的状态,避免集群的任务过多,不方便查看。

如果遇到操作失误的场景,想取消任务,有没有办法呢?

当然有啦,虽然覆水难收,通过调用

_tasks API

POST _tasks/task_id/_cancel

这里的 task_id 就是通过上面的查询任务接口获取的任务id(任务要支持取消操作,即【cancellable 为 true】时方能收效)。

删除旧索引

当我们通过 API 查询发现任务完成后,就可以进行后续操作,我这里是要删除旧索引,然后再给新索引起别名,用于替换旧索引,这样才能保证对外服务没有任何感知。

DELETE /blog

使用别名

POST /_aliases
{
"actions":[
{
"add":{
"index":"blog_lastest",
"alias":"blog"
}
}
]
}

通过别名访问新索引

进行过以上操作后,我们可以使用一个简单的搜索验证服务。

POST /blog/_search
{
"query": {
"match": {
"author": "james"
}
}
}

如果搜索结果达到我们的预期目标,至此,数据索引重建迁移完成。

本文可转载,但需声明原文出处。 程序员小明,一个很少加班的程序员。欢迎关注微信公众号“程序员小明”,获取更多优质文章。

「Elasticsearch」ES重建索引怎么才能做到数据无缝迁移呢?的更多相关文章

  1. 「Elasticsearch」SpringBoot快速集成ES

    Elastic Search 的底层是开源库 Lucene.但是Lucene的使用门槛比较高,必须自己写代码去调用它的接口.而Elastic Search的出现正是为了解决了这个问题,它是 Lucen ...

  2. 把虚拟教练带回家,「EuMotus」想用AI实现高精度运动反馈系统

    https://36kr.com/p/5089139.html 无需穿戴设备,只需一个红外摄像头和+已安装好EuMotus专利软件的手提电脑 由政府主导的高达2200亿美金的健身与运动支出,15%的健 ...

  3. 「从零单排HBase 12」HBase二级索引Phoenix使用与最佳实践

    Phoenix是构建在HBase上的一个SQL层,能让我们用标准的JDBC APIs对HBase数据进行增删改查,构建二级索引.当然,开源产品嘛,自然需要注意“避坑”啦,阿丸会把使用方式和最佳实践都告 ...

  4. ELK学习总结(4-1)elasticsearch更改mapping(不停服务重建索引)

    elasticsearch更改mapping(不停服务重建索引)原文 http://donlianli.iteye.com/blog/1924721Elasticsearch的mapping一旦创建, ...

  5. elasticsearch更改mapping(不停服务重建索引)

    转载地址:http://donlianli.iteye.com/blog/1924721?utm_source=tuicool&utm_medium=referral Elasticsearc ...

  6. elasticsearch更改mapping,不停服务重建索引(转)

    原文:http://donlianli.iteye.com/blog/1924721?utm_source=tuicool&utm_medium=referral Elasticsearch的 ...

  7. Elasticsearch系列---实战零停机重建索引

    前言 我们使用Elasticsearch索引文档时,最理想的情况是文档JSON结构是确定的,数据源源不断地灌进来即可,但实际情况中,没人能够阻拦需求的变更,在项目的某个版本,可能会对原有的文档结构造成 ...

  8. ES数据库重建索引——Reindex(数据迁移)

    应用背景: 1.当你的数据量过大,而你的索引最初创建的分片数量不足,导致数据入库较慢的情况,此时需要扩大分片的数量,此时可以尝试使用Reindex. 2.当数据的mapping需要修改,但是大量的数据 ...

  9. 「ZJOI2019」&「十二省联考 2019」题解索引

    「ZJOI2019」&「十二省联考 2019」题解索引 「ZJOI2019」 「ZJOI2019」线段树 「ZJOI2019」Minimax 搜索 「十二省联考 2019」 「十二省联考 20 ...

随机推荐

  1. 解决 spring-integration-mqtt 频繁报 Lost connection 错误

    问题描述 在之前的博客介绍了如何在 Spring Boot 集成 MQTT,后面使用中没有发现问题,最近发现一直报错: Lost connection: Connection lost; retryi ...

  2. 痞子衡嵌入式:深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是深入i.MXRT1050系列ROM中串行NOR Flash启动初始化流程. 从外部串行NOR Flash启动问题是i.MXRT系列开发最 ...

  3. day4(JWT介绍)

    1.JWT介绍 1.1jwt原理 最简单理解:jwt本质就是, 把用户信息通过加密后生成的一个字符串 JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户 { "Use ...

  4. 老猿学5G扫盲贴:N6接口用户平面协议栈对应的网络分层模型

    在网络通信模型中,都对应有分层的网络结构,如开放式系统互联(OSI)的七层模型(物理层.数据链路层.网络层.传输层.会话层.表示层和应用层)以及TCP/IP四层(网络接口层.网络层.传输层和应用层)模 ...

  5. Python中使用百分号占位符的字符串格式化方法中%s和%r的输出内容有何不同?

    Python中使用百分号占位符的字符串格式化方法中%s和%r表示需要显示的数据对应变量x会以str(x)还是repr(x)输出内容展示. 关于str和repr的关系请见: <Python中rep ...

  6. PyQt(Python+Qt)学习随笔:QTabWidget部件信号简介

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 QTabWidget自身提供的信号包括如下: currentChanged(int index):每 ...

  7. 自动化运维工具之Puppet master/agent模型、站点清单和puppet多环境设定

    前文我们了解了puppe中模块的使用,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/14086315.html:今天我来了解下puppet的master/age ...

  8. Object.prototype.toString.call()为什么可以用来检测数据类型?

    obj.toString()方法是用来干什么的 每一个对象都有一个toString()方法,默认情况下toString()被每一个Object对象继承,如果此方法未被重写,toString()返回&q ...

  9. 团队作业3_需求改进&系统设计

    一.需求&原型改进 1.需求改进: (1)发现问题:通过发布问卷调查及收集整理的形式发现用户的新需求: (2)修改需求:考虑新增提醒用户未完成事件的功能.   附:用户调查问卷(如下) 调研途 ...

  10. 【题解】NOI 系列题解总集

    每次做一道 NOI 系列的估计都很激动吧,对于我这种萌新来说( P1731 [NOI1999]生日蛋糕 练习剪枝技巧,关于剪枝,欢迎看我的垃圾无意义笔记 这道题是有一定难度的,需要运用各种高科技剪枝( ...