拆分索引 API

编辑

将现有索引拆分为具有更多主分片的新索引。

resp = client.indices.split(
    index="my-index-000001",
    target="split-my-index-000001",
    settings={
        "index.number_of_shards": 2
    },
)
print(resp)
response = client.indices.split(
  index: 'my-index-000001',
  target: 'split-my-index-000001',
  body: {
    settings: {
      'index.number_of_shards' => 2
    }
  }
)
puts response
const response = await client.indices.split({
  index: "my-index-000001",
  target: "split-my-index-000001",
  settings: {
    "index.number_of_shards": 2,
  },
});
console.log(response);
POST /my-index-000001/_split/split-my-index-000001
{
  "settings": {
    "index.number_of_shards": 2
  }
}

请求

编辑

POST /<索引>/_split/<目标索引>

PUT /<索引>/_split/<目标索引>

先决条件

编辑
  • 如果启用了 Elasticsearch 安全功能,则你必须拥有该索引的 manage 索引权限
  • 在拆分索引之前

    • 索引必须是只读的。
    • 集群健康 状态必须为绿色。

你可以使用 添加索引块 API 通过以下请求将索引设置为只读:

resp = client.indices.add_block(
    index="my_source_index",
    block="write",
)
print(resp)
response = client.indices.add_block(
  index: 'my_source_index',
  block: 'write'
)
puts response
const response = await client.indices.addBlock({
  index: "my_source_index",
  block: "write",
});
console.log(response);
PUT /my_source_index/_block/write

数据流上的当前写入索引不能拆分。为了拆分当前的写入索引,必须首先翻转数据流,以便创建新的写入索引,然后才能拆分之前的写入索引。

描述

编辑

拆分索引 API 允许你将现有索引拆分为一个新索引,其中原始的每个主分片在新索引中被拆分为两个或多个主分片。

索引可以拆分的次数(以及每个原始分片可以拆分成的分片数)由 index.number_of_routing_shards 设置确定。路由分片数指定用于在分片之间使用一致哈希来分配文档的哈希空间。例如,一个具有 5 个分片且 number_of_routing_shards 设置为 305 x 2 x 3)的索引,可以按 23 的因子拆分。换句话说,它可以按如下方式拆分

  • 51030 (先按 2 拆分,再按 3 拆分)
  • 51530 (先按 3 拆分,再按 2 拆分)
  • 530 (按 6 拆分)

index.number_of_routing_shards 是一个静态索引设置。你只能在创建索引时或在关闭索引时设置 index.number_of_routing_shards

索引创建示例

以下创建索引 API 使用 30index.number_of_routing_shards 设置创建 my-index-000001 索引。

resp = client.indices.create(
    index="my-index-000001",
    settings={
        "index": {
            "number_of_routing_shards": 30
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    settings: {
      index: {
        number_of_routing_shards: 30
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index-000001",
  settings: {
    index: {
      number_of_routing_shards: 30,
    },
  },
});
console.log(response);
PUT /my-index-000001
{
  "settings": {
    "index": {
      "number_of_routing_shards": 30
    }
  }
}

index.number_of_routing_shards 设置的默认值取决于原始索引中的主分片数。默认值旨在允许你按 2 的因子拆分,最多可以拆分到 1024 个分片。但是,必须考虑原始主分片数。例如,使用 5 个主分片创建的索引可以拆分为 10、20、40、80、160、320 或最多 640 个分片(通过单个拆分操作或多个拆分操作)。

如果原始索引包含一个主分片(或多分片索引已收缩为单个主分片),则可以将索引拆分为任意大于 1 的分片数。默认路由分片数的属性将应用于新拆分的索引。

拆分工作原理

编辑

拆分操作

  1. 创建一个新的目标索引,其定义与源索引相同,但具有更多的主分片。
  2. 将来自源索引的段硬链接到目标索引。(如果文件系统不支持硬链接,则所有段都将复制到新索引中,这是一个耗时得多的过程。)
  3. 在创建低级文件后再次哈希所有文档,以删除属于不同分片的文档。
  4. 恢复目标索引,就像它是一个刚刚重新打开的已关闭索引一样。

为什么 Elasticsearch 不支持增量重新分片?

编辑

N 个分片到 N+1 个分片,即增量重新分片,确实是许多键值存储支持的功能。添加一个新分片并将新数据推送到这个新分片并不是一个选项:这很可能成为索引瓶颈,并且确定文档属于哪个分片(给定其 _id),这对于 get、delete 和 update 请求是必要的,将会变得非常复杂。这意味着我们需要使用不同的哈希方案来重新平衡现有数据。

键值存储有效执行此操作的最常见方式是使用一致哈希。当将分片数从 N 增加到 N+1 时,一致哈希只需要重新定位 1/N-th 的键。然而,Elasticsearch 的存储单元(分片)是 Lucene 索引。由于它们面向搜索的数据结构,因此,获取 Lucene 索引的很大一部分(即使只有 5% 的文档)、删除它们并在另一个分片上建立索引的成本通常比键值存储高得多。当按照上面部分描述的乘法因子增加分片数时,此成本会保持在合理范围内:这允许 Elasticsearch 在本地执行拆分,从而允许在索引级别执行拆分,而不是重新索引需要移动的文档,以及使用硬链接进行高效的文件复制。

在仅附加数据的情况下,可以通过创建一个新索引并将新数据推送到该索引,同时添加一个涵盖旧索引和新索引的别名来进行读取操作,从而获得更大的灵活性。假设旧索引和新索引分别具有 MN 个分片,则这与搜索具有 M+N 个分片的索引相比没有开销。

拆分索引

编辑

要将 my_source_index 拆分为名为 my_target_index 的新索引,请发出以下请求

resp = client.indices.split(
    index="my_source_index",
    target="my_target_index",
    settings={
        "index.number_of_shards": 2
    },
)
print(resp)
response = client.indices.split(
  index: 'my_source_index',
  target: 'my_target_index',
  body: {
    settings: {
      'index.number_of_shards' => 2
    }
  }
)
puts response
const response = await client.indices.split({
  index: "my_source_index",
  target: "my_target_index",
  settings: {
    "index.number_of_shards": 2,
  },
});
console.log(response);
POST /my_source_index/_split/my_target_index
{
  "settings": {
    "index.number_of_shards": 2
  }
}

一旦目标索引被添加到集群状态,上述请求将立即返回,它不会等待拆分操作开始。

仅当索引满足以下要求时才能拆分索引

  • 目标索引不得存在
  • 源索引的主分片数必须少于目标索引。
  • 目标索引中的主分片数必须是源索引中的主分片数的倍数。
  • 处理拆分过程的节点必须具有足够的可用磁盘空间来容纳现有索引的第二个副本。

_split API 类似于 create index API,并接受目标索引的 settingsaliases 参数

resp = client.indices.split(
    index="my_source_index",
    target="my_target_index",
    settings={
        "index.number_of_shards": 5
    },
    aliases={
        "my_search_indices": {}
    },
)
print(resp)
response = client.indices.split(
  index: 'my_source_index',
  target: 'my_target_index',
  body: {
    settings: {
      'index.number_of_shards' => 5
    },
    aliases: {
      my_search_indices: {}
    }
  }
)
puts response
const response = await client.indices.split({
  index: "my_source_index",
  target: "my_target_index",
  settings: {
    "index.number_of_shards": 5,
  },
  aliases: {
    my_search_indices: {},
  },
});
console.log(response);
POST /my_source_index/_split/my_target_index
{
  "settings": {
    "index.number_of_shards": 5 
  },
  "aliases": {
    "my_search_indices": {}
  }
}

目标索引中的分片数。这必须是源索引中分片数的倍数。

不能在 _split 请求中指定映射。

监控拆分过程

编辑

可以使用 _cat recovery API 监控拆分过程,或者可以使用 cluster health API 通过将 wait_for_status 参数设置为 yellow 来等待所有主分片都被分配。

_split API 在目标索引被添加到集群状态后立即返回,在任何分片被分配之前。此时,所有分片都处于 unassigned 状态。如果由于任何原因无法分配目标索引,则其主分片将保持 unassigned 状态,直到它可以在该节点上分配。

一旦分配了主分片,它将移动到 initializing 状态,并且拆分过程开始。当拆分操作完成时,分片将变为 active。届时,Elasticsearch 将尝试分配任何副本,并可能决定将主分片重新定位到另一个节点。

等待活动分片

编辑

由于拆分操作会创建一个新索引来拆分分片,因此索引创建上的等待活动分片 设置也适用于拆分索引操作。

路径参数

编辑
<索引>
(必需,字符串)要拆分的源索引的名称。
<目标索引>

(必需,字符串)要创建的目标索引的名称。

索引名称必须符合以下条件

  • 仅限小写
  • 不能包含 \/*?"<>|、 ` ` (空格字符)、,#
  • 7.0 之前的索引可以包含冒号(:),但这已被弃用,并且在 7.0+ 中不受支持
  • 不能以 -_+ 开头
  • 不能是 ...
  • 不能超过 255 字节(请注意,它是字节,因此多字节字符将更快地计入 255 限制)
  • . 开头的名称已弃用,但隐藏索引和插件管理的内部索引除外

查询参数

编辑
wait_for_active_shards

(可选,字符串)在继续操作之前必须处于活动状态的每个分片的副本数。设置为 all 或任何非负整数,最大值不超过索引中每个分片的总副本数(number_of_replicas+1)。默认为 1,表示仅等待每个主分片处于活动状态。

请参阅活动分片

master_timeout
(可选,时间单位)等待主节点的时间段。如果在超时过期之前主节点不可用,则请求失败并返回错误。默认为 30 秒。也可以设置为 -1 表示请求永远不应超时。
timeout
(可选,时间单位)在更新集群元数据后等待来自集群中所有相关节点的响应的时间段。如果在超时过期之前未收到响应,则集群元数据更新仍然适用,但响应将指示它未被完全确认。默认为 30 秒。也可以设置为 -1 表示请求永远不应超时。

请求正文

编辑
aliases

(可选,对象对象)结果索引的别名。

aliases 对象的属性
<别名>

(必需,对象)键是别名名称。索引别名名称支持日期数学

对象主体包含别名的选项。支持空对象。

<别名> 的属性
filter
(可选,查询 DSL 对象)用于限制别名可以访问的文档的查询。
index_routing
(可选,字符串)用于将索引操作路由到特定分片的值。如果指定,此值将覆盖索引操作的 routing 值。
is_hidden
(可选,布尔值)如果为 true,则别名是隐藏的。默认为 false。别名的所有索引必须具有相同的 is_hidden 值。
is_write_index
(可选,布尔值)如果为 true,则该索引是别名的写入索引。默认为 false
routing
(可选,字符串)用于将索引和搜索操作路由到特定分片的值。
search_routing
(可选,字符串)用于将搜索操作路由到特定分片的值。如果指定,此值将覆盖搜索操作的 routing 值。
settings
(可选,索引设置对象)目标索引的配置选项。请参阅索引设置