部署后

一旦将集群部署到生产环境后,就需要有一些工具及最佳实践经验来保证集群运行在最佳状态。本章将探讨动态配置、日志调优、索引性能优化以及集群备份。

动态变更设置

Elasticsearch 里很多设置都是动态的,可以通过 API 修改。需要强制重启节点(或者集群)的配置修改都要极力避免。而且虽然通过静态配置项也可以完成这些变更,我们建议你还是用 API 来实现。

集群更新 API 有两种工作模式:

临时(Transient)

这些变更在集群重启之前一直会生效。一旦整个集群重启,这些配置就被清除。

永久(Persistent)

这些变更会永久存在直到被显式修改。即使全集群重启它们也会存活下来并覆盖掉静态配置文件里的选项。

临时或永久配置需要在 JSON 体里分别指定:

PUT /_cluster/settings
{
    "persistent" : {
        "discovery.zen.minimum_master_nodes" : 2 (1)
    },
    "transient" : {
        "indices.store.throttle.max_bytes_per_sec" : "50mb" (2)
    }
}
  1. 这个永久设置会在全集群重启时存活下来。

  2. 这个临时设置会在第一次全集群重启后被移除。

可以动态更新的设置的完整清单,请阅读 online reference docs

日志记录

Elasticsearch 会输出很多日志,都放在 ES_HOME/logs 目录下。默认的日志记录等级是 INFO 。它提供了适度的信息,但是又设计好了不至于让你的日志太过庞大。

当调试问题的时候,特别是节点发现相关的问题(因为这个经常依赖于各式过于繁琐的网络配置),提高日志记录等级到 DEBUG 是很有帮助的。

可以 修改 logging.yml 文件然后重启你的节点——但是这样做即繁琐还会导致不必要的宕机时间。作为替代,你可以通过 cluster-settings API 更新日志记录级别,就像我们前面刚学过的那样。

要实现这个更新,选择你感兴趣的日志器,然后在前面补上 logger. 。对根日志器你可以用 logger._root 来表示。

让我们调高节点发现的日志记录级别:

PUT /_cluster/settings
{
    "transient" : {
        "logger.discovery" : "DEBUG"
    }
}

设置生效,Elasticsearch 将开始输出 discovery 模块的 DEBUG 级别的日志。

Tip
避免使用 TRACE 。这个级别非常的详细,详细到日志反而不再有用了。

慢日志

还有另一个日志叫 慢日志 。这个日志的目的是捕获那些超过指定时间阈值的查询和索引请求。这个日志用来追踪由用户产生的很慢的请求很有用。

默认情况,慢日志是不开启的。要开启它,需要定义具体动作(query,fetch 还是 index),你期望的事件记录等级( WARNDEBUG 等),以及时间阈值。

这是一个索引级别的设置,也就是说可以独立应用给单个索引:

PUT /my_index/_settings
{
    "index.search.slowlog.threshold.query.warn" : "10s", (1)
    "index.search.slowlog.threshold.fetch.debug": "500ms", (2)
    "index.indexing.slowlog.threshold.index.info": "5s" (3)
}
  1. 查询慢于 10 秒输出一个 WARN 日志。

  2. 获取慢于 500 毫秒输出一个 DEBUG 日志。

  3. 索引慢于 5 秒输出一个 INFO 日志。

你也可以在 elasticsearch.yml 文件里定义这些阈值。没有阈值设置的索引会自动继承在静态配置文件里配置的参数。

一旦阈值设置过了,你可以和其他日志器一样切换日志记录等级:

PUT /_cluster/settings
{
    "transient" : {
        "logger.index.search.slowlog" : "DEBUG", (1)
        "logger.index.indexing.slowlog" : "WARN" (2)
    }
}
  1. 设置搜索慢日志为 DEBUG 级别。

  2. 设置索引慢日志为 WARN 级别。

索引性能技巧

如果你是在一个索引负载很重的环境,比如索引的是基础设施日志,你可能愿意牺牲一些搜索性能换取更快的索引速率。在这些场景里,搜索常常是很少见的操作,而且一般是由你公司内部的人发起的。他们也愿意为一个搜索等上几秒钟,而不像普通消费者,要求一个搜索必须毫秒级返回。

基于这种特殊的场景,我们可以有几种权衡办法来提高你的索引性能。

这些技巧仅适用于 Elasticsearch 1.3 及以后的版本

本书是为最新几个版本的 Elasticsearch 写的,虽然大多数内容在更老的版本也也有效。

不过,本节提及的技巧, 针对 1.3 及以后版本。该版本后有不少性能提升和故障修复是直接影响到索引的。事实上,有些建议在老版本上反而会因为故障或性能缺陷而 降低 性能。

科学的测试性能

性能测试永远是复杂的,所以在你的方法里已经要尽可能的科学。随机摆弄旋钮以及写入开关可不是做性能调优的好办法。如果有太多种 可能 ,我们就无法判断到底哪一种有最好的 效果 。合理的测试方法如下:

  1. 在单个节点上,对单个分片,无副本的场景测试性能。

  2. 在 100% 默认配置的情况下记录性能结果,这样你就有了一个对比基线。

  3. 确保性能测试运行足够长的时间(30 分钟以上)这样你可以评估长期性能,而不是短期的峰值或延迟。一些事件(比如段合并,GC)不会立刻发生,所以性能概况会随着时间继续而改变的。

  4. 开始在基线上逐一修改默认值。严格测试它们,如果性能提升可以接受,保留这个配置项,开始下一项。

使用批量请求并调整其大小

显而易见的,优化性能应该使用批量请求。批量的大小则取决于你的数据、分析和集群配置,不过每次批量数据 5–15 MB 大是个不错的起始点。注意这里说的是物理字节数大小。文档计数对批量大小来说不是一个好指标。比如说,如果你每次批量索引 1000 个文档,记住下面的事实:

  • 1000 个 1 KB 大小的文档加起来是 1 MB 大。

  • 1000 个 100 KB 大小的文档加起来是 100 MB 大。

这可是完完全全不一样的批量大小了。批量请求需要在协调节点上加载进内存,所以批量请求的物理大小比文档计数重要得多。

从 5–15 MB 开始测试批量请求大小,缓慢增加这个数字,直到你看不到性能提升为止。然后开始增加你的批量写入的并发度(多线程等等办法)。

用 Marvel 以及诸如 iostattopps 等工具监控你的节点,观察资源什么时候达到瓶颈。如果你开始收到 EsRejectedExecutionException ,你的集群没办法再继续了:至少有一种资源到瓶颈了。或者减少并发数,或者提供更多的受限资源(比如从机械磁盘换成 SSD),或者添加更多节点。

Note

写数据的时候,要确保批量请求是轮询发往你的全部数据节点的。不要把所有请求都发给单个节点,因为这个节点会需要在处理的时候把所有批量请求都存在内存里。

存储

磁盘在现代服务器上通常都是瓶颈。Elasticsearch 重度使用磁盘,你的磁盘能处理的吞吐量越大,你的节点就越稳定。这里有一些优化磁盘 I/O 的技巧:

  • 使用 SSD。就像其他地方提过的,他们比机械磁盘优秀多了。

  • 使用 RAID 0。条带化 RAID 会提高磁盘 I/O,代价显然就是当一块硬盘故障时整个就故障了。不要使用镜像或者奇偶校验 RAID 因为副本已经提供了这个功能。

  • 另外,使用多块硬盘,并允许 Elasticsearch 通过多个 path.data 目录配置把数据条带化分配到它们上面。

  • 不要使用远程挂载的存储,比如 NFS 或者 SMB/CIFS。这个引入的延迟对性能来说完全是背道而驰的。

  • 如果你用的是 EC2,当心 EBS。即便是基于 SSD 的 EBS,通常也比本地实例的存储要慢。

段和合并

段合并的计算量庞大,而且还要吃掉大量磁盘 I/O。合并在后台定期操作,因为他们可能要很长时间才能完成,尤其是比较大的段。这个通常来说都没问题,因为大规模段合并的概率是很小的。

不过有时候合并会拖累写入速率。如果这个真的发生了,Elasticsearch 会自动限制索引请求到单个线程里。这个可以防止出现 段爆炸 问题,即数以百计的段在被合并之前就生成出来。如果 Elasticsearch 发现合并拖累索引了,它会会记录一个声明有 now throttling indexingINFO 级别信息。

Elasticsearch 默认设置在这块比较保守:不希望搜索性能被后台合并影响。不过有时候(尤其是 SSD,或者日志场景)限流阈值太低了。

默认值是 20 MB/s,对机械磁盘应该是个不错的设置。如果你用的是 SSD,可以考虑提高到 100–200 MB/s。测试验证对你的系统哪个值合适:

PUT /_cluster/settings
{
    "persistent" : {
        "indices.store.throttle.max_bytes_per_sec" : "100mb"
    }
}

如果你在做批量导入,完全不在意搜索,你可以彻底关掉合并限流。这样让你的索引速度跑到你磁盘允许的极限:

PUT /_cluster/settings
{
    "transient" : {
        "indices.store.throttle.type" : "none" (1)
    }
}
  1. 设置限流类型为 none 彻底关闭合并限流。等你完成了导入,记得改回 merge 重新打开限流。

如果你使用的是机械磁盘而非 SSD,你需要添加下面这个配置到你的 elasticsearch.yml 里:

index.merge.scheduler.max_thread_count: 1

机械磁盘在并发 I/O 支持方面比较差,所以我们需要降低每个索引并发访问磁盘的线程数。这个设置允许 max_thread_count + 2 个线程同时进行磁盘操作,也就是设置为 1 允许三个线程。

对于 SSD,你可以忽略这个设置,默认是 Math.min(3, Runtime.getRuntime().availableProcessors() / 2) ,对 SSD 来说运行的很好。

最后,你可以增加 index.translog.flush_threshold_size 设置,从默认的 512 MB 到更大一些的值,比如 1 GB。这可以在一次清空触发的时候在事务日志里积累出更大的段。而通过构建更大的段,清空的频率变低,大段合并的频率也变低。这一切合起来导致更少的磁盘 I/O 开销和更好的索引速率。当然,你会需要对应量级的 heap 内存用以积累更大的缓冲空间,调整这个设置的时候请记住这点。

其他

最后,还有一些其他值得考虑的东西需要记住:

  • 如果你的搜索结果不需要近实时的准确度,考虑把每个索引的 index.refresh_interval改到 30s 。如果你是在做大批量导入,导入期间你可以通过设置这个值为 -1 关掉刷新。别忘记在完工的时候重新开启它。

  • 如果你在做大批量导入,考虑通过设置 index.number_of_replicas: 0关闭副本。文档在复制的时候,整个文档内容都被发往副本节点,然后逐字的把索引过程重复一遍。这意味着每个副本也会执行分析、索引以及可能的合并过程。

    相反,如果你的索引是零副本,然后在写入完成后再开启副本,恢复过程本质上只是一个字节到字节的网络传输。相比重复索引过程,这个算是相当高效的了。

  • 如果你没有给每个文档自带 ID,使用 Elasticsearch 的自动 ID 功能。这个为避免版本查找做了优化,因为自动生成的 ID 是唯一的。

  • 如果你在使用自己的 ID,尝试使用一种 Lucene 友好的 ID。包括零填充序列 ID、UUID-1 和纳秒;这些 ID 都是有一致的,压缩良好的序列模式。相反的,像 UUID-4 这样的 ID,本质上是随机的,压缩比很低,会明显拖慢 Lucene。

推迟分片分配

正如我们在 水平扩容 讨论过, Elasticsearch 将自动在可用节点间进行分片均衡,包括新节点的加入和现有节点的离线。

理论上来说,这个是理想的行为,我们想要提拔副本分片来尽快恢复丢失的主分片。 我们同时也希望保证资源在整个集群的均衡,用以避免热点。

然而,在实践中,立即的再均衡所造成的问题会比其解决的更多。举例来说,考虑到以下情形:

  1. Node(节点) 19 在网络中失联了(某个家伙踢到了电源线)

  2. Master 立即注意到了这个节点的离线,它决定在集群内提拔其他拥有 Node 19 上面的主分片对应的副本分片为主分片

  3. 在副本被提拔为主分片以后,master 节点开始执行恢复操作来重建缺失的副本。集群中的节点之间互相拷贝分片数据,网卡压力剧增,集群状态尝试变绿。

  4. 由于目前集群处于非平衡状态,这个过程还有可能会触发小规模的分片移动。其他不相关的分片将在节点间迁移来达到一个最佳的平衡状态

与此同时,那个踢到电源线的倒霉管理员,把服务器插好电源线进行了重启,现在节点 Node 19 又重新加入到了集群。不幸的是,这个节点被告知当前的数据已经没有用了, 数据已经在其他节点上重新分配了。所以 Node 19 把本地的数据进行删除,然后重新开始恢复集群的其他分片(然后这又导致了一个新的再平衡)

如果这一切听起来是不必要的且开销极大,那就对了。是的,不过前提是你知道这个节点会很快回来。如果节点 Node 19 真的丢了,上面的流程确实正是我们想要发生的。

为了解决这种瞬时中断的问题,Elasticsearch 可以推迟分片的分配。这可以让你的集群在重新分配之前有时间去检测这个节点是否会再次重新加入。

修改默认延时

默认情况,集群会等待一分钟来查看节点是否会重新加入,如果这个节点在此期间重新加入,重新加入的节点会保持其现有的分片数据,不会触发新的分片分配。

通过修改参数 delayed_timeout ,默认等待时间可以全局设置也可以在索引级别进行修改:

PUT /_all/_settings (1)
{
  "settings": {
    "index.unassigned.node_left.delayed_timeout": "5m" (2)
  }
}
  1. 通过使用 _all 索引名,我们可以为集群里面的所有的索引使用这个参数

  2. 默认时间被修改成了 5 分钟

这个配置是动态的,可以在运行时进行修改。如果你希望分片立即分配而不想等待,你可以设置参数: delayed_timeout: 0.

Note
延迟分配不会阻止副本被提拔为主分片。集群还是会进行必要的提拔来让集群回到 yellow 状态。缺失副本的重建是唯一被延迟的过程。

自动取消分片迁移

如果节点在超时之后再回来,且集群还没有完成分片的移动,会发生什么事情呢?在这种情形下, Elasticsearch 会检查该机器磁盘上的分片数据和当前集群中的活跃主分片的数据是不是一样 — 如果两者匹配, 说明没有进来新的文档,包括删除和修改 — 那么 master 将会取消正在进行的再平衡并恢复该机器磁盘上的数据。

之所以这样做是因为本地磁盘的恢复永远要比网络间传输要快,并且我们保证了他们的分片数据是一样的,这个过程可以说是双赢。

如果分片已经产生了分歧(比如:节点离线之后又索引了新的文档),那么恢复进程会继续按照正常流程进行。重新加入的节点会删除本地的、过时的数据,然后重新获取一份新的。

滚动重启

总有一天你会需要做一次集群的滚动重启——保持集群在线和可操作,但是逐一把节点下线。

常见的原因:Elasticsearch 版本升级,或者服务器自身的一些维护操作(比如操作系统升级或者硬件相关)。不管哪种情况,都要有一种特别的方法来完成一次滚动重启。

正常情况下,Elasticsearch 希望你的数据被完全的复制和均衡的分布。如果你手动关闭了一个节点,集群会立刻发现节点的丢失并开始再平衡。如果节点的维护是短期工作的话,这一点就很烦人了,因为大型分片的再平衡需要花费相当的时间(想想尝试复制 1TB 的数据——即便在高速网络上也是不一般的事情了)。

我们需要的是,告诉 Elasticsearch 推迟再平衡,因为对外部因子影响下的集群状态,我们自己更了解。操作流程如下:

  1. 可能的话,停止索引新的数据。虽然不是每次都能真的做到,但是这一步可以帮助提高恢复速度。

  2. 禁止分片分配。这一步阻止 Elasticsearch 再平衡缺失的分片,直到你告诉它可以进行了。如果你知道维护窗口会很短,这个主意棒极了。你可以像下面这样禁止分配:

    PUT /_cluster/settings
    {
        "transient" : {
            "cluster.routing.allocation.enable" : "none"
        }
    }
  3. 关闭单个节点。

  4. 执行维护/升级。

  5. 重启节点,然后确认它加入到集群了。

  6. 用如下命令重启分片分配:

    PUT /_cluster/settings
    {
        "transient" : {
            "cluster.routing.allocation.enable" : "all"
        }
    }

    分片再平衡会花一些时间。一直等到集群变成 绿色 状态后再继续。

  7. 重复第 2 到 6 步操作剩余节点。

  8. 到这步你可以安全的恢复索引了(如果你之前停止了的话),不过等待集群完全均衡后再恢复索引,也会有助于提高处理速度。

备份你的集群

使用无论哪个存储数据的软件,定期备份你的数据都是很重要的。Elasticsearch 副本提供了高可靠性;它们让你可以容忍零星的节点丢失而不会中断服务。

但是,副本并不提供对灾难性故障的保护。对这种情况,你需要的是对集群真正的备份——在某些东西确实出问题的时候有一个完整的拷贝。

要备份你的集群,你可以使用 snapshot API。这个会拿到你集群里当前的状态和数据然后保存到一个共享仓库里。这个备份过程是"智能"的。你的第一个快照会是一个数据的完整拷贝,但是所有后续的快照会保留的是已存快照和新数据之间的差异。随着你不时的对数据进行快照,备份也在增量的添加和删除。这意味着后续备份会相当快速,因为它们只传输很小的数据量。

要使用这个功能,你必须首先创建一个保存数据的仓库。有多个仓库类型可以供你选择:

  • 共享文件系统,比如 NAS

  • Amazon S3

  • HDFS (Hadoop 分布式文件系统)

  • Azure Cloud

创建仓库

让我部署一个共享文件系统仓库:

PUT _snapshot/my_backup (1)
{
    "type": "fs", (2)
    "settings": {
        "location": "/mount/backups/my_backup" (3)
    }
}
  1. 给我们的仓库取一个名字,在本例它叫 my_backup

  2. 我们指定仓库的类型应该是一个共享文件系统。

  3. 最后,我们提供一个已挂载的设备作为目的地址。

注意:共享文件系统路径必须确保集群所有节点都可以访问到。

这步会在挂载点创建仓库和所需的元数据。还有一些其他的配置你可能想要配置的,这些取决于你的节点、网络的性能状况和仓库位置:

max_snapshot_bytes_per_sec

当快照数据进入仓库时,这个参数控制这个过程的限流情况。默认是每秒 20mb

max_restore_bytes_per_sec

当从仓库恢复数据时,这个参数控制什么时候恢复过程会被限流以保障你的网络不会被占满。默认是每秒 20mb

假设我们有一个非常快的网络,而且对额外的流量也很 OK,那我们可以增加这些默认值:

POST _snapshot/my_backup/ (1)
{
    "type": "fs",
    "settings": {
        "location": "/mount/backups/my_backup",
        "max_snapshot_bytes_per_sec" : "50mb", (2)
        "max_restore_bytes_per_sec" : "50mb"
    }
}
  1. 注意我们用的是 POST 而不是 PUT 。这会更新已有仓库的设置。

  2. 然后添加我们的新设置。

快照所有打开的索引

一个仓库可以包含多个快照。每个快照跟一系列索引相关(比如所有索引,一部分索引,或者单个索引)。当创建快照的时候,你指定你感兴趣的索引然后给快照取一个唯一的名字。

让我们从最基础的快照命令开始:

PUT _snapshot/my_backup/snapshot_1

这个会备份所有打开的索引到 my_backup 仓库下一个命名为 snapshot_1 的快照里。这个调用会立刻返回,然后快照会在后台运行。

Tip

通常你会希望你的快照作为后台进程运行,不过有时候你会希望在你的脚本中一直等待到完成。这可以通过添加一个 wait_for_completion 标记实现:

PUT _snapshot/my_backup/snapshot_1?wait_for_completion=true

这个会阻塞调用直到快照完成。注意大型快照会花很长时间才返回。

快照指定索引

默认行为是备份所有打开的索引。不过如果你在用 Marvel,你不是真的想要把所有诊断相关的 .marvel 索引也备份起来。可能你就压根没那么大空间备份所有数据。

这种情况下,你可以在快照你的集群的时候指定备份哪些索引:

PUT _snapshot/my_backup/snapshot_2
{
    "indices": "index_1,index_2"
}

这个快照命令现在只会备份 index1index2 了。

列出快照相关的信息

一旦你开始在你的仓库里积攒起快照了,你可能就慢慢忘记里面各自的细节了——特别是快照按照时间划分命名的时候(比如, backup_2014_10_28 )。

要获得单个快照的信息,直接对仓库和快照名发起一个 GET 请求:

GET _snapshot/my_backup/snapshot_2

这个会返回一个小响应,包括快照相关的各种信息:

{
   "snapshots": [
      {
         "snapshot": "snapshot_1",
         "indices": [
            ".marvel_2014_28_10",
            "index1",
            "index2"
         ],
         "state": "SUCCESS",
         "start_time": "2014-09-02T13:01:43.115Z",
         "start_time_in_millis": 1409662903115,
         "end_time": "2014-09-02T13:01:43.439Z",
         "end_time_in_millis": 1409662903439,
         "duration_in_millis": 324,
         "failures": [],
         "shards": {
            "total": 10,
            "failed": 0,
            "successful": 10
         }
      }
   ]
}

要获取一个仓库中所有快照的完整列表,使用 _all 占位符替换掉具体的快照名称:

GET _snapshot/my_backup/_all

删除快照

最后,我们需要一个命令来删除所有不再有用的旧快照。这只要对仓库/快照名称发一个简单的 DELETE HTTP 调用:

DELETE _snapshot/my_backup/snapshot_2

用 API 删除快照很重要,而不能用其他机制(比如手动删除,或者用 S3 上的自动清除工具)。因为快照是增量的,有可能很多快照依赖于过去的段。delete API 知道哪些数据还在被更多近期快照使用,然后会只删除不再被使用的段。

但是,如果你做了一次人工文件删除,你将会面临备份严重损坏的风险,因为你在删除的是可能还在使用中的数据。

监控快照进度

wait_for_completion 标记提供了一个监控的基础形式,但哪怕只是对一个中等规模的集群做快照恢复的时候,它都真的不够用。

另外两个 API 会给你有关快照状态更详细的信息。首先你可以给快照 ID 执行一个 GET,就像我们之前获取一个特定快照的信息时做的那样:

GET _snapshot/my_backup/snapshot_3

如果你调用这个命令的时候快照还在进行中,你会看到它什么时候开始,运行了多久等等信息。不过要注意,这个 API 用的是快照机制相同的线程池。如果你在快照非常大的分片,状态更新的间隔会很大,因为 API 在竞争相同的线程池资源。

更好的方案是拽取 _status API 数据:

GET _snapshot/my_backup/snapshot_3/_status

_status API 立刻返回,然后给出详细的多的统计值输出:

{
   "snapshots": [
      {
         "snapshot": "snapshot_3",
         "repository": "my_backup",
         "state": "IN_PROGRESS", (1)
         "shards_stats": {
            "initializing": 0,
            "started": 1, (2)
            "finalizing": 0,
            "done": 4,
            "failed": 0,
            "total": 5
         },
         "stats": {
            "number_of_files": 5,
            "processed_files": 5,
            "total_size_in_bytes": 1792,
            "processed_size_in_bytes": 1792,
            "start_time_in_millis": 1409663054859,
            "time_in_millis": 64
         },
         "indices": {
            "index_3": {
               "shards_stats": {
                  "initializing": 0,
                  "started": 0,
                  "finalizing": 0,
                  "done": 5,
                  "failed": 0,
                  "total": 5
               },
               "stats": {
                  "number_of_files": 5,
                  "processed_files": 5,
                  "total_size_in_bytes": 1792,
                  "processed_size_in_bytes": 1792,
                  "start_time_in_millis": 1409663054859,
                  "time_in_millis": 64
               },
               "shards": {
                  "0": {
                     "stage": "DONE",
                     "stats": {
                        "number_of_files": 1,
                        "processed_files": 1,
                        "total_size_in_bytes": 514,
                        "processed_size_in_bytes": 514,
                        "start_time_in_millis": 1409663054862,
                        "time_in_millis": 22
                     }
                  },
                  ...
  1. 一个正在运行的快照会显示 IN_PROGRESS 作为状态。

  2. 这个特定快照有一个分片还在传输(另外四个已经完成)。

响应包括快照的总体状况,但也包括下钻到每个索引和每个分片的统计值。这个给你展示了有关快照进展的非常详细的视图。分片可以在不同的完成状态:

INITIALIZING

分片在检查集群状态看看自己是否可以被快照。这个一般是非常快的。

STARTED

数据正在被传输到仓库。

FINALIZING

数据传输完成;分片现在在发送快照元数据。

DONE

快照完成!

FAILED

快照处理的时候碰到了错误,这个分片/索引/快照不可能完成了。检查你的日志获取更多信息。

取消一个快照

最后,你可能想取消一个快照或恢复。因为它们是长期运行的进程,执行操作的时候一个笔误或者过错就会花很长时间来解决——而且同时还会耗尽有价值的资源。

要取消一个快照,在他进行中的时候简单的删除快照就可以:

DELETE _snapshot/my_backup/snapshot_3

这个会中断快照进程。然后删除仓库里进行到一半的快照。

从快照恢复

一旦你备份过了数据,恢复它就简单了:只要在你希望恢复回集群的快照 ID后面加上 _restore 即可:

POST _snapshot/my_backup/snapshot_1/_restore

默认行为是把这个快照里存有的所有索引都恢复。如果 snapshot_1 包括五个索引,这五个都会被恢复到我们集群里。和 snapshot API 一样,我们也可以选择希望恢复具体哪个索引。

还有附加的选项用来重命名索引。这个选项允许你通过模式匹配索引名称,然后通过恢复进程提供一个新名称。如果你想在不替换现有数据的前提下,恢复老数据来验证内容,或者做其他处理,这个选项很有用。让我们从快照里恢复单个索引并提供一个替换的名称:

POST /_snapshot/my_backup/snapshot_1/_restore
{
    "indices": "index_1", (1)
    "rename_pattern": "index_(.+)", (2)
    "rename_replacement": "restored_index_$1" (3)
}
  1. 只恢复 index_1 索引,忽略快照中存在的其余索引。

  2. 查找所提供的模式能匹配上的正在恢复的索引。

  3. 然后把它们重命名成替代的模式。

这个会恢复 index_1 到你及群里,但是重命名成了 restored_index_1

Tip

和快照类似, restore 命令也会立刻返回,恢复进程会在后台进行。如果你更希望你的 HTTP 调用阻塞直到恢复完成,添加 wait_for_completion 标记:

POST _snapshot/my_backup/snapshot_1/_restore?wait_for_completion=true

监控恢复操作

从仓库恢复数据借鉴了 Elasticsearch 里已有的现行恢复机制。在内部实现上,从仓库恢复分片和从另一个节点恢复是等价的。

如果你想监控恢复的进度,你可以使用 recovery API。这是一个通用目的的 API,用来展示你集群中移动着的分片状态。

这个 API 可以为你在恢复的指定索引单独调用:

GET restored_index_3/_recovery

或者查看你集群里所有索引,可能包括跟你的恢复进程无关的其他分片移动:

GET /_recovery/

输出会跟这个类似(注意,根据你集群的活跃度,输出可能会变得非常啰嗦!):

{
  "restored_index_3" : {
    "shards" : [ {
      "id" : 0,
      "type" : "snapshot", (1)
      "stage" : "index",
      "primary" : true,
      "start_time" : "2014-02-24T12:15:59.716",
      "stop_time" : 0,
      "total_time_in_millis" : 175576,
      "source" : { (2)
        "repository" : "my_backup",
        "snapshot" : "snapshot_3",
        "index" : "restored_index_3"
      },
      "target" : {
        "id" : "ryqJ5lO5S4-lSFbGntkEkg",
        "hostname" : "my.fqdn",
        "ip" : "10.0.1.7",
        "name" : "my_es_node"
      },
      "index" : {
        "files" : {
          "total" : 73,
          "reused" : 0,
          "recovered" : 69,
          "percent" : "94.5%" (3)
        },
        "bytes" : {
          "total" : 79063092,
          "reused" : 0,
          "recovered" : 68891939,
          "percent" : "87.1%"
        },
        "total_time_in_millis" : 0
      },
      "translog" : {
        "recovered" : 0,
        "total_time_in_millis" : 0
      },
      "start" : {
        "check_index_time" : 0,
        "total_time_in_millis" : 0
      }
    } ]
  }
}
  1. type 字段告诉你恢复的本质;这个分片是在从一个快照恢复。

  2. source 哈希描述了作为恢复来源的特定快照和仓库。

  3. percent 字段让你对恢复的状态有个概念。这个特定分片目前已经恢复了 94% 的文件;它就快完成了。

输出会列出所有目前正在经历恢复的索引,然后列出这些索引里的所有分片。每个分片里会有启动/停止时间、持续时间、恢复百分比、传输字节数等统计值。

取消一个恢复

要取消一个恢复,你需要删除正在恢复的索引。因为恢复进程其实就是分片恢复,发送一个 删除索引 API 修改集群状态,就可以停止恢复进程。比如:

DELETE /restored_index_3

如果 restored_index_3 正在恢复中,这个删除命令会停止恢复,同时删除所有已经恢复到集群里的数据。

集群是活着的、呼吸着的生命

一旦你的集群投入生产,你会发现他就开始了他自己的一生。Elasticsearch 努力工作来保证集群自给自足而且 真就在工作 。不过一个集群也还要有日常照料和投喂,比如日常备份和升级。

Elasticsearch 以非常快的速度发布新版本,进行错误修复和性能增强。保持你的集群采用最新版总是一个好主意。类似的,Lucene 持续在发现 JVM 自身的新的和令人惊讶的错误,这意味着你需要尽量保持你的 JVM 是最新的。

这意味着最好是拥有一个标准化的、日常的方案来操作你集群的滚动重启和升级。升级应该是一个日常程序,而不是一个需要好多个小时的精细规划下的年度『惨剧』。

类似的,拥有一个灾备计划是很重要的。请对你的集群做频繁的快照——而且通过执行真实恢复的方式定期 测试 这些快照!有些组织做日常备份却从不测试他们的恢复机制,这简直太常见了。通常你会在第一次演练真实恢复的时候发现明显的缺陷(比如用户不知道应该挂载哪个磁盘)。比起在凌晨 3 点真的发生危机的时候,在日常测试中暴露出这些问题总是更好的。


书籍推荐