教你如何在 elasticsearch 中重建索引

18,600 阅读4分钟

序言

image

Elasticsearch 是一个实时的分布式搜索分析引擎。Teambition 使用 Elastisearch 作为搜索引擎,为用户提供搜索服务,当我们决定存储某种数据时,我们需要使用PUT /teambition创建索引,在创建索引的时候需要将数据结构完整确定下来,于此同时索引的设定和很多固定配置将用不能改变。当需要改变数据结构时,就需要重新建立索引,为此,Elastic团队提供了很多辅助工具帮助开发人员进行重建索引。

决定是否需要重建?

重建时相当痛苦的,如果没有很好的基础,服务可能中断,当数据量非常大时,重建恢复时间可能很长,甚至在重建过程中出错等等。所以,没有万不得已的情况下还是尽量避免重建索引。Teambition 重建索引的理由略,因为这并不重要。

使用正确的工具

kibana 是 Elasticsearch 的最佳拍档,从 ES 5.0 开始,Kibana 强大的功能能够替代几乎所有旧时代 ES 1.x 和 ES 2.x 的插件。关键是人家还是免费的!

先决条件

正在运行的服务必须使用别名(alias)来访问索引(index),原因很简单,搜索服务最终要使用重建的索引,原始的索引将被删除。如果你的服务正在直接使用索引名,在重建前创建别名,更新服务。

POST /_aliases
{
  "actions": [
    {
      "add": {
        "index": "teambition", // 原有索引
        "alias": "teambition_latest" // 服务的别名
      }
    }
  ]
}



先决条件2

记得查看 Elasticsearch 的 Disk Usage,如果不够,请先申请好足够的空间。

创建新索引

和创建普通索引一样创建新索引。这里值得一提的时,当数据量很大的时候,需要设置刷新时间间隔,在此期间写入的数据不能搜到,从而提高重建速度:refresh_intervals = -1, number_of_replicas = 0。实践告诉我,大概会提高100% ~ 400%的提升。

PUT /teambition_20180328
{
  "settings": {...},
  "mapping": {...}
}

记录同步数据的偏移值(offset)

Teambition使用Kafka把MongoDB中的数据导入到Elasticsearch中,如果没有kafka,亦可读取oplog的数据写入Elasticsearch。无论使用哪种同步数据的方式,都需要记录同步数据的offset。重建索引可能非常耗时,在这段时间内,同步进程仍然在向旧索引更新数据,此时重建索引是无法更新这些新数据的。这里记录的方法就不多说了,Teambition 使用 kafka-admin 的API记录 offset。

开始重建索引

使用 Elasticsearch 团队提供的 reindex api 就可以将数据 copy 到新索引中。这里几条路可以选:

  1. 当只是改变 mapping 数据结构时,可以仅仅使用 reindex api 即可。例如:删除字段,更新字段分词方式等。
  2. 当需要写入新的字段,新的字段是由老的字段计算得到时,可以使用script参数。例如,计算某条数据某字段的总和。script 有很多坑,当 script 出错时,reindex 跑了很久之后失败,即使将数据恢复,也需要重新跑 reindex。
  3. 当含有很复杂的逻辑时,额,还是自己写程序吧。

调用 reindex 接口,接口将会在 reindex 结束后返回,而接口返回超时只有30秒,如果 reindex 时间过长,建议加上wait_for_completion=false的参数条件,这样 reindex 将直接返回taskId

POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "teambition"
  },
  "dest": {
    "index": "teambition_20180328"
  },
  "script": {...}
}

重建索引中

重建索引非常耗时,喝杯咖啡歇一会儿吧(顺便去打个球,睡个觉,旅个游)。

在没有设置 refresh_intervalsnumber_of_replicas 时,reindex 的速度在 500~1000 doc/sec, 如果包含 script 时可能会更低。设置之后,可以到 4000~8000 doc/sec。 Teambition 70M Documents 大概耗时4小时。

可以使用GET _tasks/{taskID}可以看到重建进程,其中包含耗时,剩余doc数量等信息。

如果发现错误,可以使用PUT _tasks/{taskID}/cancel接口放弃任务,从头再来。

恢复同步数据

重建索引结束后,别忘了在setting中的将number_of_replicasrefresh_intervals设为原有值. 启动新的同步索引的进程(从记录 offset 开始同步)

建立新的alias

需要在同时绑定建立的新索引以及解绑旧索引,语句如下:

POST _aliases
{
  "actions": [{"add": {
    "index": "teambition_20180328",
    "alias": "teambition_latest"
  }}, {"remove": {
    "index": "teambition",
    "alias": "teambition_latest"
  }}]
}

删掉index

删除旧的 index,释放磁盘空间;停止原有同步进程。

DELETE teambition

总结

修改索引真的是一件费时费力的工作,特别是如果发生了错误,整个人都不好了。所以还是在创建索引的时候尽量想好能否满足需求,当然大家都知道这几乎是不可能的,因为存在着万恶的产品经理。

这里还有一个很重要的内容没有详细介绍就是同步进程,前面提到同步进程是将 MongoDB 的数据同步到 ES 中去的程序,这个程序同时还需要有能力暂停同步,重复同步的等能力。