如何平滑切换线上Elasticsearch索引

标签: | 发表时间:2021-04-08 11:26 | 作者:
出处:https://mp.weixin.qq.com

前言

哈喽,大家好,我是 asong,今天与大家聊一聊如何平滑切换线上的 ES索引。使用过 ES的朋友们都知道,修改索引真的是一件费时又费力的工作,所以我们应该在创建索引的时候就尽量设计好索引能够满足需求,当然这几乎是不可能的,毕竟存在着万恶的产品经理,所以掌握"平滑切换线上的 ES索引"就很必要,接下来我们就来看一看如何实现!

前置条件

能够平滑切换线上的 ES索引需要有两个先决条件,只有满足了这两个条件才能去执行接下来的平滑切换操作,否则一切操作都是白费。

前置条件之使用别名访问索引

重建索引的问题是必须更新应用中的索引名称,索引别名就是用来解决这个问题的。索引别名就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用。 别名带给我们极大的灵活性,允许我们做下面这些:

  • 在运行的集群中可以无缝的从一个索引切换到另一个索引
  • 给多个索引分组
  • 给索引的一个子集创建 视图

索引与索引别名的关系,我们画个图来说一下:

上图中 user_index就是索引别名, user_index_v1user_index_v2user_index_v3分别是三个索引,这里索引别名 user_indexuser_index_v1进行了关联,所以我们搜索的时候使用索引别名,也就是去索引 user_index_v1上查询。假设现在我们不想使用索引 user_index_v1了,想使用索引 user_index_v2,那么直接使用 _aliases操作执行原子操作(后面介绍具体使用),将索引别名 user_index与索引 user_index_v2进行关联,现在使用索引别名 user_index搜索的就是索引 user_index_v2的数据了。

前置条件之足够空间

既然要重建 ES索引,就一定保证你有足够的空间存储数据,可以使用如下指令查看 ES每个节点的可用磁盘空间:

    curl http://localhost:9200/_cat/allocation\?v      

获得结果如下:

如何平滑切换

因为大家使用的 ES场景不同,所以平滑切换的步骤会稍有偏差,但是都离不开这几个步骤:

  1. 创建新索引
  2. 同步数据/数据迁移到新索引
  3. 切换索引

先介绍一下数据迁移和切换索引使用什么指令操作:

  • 数据迁移

使用 ES中提供的 reindex api就可以将数据 copy到新索引中,比如:

    curl --location --request POST 'http://localhost:9200/_reindex' \      
--header 'Content-Type: application/json' \
--data-raw '{
"conflicts": "proceed",
"source": {
"index": "user_index_v1"
},
"dest": {
"index": "user_index_v2",
"op_type": "create",
"version_type": "external"
}
}'

介绍一下上面几个字段的意义:

  • "source":{"index": "user_index_v1"}:这里代表我们要迁移数据的源索引;
  • "dest":{"index": "user_index_v2"}:这里代表我们要迁移的目标索引;
  • "conflicts": "proceed":默认情况下,版本冲突会导致 _reindex操作终止,可以设置这个字段使该请求遇到冲突时不会终止,而是统计冲突数量;
  • "version_type": "extrenal":这个字段介绍起来比较复杂,且听我细细道来。 _reindex指令会生成源索引的快照,它的目标索引必须是一个不同的索引[新索引],以便避免版本冲突。如果不设置 version_type字段,默认为 internalES会直接将文档转存储到目标索引中( dest index),直接覆盖任何具有相同类型和 iddocument,不会产生版本冲突。如果把 version_type设置为 extertral,那么 ES会从源索引( source index)中读取 version字段,当遇到具有相同类型和 iddocument时,只会保留 new version,即最新的 version对应的数据。此时可能会有冲突产生,比如当把 op_tpye设置为 create,对于产生的冲突现象,返回体中的 failures会携带冲突的数据信息【类似详细的日志可以查看】。
  • op_typeop_type参数控制着写入数据的冲突处理方式,如果把 op_type设置为 create【默认值】,在 _reindex API中,表示写入时只在 dest index中添加不存在的 doucment,如果相同的 document已经存在,则会报 version confilct的错误,那么索引操作就会失败。【这种方式与使用 _create API时效果一致】。

更多 _redinx api使用方法可以移步官方文档学习:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/docs-reindex.html

上面只是举一个简单的例子,具体要在数据迁移中使用哪些参数需要根据场景而定。

什么时候可以选择数据迁移:

  1. 当我们新创建的索引只改变了 mapping结构时,例如:删除字段,更新字段的类型,这种场景就可以直接使用 _reindex进行数据迁移;
  2. 新创建的索引中添加了新字段,但是新的字段都是由老的字段计算得到的,这种情况,也可以使用 _reindex进行数据迁移, api中使用 script参数,编写你的脚本即可。

注意:使用 _redindex接口时要注意一个问题,接口会在 reindex结束后返回,接口超时控制只有 30s,如果 reindex时间过长,建议加上 wait_for_completion=false参数,这样 redindex就变成异步任务,返回的是 taskID,查看进度可以通过 _tasks API进行查看。

  • 切换索引

ES中两种方式管理别名: _alias用于单个操作, _aliases用于执行多个原子级操作。

因为我们这里要做的是切换索引,主要分为两个步骤:

  1. 移除当前索引与索引别名的关联
  2. 将新建的索引与索引别名进行关联

所以我们可以选择 _alisases执行原子操作:

    curl --location --request POST 'http://localhost:9200/_aliases' \      
--header 'Content-Type: application/json' \
--data-raw '{
"actions": [
{"remove": {"index": "user_index_v2", "alias": "user_index"}},
{ "add": {"index": "user_index_v1", "alias": "user_index"}}
]
}'

举例子

假设我们有一个 user_index_v1,他的 mapping结构如下;

    {      
    "mappings":{
        "properties":{
            "id":{
                "type":"byte"
            },
            "Name":{
                "type":"text"
            },
            "Age":{
                "type":"byte"
            }
        }
    }
}

现在这个 v1索引中,我们的 id字段使用的 byte类型,显然范围是比较小的,随着数据量增多, id数值的增大,该字段已经不能满足存储需求了,所以需要把它换成 long类型,因此可以创建 v2索引:

    {      
    "mappings":{
        "properties":{
            "id":{
                "type":"long"
            },
            "Name":{
                "type":"text"
            },
            "Age":{
                "type":"byte"
            }
        }
    }
}

现在我们就来考虑一下,如何平滑的进行索引切换。这里假设我们 ES中数据同步采用的 消息队列推送完成的,所以在切换索引时要考虑数据损失的问题。

这里我们可以列举几种方案如下:

  • 方案一:直接创建 v2索引,使用 _aliases切换索引,进行数据迁移,优点是直接切换别名和索引的关联,简单方便,缺点是出现问题回退到旧索引,会有数据损失,直接切换到 v2索引会导致服务在数据没有迁移完之前不可用。
  • 方案二:创建 v2索引,添加 v2索引与别名的关联,进行数据迁移, _alias操作解除别名和 v2索引的关联。优点是不会造成服务不可用,缺点是在解除别名和 v1关联之前,一个别名关联两个索引,单索引操作无法执行,只能搜索,搜索也会出现数据重复,并且也会造成数据损失。
  • 方案三:创建 v2索引,添加 v2索引与别名的关联,修改代码写入操作使用 v2索引,搜索操作使用别名索引,进行数据迁移,解除 v1索引与别名的关联,优点是搜索和写入操作分开了,缺点是回退需要修改代码,并且会出现数据损失,如果 v2索引不可用了,不能立刻回退索引。
  • 方案四:创建 v2索引,进行数据迁移,然后切换索引;优点是同步数据到v2期间搜索功能正常使用,回退无数据损失;缺点是会造成数据丢失。
  • 方案五:创建 v2索引,添加两个别名索引 readwrite,添加别名 readv1索引、 v2索引的关联,添加别名 writev2索引的关联,进行数据迁移,解除别名 readv1索引的关联;优点是搜索和写入分开了,更新索引时只需要创建新索引,数据同步完成后,解除别名 read和旧索引关联即可;缺点是数据迁移完成之前,搜索结果会出现重复,回退到旧索引,会有数据损失。

这里总共列举了5种方案,我也不推荐具体使用那个方案比较好,各有利弊,大家可以根据自己的业务场景来进行选择。

这里以选择方案四为例子,给出我的脚本数据,作为样例;

  • 创建 user_index_v2索引:
    #!/bin/bash      

url=$1
index=$2


echo `curl --location --request GET ${url}/${index}`

echo `curl --location --request PUT ${url}/${index} \
--header 'Content-Type: application/json' \
--data-raw '{
    "mappings":{
        "properties":{
            "id":{
                "type":"long"
            },
            "Name":{
                "type":"text"
            },
            "Age":{
                "type":"byte"
            }
        }
    }
}'`

echo `curl --location --request GET ${url}/${index}`

运行指令: ./create_index.sh http://localhost:9200 user_index_v2

  • 进行数据迁移(数据量比较大时建议分批 and异步处理)
    #!/bin/bash      

url=$1



echo `curl --location --request POST ${url}/'_reindex?wait_for_completion=false' \
--header 'Content-Type: application/json' \
--data-raw '{
  "conflicts": "proceed",
  "source": {
    "index": "user_index_v1"
  },
  "dest": {
    "index": "user_index_v2",
    "op_type": "create",
    "version_type": "external"
  }
}'`

运行指令: ./reindex.sh http://localhost:9200

  • 切换索引
    #!/bin/bash      

url=$1
aliasIndex=$2
oldIndex=$3
newIndex=$4

echo `curl --location --request GET ${url}/${aliasIndex}`

echo `curl --location --request POST ${url}/_aliases --header 'Content-Type: application/json' --data-raw '{"actions": [{"remove": {"index": "'$oldIndex'", "alias": "'$aliasIndex'"}},{ "add": {"index": "'$newIndex'",  "alias": "'$aliasIndex'"}}]}'`

echo `curl --location --request GET ${url}/${aliasIndex}`

运行指令:./aliases.sh http://localhost:9200 user_index user_index_v1 user_index_v2

总结

本文例举了几种平滑切换 ES索引的方案,可以看出修改索引真不是一件容易的事情,要考虑的事情比较多,所以最好在第一次创建索引的时候就多考虑一下以后的使用场景,确定好字段和类型,这样就可以避免重建 ES索引。当然随着产品的需求变更,重建 ES索引也是不可避免的,上面几种仅供大家参考,根据自己的场景去选择就好啦。

好啦,这篇文章就到这里啦,素质三连(分享、点赞、在看)都是笔者持续创作更多优质内容的动力!

创建了一个Golang学习交流群,欢迎各位大佬们踊跃入群,我们一起学习交流。入群方式:加我vx拉你入群,或者公众号获取入群二维码

结尾给大家发一个小福利吧,最近我在看[微服务架构设计模式]这一本书,讲的很好,自己也收集了一本PDF,有需要的小伙可以到自行下载。获取方式:关注公众号:[Golang梦工厂],后台回复:[微服务],即可获取。

我翻译了一份GIN中文文档,会定期进行维护,有需要的小伙伴后台回复[gin]即可下载。

翻译了一份Machinery中文文档,会定期进行维护,有需要的小伙伴们后台回复[machinery]即可获取。


相关 [平滑 切换 线上] 推荐:

如何平滑切换线上Elasticsearch索引

- -
asong,今天与大家聊一聊如何平滑切换线上的. ES的朋友们都知道,修改索引真的是一件费时又费力的工作,所以我们应该在创建索引的时候就尽量设计好索引能够满足需求,当然这几乎是不可能的,毕竟存在着万恶的产品经理,所以掌握"平滑切换线上的. ES索引"就很必要,接下来我们就来看一看如何实现. ES索引需要有两个先决条件,只有满足了这两个条件才能去执行接下来的平滑切换操作,否则一切操作都是白费.

javascript图片切换

- Haipeng - 博客园-首页原创精华区
闲来无事,练习js,参照网上的一些源码写了一个图片切换程序,刚来博客园,不懂怎么发布js程序,懂的朋友可以留言告之. 1.图片切换效果有以下几种:随机切换   4格纵向百叶窗  16格横向百叶窗  由里至外逐渐放大   中间向左右两边逐渐放大   中间向上下两边逐渐放大   由上至下落幕   由左至右   由左上至右下   由右下至左上              8格纵向百叶窗    8格纵向百叶窗2   8格万花筒  24格万花筒  4格滑行左上至右下  4格滑行左上至右下  4格滑行落幕   4格滑行延伸.

从 screen 切换到 tmux

- iworm - LinuxTOY
在我的 Linux 生活中,我曾做过几次重要的切换. 我先是从 Archlinux 切换到 Gentoo,后来又从 bash 切换到了 zsh. 现在,我又从 screen 切换到 tmux. 对于各个终端控来说,screen 是几乎每天都会使用的好工具,抛开确实不易. 但有了更加好用的 tmux,我为什么不切换.

广告计算——平滑CTR

- - CSDN博客综合推荐文章
在互联网发展的过程中,广告成为了互联网企业盈利的一个很重要的部分,根据不同的广告形式,互联网广告可以分为:. 展示广告(display ads). 赞助商搜索广告(sponsored search). 上下文广告(contextual advertising). 对于在线广告,主要有如下的几种竞价模型:.

Public DNS Tool-DNS切换工具

- - 无名小卒
Public DNS Tool v9.1下载: dbank| kuaipan| 官方. 无名小卒(Digital Fingerprint: b98c67913fef00419d415179421ab42f) Related Posts. Webluker-免费CDN、DNS解析和网站监控服务.

redis+Keepalived主从热备秒级切换

- - 开源软件 - ITeye博客
安装使用centos 5.10 . 编译环境 yum -y install gcc gcc+ gcc-c++ openssl openssl-devel pcre pcre-devel. 当 Master 与 Slave 均运作正常时, Master负责服务,Slave负责Standby;. 当 Master 挂掉,Slave 正常时, Slave接管服务,同时关闭主从复制功能;.

spring数据源动态切换

- - 企业架构 - ITeye博客
     原文->http://exceptioneye.iteye.com/blog/1698064.       在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上.

Gmail推出可切换的多种收件箱模式

- 小明 - 互联网的那点事
据国外媒体报道,Google日前对Gmail收件箱的功能和用户界面进行了升级,可以支持以标签切换多种收件箱模式. 此前,Gmail的收件箱只有一种模式,即按照时间先后顺序列出收到的所有电子邮件. 在新版中,上述模式被称为“经典模式”. 除此之外,Google还增加了若干种界面模式. “重要顺序”,即按照信件是否重要依次列出,用户可以自行设置通信的重要级别.

SwitchyPlus 发布,全平台原生API支持代理切换

- KP - Chrome迷
感谢读者 xxy171070 的分享. SwitchyPlus 是一款基于 Chrome 原生的实验性代理 API 开发的代理切换扩展,主要功能和 Chrome 平台上大名鼎鼎的代理切换插件 Proxy Switchy. 基本上一样,不过由于采用的是 Chrome 原生的 Proxy API,所以可以完美在各种平台上实现,不受系统代理调用的限制.

Unity的变革(一):全新的任务切换

- Feng - I'm TualatriX
好久没写技术相关的文章了,浑身发痒. 今天就来一篇,具体的说是介绍产品细节的文章,而不是具体的技术实现. 会写好几篇,第一篇便是讲Unity的——Ubuntu 11.04开始的默认桌面环境. 离Ubuntu 11.10的发布只有不到一个半月了,这次Ubuntu又会带来什么新鲜的东西. 还是像11.04发布那会,带来一个不是很稳定、又饱受争议的Unity.