目录

问题:

在使用Elastic Search 过程中,可能经常会碰到需要修改 mapping的情况,如果是新增字段,还算比较简单,只需要在原来的mapping基础之上再新增字段即可。 但是如果碰到要修改原来字段的属性,就会比较棘手了, 因为ES并不支持在原来的mapping基础上修改字段的属性。 这种情况能做的选择就是重新建一份索引。

那么如何重建索引呢。 最直观的就是直接从数据源从新导一份数据进入ES里边,但是这个是一个费时费力的操作。 可喜的是, ES给我们提供了一个 Reindex 的API,能够直接从原来的index重建一份新的索引。由于ES里边本来就保存了一份源数据,从这份源数据重新一份索引相对来说是一个比较快速的选择。

但是,在使用ES的Reindex API的时候,还有一个问题必须要考虑。 官方文档中说明,Reindex的时候它是取了一份老的index的快照,这样如果在停服的情况下没什么问题,但是如果在做重新索引的过程中, 仍旧有数据一直在更新,如何处理这部分更新的增量数据。

当然,办法是有的,就是需要利用好ES提供的Reindex API的各种参数。

要做到不停服重建索引的前提,就是我们使用的索引已经建立了别名。比如我们创建的索引叫 index_v1 ,现在我们要重建新的索引 index_v2。 如果在没使用别名的情况下,我们的逻辑代码中直接访问了 index_v1, 那么在重建索引之后,我们还必须更改代码逻辑,并且上线, 这是一个费时并且有延迟的过程。

如果我们给老的 index_v1 建立了一个别名 index_alias, 程序中也是直接访问的这个别名。 在新建索引完成之后 ,就只需要将这个别名指向新的索引 index_v2 即可, 代码层面无需做任何的修改,而且是即时生效的。 

解决办法:

接下去就需要讨论如何做到不停服的重建索引。 我们可以举例说明。仿照官方文档的例子, 假设我们需要将老的索引 twitter 重建索引导 new_twitter。

  • 第一步,当然是新建新的索引, 将mapping字段都设置成想要的格式,  
  • 第二步,利用ES的API重建新的索引, 示例如下
curl -H "Content-Type:application/json" -X POST 'http://127.0.0.1:9200/_reindex' -d '{
  "conflicts": "proceed",
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
    "version_type": "external"
  }
}'

可以比较一下最简单的重建索引的方式:

curl -H "Content-Type:application/json" -X POST 'http://127.0.0.1:9200/_reindex' -d '{
  "source": {
    "index": "twitter"
  },
  "dest": {
    "index": "new_twitter",
  }
}'

主要的区别就在这些参数上边,我们一项项说明。 conflicts设置成proceed表示在重建索引的过程中,如果碰到源索引和目标索引同时存在相同的数据的时候,即发生数据冲突的时候,忽略冲突继续向下执行。 version_type设置成external表示从源索引拷贝数据到目标索引的过程中,会把源索引中的版本号version带上;如果发现目标索引数据version版本好比源索引的要小,就会执行更新的过程; ES默认的version_type为internal,会直接覆盖目标索引的数据 当然, 在重建索引的过程,我们还可以用query指定过滤条件,只将一部分数据更新到目标索引

重建索引之后,我们可以修改源索引和目标索引的别名,将原来的别名指向新的索引,这样所有更新的操作就会从新的索引出去。

接下去只剩最后一个问题,如果将重建索引的这段时间内,旧索引的一些更新操作增量同步到新的索引。其实很好解决,只需要我们在重新做一个重建索引的操作。 由于我们设置了version_type=external, 在碰到没有更新的索引的时候,目标索引并不会做更新。如果碰到了有更新的索引,那么他在源索引的版本号version是比目标索引里边的大的, 这样就会对目标索引的数据做一个更新的操作

至此,我们就解决了如何不停服的进行数据索引重建的过程。重新操作之后的结果,会返回更新了多少个文档:

{
    "took":88,
    "timed_out":false,
    "total":313,
    "updated":1,
    "created":0,
    "deleted":0,
    "batches":1,
    "version_conflicts":312,
    "noops":0,
    "retries":{
        "bulk":0,
        "search":0
    },
    "throttled_millis":0,
    "requests_per_second":-1,
    "throttled_until_millis":0,
    "failures":[
 
    ]
}

当然, 如果在重建索引的过程, 发现了问题,我们也可以终止重建索引的过程。我们可以通过

GET _tasks?detailed=true&actions=*reindex

查询索引重建的进度并活动重建过程的任务id,通过这个id我们能对他进行取消任务的操作

POST _tasks/task_id:1/_cancel