发布日期: 2026-03-02
版本号: v1.37.0

Meilisearch v1.37 版本引入了复制分片功能(仅限企业版),移除了 vectorStoreSetting 实验性特性,并将新的向量存储稳定化以提升性能。此版本还包含一项安全修复,升级了本地 Web 界面以改进 API 密钥的存储方式。此外,更新涉及对 network 实验性功能的破坏性更改,要求分片配置中至少包含一个远程节点,并自动迁移现有数据库。其他改进包括优化向量索引性能和若干小问题修复。

更新内容 (中文)

[!IMPORTANT]
此版本包含针对 network 实验功能用户的破坏性变更

Meilisearch v1.37 引入了复制分片功能,移除了 vectorStoreSetting 实验功能,优化了新向量存储的最佳性能,增加了安全修复其他改进

✨ 改进

§ 复制分片

[!NOTE] 复制分片功能需要 Meilisearch 企业版 (EE)。

  • Meilisearch Cloud 用户,如需复制分片功能,请联系支持团队。
  • 社区版用户,如需在生产环境中使用复制分片,请联系销售部门。

§ 破坏性变更

  • 现在,当 leader 不为 null 时,发送到 PATCH /network 路由的 network 对象必须包含至少一个 shard 对象,且该对象内须包含至少一个远程节点。

现有数据库在使用 --experimental-dumpless-upgrade 升级且 leader 不为 null 时,将自动进行迁移,对于每个远程节点:

  1. 创建一个与远程节点同名的分片。
  2. 该分片的 remotes 列表中仅包含一个与其同名的远程节点。

此变更不会导致任何文档重新分片。

为确保在不进行文档重新分片的情况下完成升级,迁移过程会为远程节点和分片使用相同的名称。但在新配置中,建议为分片和远程节点使用不同的名称。

迁移示例

例如,以下 network 对象:

{
  "leader": "ms-00",
  "self": "ms-01",
  "remotes": {
    "ms-00": { /* .. */ },
    "ms-01": { /* .. */ }
  }
}

将转换为:

{
  "leader": "ms-00",
  "self": "ms-01",
  "remotes": {
    "ms-00": { /* .. */ },
    "ms-01": { /* .. */ }
  },
  "shards": {  // ✨ 新增
    "ms-00": {  // 分片名称与远程节点同名
      "remotes": ["ms-00"] // 由该远程节点拥有
    },
    "ms-01": {
      "remotes": ["ms-01"]
    }
  }
}

新增 network.shards 字段

PATCH /networkGET /network 路由的 network 对象现在包含新字段 shards,这是一个对象,其值为 shard 对象,键为每个分片的名称。

每个 shard 对象包含单个字段 remotes,这是一个字符串数组,每个字符串代表一个现有远程节点的名称。

便捷字段

PATCH /network 中的 shard 对象包含额外的便捷字段 addRemotesremoveRemotes

  • 将远程节点名称数组传递给 shard.addRemotes 可将这些远程节点添加到分片的远程节点列表中。
  • 将远程节点名称数组传递给 shard.removeRemotes 可将这些远程节点从分片的远程节点列表中移除。
  • 如果 shard.remotes 字段存在且非 null,它将完全覆盖分片现有的远程节点列表。
  • 如果多个选项存在且非 null,则应用顺序为 shard.remotes,然后是 shard.addRemotes,最后是 shard.removeRemotes
添加一个带有若干远程节点的新分片
// PATCH /network
{
  // 假设远程节点 `ms-0`、`ms-1`、`ms-2` 已在先前的 PATCH /network 调用中发送
  "shards": {
    "s-a": { // 新分片
      "remotes": ["ms-0", "ms-1"]
    }
  }
}

远程节点 ms-0ms-1 拥有新分片 s-a

完全覆盖拥有分片的远程节点列表
// PATCH /network
{
  // 假设远程节点 `ms-0`、`ms-1`、`ms-2`
  // 假设分片 `s-a` 由 `ms-0` 和 `ms-1` 拥有
  "shards": {
    "s-a": {
      "remotes": ["ms-2"]
    }
  }
}

ms-2 现在是 s-a 的唯一所有者,替代了 ms-0ms-1

添加一个远程节点,而不覆盖拥有分片的远程节点列表
// PATCH /network
{
  // 假设远程节点 `ms-0`、`ms-1`、`ms-2`
  // 假设分片 `s-a` 由 `ms-2` 拥有
  "shards": {
    "s-a": {
      "addRemotes": ["ms-0"]
    }
  }
}

ms-0ms-2 现在是 s-a 的所有者。

移除一个远程节点,而不覆盖拥有分片的远程节点列表
// PATCH /network
{
  // 假设远程节点 `ms-0`、`ms-1`、`ms-2`
  // 假设分片 `s-a` 由 `ms-0` 和 `ms-2` 拥有
  "shards": {
    "s-a": {
      "removeRemotes": ["ms-2"]
    }
  }
}

ms-0 现在是 s-a 的唯一所有者。

从分片列表中完全移除一个分片

将分片设置为 null

// PATCH /network
{
  "shards": {
    "s-a": null
  }
}

或将其 remotes 列表设置为空列表:

// PATCH /network
{
  "shards": {
    "s-a": {
      "remotes": []
    }
  }
}

network.shards 有效性

network.leader 不为 null 时,network.shards 中的每个 shard 对象必须满足:

  1. 仅包含存在于 remotes 列表中的 remotes
  2. 至少包含一个远程节点。

此外,network.shards 必须包含至少一个分片。

不满足任何这些条件将导致 PATCH /network 路由返回 400 invalid_network_shards 响应。

分片逻辑变更

文档现在根据网络中声明的分片列表进行分片,而非远程节点列表。拥有某个分片的所有远程节点将处理属于该分片的文档,从而实现复制。

复制示例

以下配置定义了 3 个远程节点 012,以及 3 个分片 ABC,使得每个远程节点拥有两个分片,实现复制(丢失一个远程节点不会丢失任何文档)。

{
  "leader": "0",
  "self": "0",
  "remotes": {
    "0": { /* .. */ },
    "1": { /* .. */ },
    "2": { /* .. */ }
  },
  "shards": {
    "A": {
      "remotes": ["0", "1"]
    },
    "B": {
      "remotes": ["1", "2"]
    },
    "C": {
      "remotes": ["2", "0"]
    }
  }
}
  • 通过让所有远程节点拥有所有分片,支持完全复制。
  • 通过让部分远程节点比其他远程节点拥有更多分片,支持不平衡复制。
  • 通过让远程节点不拥有任何分片,支持“观察者”远程节点。观察者远程节点在本版本中用处不大,未来版本可能会升级,使其在不索引文档的情况下保留所有文档,以便为其他远程节点“重新生成”分片。

useNetwork 考虑 network.shards

当在搜索查询中传递 useNetwork: true 时,它将被扩展为多个查询,使得 network.shards 中声明的每个分片恰好出现一次,并与拥有该分片的远程节点关联。

这确保了结果中没有缺失或重复的文档。

_shard 过滤器

当启用 network 实验功能后,就可以根据文档所属的分片对其进行过滤。

假设 s-as-bnetwork.shards 中声明的两个分片的名称,那么:

  • 在搜索或文档获取的 filter 参数中使用 _shard = "s-a" 将返回属于 s-a 的文档。
  • _shard != "s-a" 将返回不属于 s-a 的文档。
  • _shard IN ["s-a", "s-b"] 将返回属于 s-as-b 的文档。

您可以在手动远程联合搜索中使用这些新过滤器,以在网络的所有分片上创建分区。

[!IMPORTANT] 为避免结果中出现重复或缺失的文档,对于手动构建的远程联合搜索请求,所有分片应恰好出现在一个查询中。

[!TIP] 使用 useNetwork: true 构建的搜索请求已经构建了正确的分片分区。在复制分片场景中,应优先使用这些请求,而不是手动构建的远程联合搜索请求。

更新说明

在使用无转储升级更新您的 Meilisearch 网络时,请遵守以下指南:

  1. 在所有网络远程节点完成更新之前,请勿调用 PATCH /network 路由。
  2. 如果使用带有 useNetwork: true 的搜索路由,请在未更新的远程节点上调用它们。在已更新的远程节点上调用将导致未更新的远程节点搜索失败,因为它们不知道 _shard 过滤器。

由 @dureuill 在 https://github.com/meilisearch/meilisearch/pull/6128 提交

§ 移除 vectorStoreSetting 实验功能

新的 HNSW 向量存储 (hannoy) 已经稳定,现在是 Meilisearch 中唯一支持的向量存储。

因此,更新到 v1.37.0 将迁移所有剩余的遗留向量存储索引(使用 arroy)到 hannoy,而 vectorStoreSetting 实验功能 不再可用。

由 @Kerollmops 在 https://github.com/meilisearch/meilisearch/pull/6176 提交

改进嵌入的索引性能

我们从向量索引中移除了一个计算成本高的步骤。

在一个有 2000 万文档的数据库中,这为每批 1100 秒的索引操作节省了 300 秒。

由 @Kerollmops 在 https://github.com/meilisearch/meilisearch/pull/6175 提交

§ 🔒 安全

  • 升级了 mini-dashboard(本地 Web 界面),现在
    • 将 API 密钥存储在内存中,而不是 localStorage
    • 升级了具有潜在安全漏洞的依赖项

由 @Strift 和 @curquiza 在 https://github.com/meilisearch/meilisearch/pull/6186https://github.com/meilisearch/meilisearch/pull/6172 提交

§ 🔩 其他改进

完整变更日志: https://github.com/meilisearch/meilisearch/compare/v1.36.0...v1.37.0

更新内容 (原始)

[!IMPORTANT]
This release contains breaking changes for users of the network experimental feature.

Meilisearch v1.37 introduces replicated sharding, removes the vectorStoreSetting experimental feature, stabilizes our new vector store for best performance, adds a security fix and miscellaneous improvements.

✨ Improvements

§ Replicated sharding

[!NOTE] Replicated sharding requires Meilisearch Enterprise Edition (EE).

  • Users of Meilisearch Cloud, please contact support if you need replicated sharding.
  • Users of the Community Edition, please contact the sales if you want to use replicated sharding in production.

§ Breaking changes

  • network objects sent to the PATCH /network route must now contain at least one shard object containing at least one remote when leader is not null.

Existing databases will be migrated automatically when upgraded with --experimental-dumpless-upgrade when leader is not null, such that for each remote:

  1. A shard with the same name as the remote is created
  2. This shard has exactly one remote in its remotes list: the remote with the same name as the shard.

This change will not cause any document to be resharded.

To be able to upgrade without resharding, the migration uses the same name for remotes and for shards. However, in new configurations, we recommend using different names for shards and remotes.

Example of migration

For instance, the following network object:

{
  "leader": "ms-00",
  "self": "ms-01",
  "remotes": {
    "ms-00": { /* .. */ },
    "ms-01": { /* .. */ }
  }
}

is converted to:

{
  "leader": "ms-00",
  "self": "ms-01",
  "remotes": {
    "ms-00": { /* .. */ },
    "ms-01": { /* .. */ }
  },
  "shards": {  // ✨ NEW
    "ms-00": {  // shard named like the remote
      "remotes": ["ms-00"] // is owned by the remote
    },
    "ms-01": {
      "remotes": ["ms-01"]
    }
  }
}

Addition of network.shards

The network object for routes PATCH /network and GET /network now contains the new field shards, which is an object whose values are shard objects, and keys the name of each shard.

Each shard object contains a single field remotes, which is an array of strings, each string representing the name of an existing remote.

Convenience fields

The shard objects in PATCH /network contain the additional fields addRemotes and removeRemotes meant for convenience:

  • pass an array of remote names to shard.addRemotes to add these remotes to the list of remotes of a shard.
  • pass an array of remote names to shard.removeRemotes to remove these remotes from the list of remotes of a shard.
  • if present and non-null, shard.remotes will completely override the existing list of remotes for a shard.
  • if several of these options are present and non-null, then the order of application is shard.remotes, then shard.addRemotes, then shard.removeShards.
Adding a new shard with some remotes
// PATCH /network
{
  // assuming that remotes `ms-0`, `ms-1`, `ms-2` where sent in a previous call to PATCH /network
  "shards": {
    "s-a": { // new shard
      "remotes": ["ms-0", "ms-1"]
    }
  }
}

Remotes ms-0 and ms-1 own the new shard s-a.

Fully overriding the list of remotes owning a shard
// PATCH /network
{
  // assuming remotes `ms-0`, `ms-1`, `ms-2`
  // assuming shard `s-a`, owned by `ms-0` and `ms-1`
  "shards": {
    "s-a": {
      "remotes": ["ms-2"]
    }
  }
}

ms-2 is now the sole owner of s-a, replacing ms-0 and ms-1.

Adding a remote without overriding the list of remotes owning a shard
// PATCH /network
{
  // assuming remotes `ms-0`, `ms-1`, `ms-2`
  // assuming shard `s-a`, owned by `ms-2`
  "shards": {
    "s-a": {
      "addRemotes": ["ms-0"]
    }
  }
}

ms-0 and ms-2 are now the owners of s-a.

Removing a remote without overriding the list of remotes owning a shard
// PATCH /network
{
  // assuming remotes `ms-0`, `ms-1`, `ms-2`
  // assuming shard `s-a`, owned by `ms-0` and `ms-2`
  "shards": {
    "s-a": {
      "removeRemotes": ["ms-2"]
    }
  }
}

ms-0 is now the sole owner of s-a.

Entirely removing a shard from the list of shards

Set the shard to null:

// PATCH /network
{
  "shards": {
    "s-a": null
  }
}

Or set its remotes list to the empty list:

// PATCH /network
{
  "shards": {
    "s-a": {
      "remotes": []
    }
  }
}

network.shards validity

When network.leader is not null, each shard object in network.shards must:

  1. Only contain remotes that exist in the list of remotes.
  2. Contain at least one remote.

Additionally, network.shards must contain at least one shard.

Failure to meet any of these conditions will cause the PATCH /network route to respond with 400 invalid_network_shards.

Change in sharding logic

Documents are now sharded according to the list of shards declared in the network rather than the list of remotes. All remotes owning a shard will process the documents that belong to this shard, allowing for replication.

Example of replication

The following configuration defines 3 remotes 0, 1 and 2, and 3 shards A, B, C, such that each remote owns two shards, achieving replication (losing one remote does not lose any document).

{
  "leader": "0",
  "self": "0",
  "remotes": {
    "0": { /* .. */ },
    "1": { /* .. */ },
    "2": { /* .. */ }
  },
  "shards": {
    "A": {
      "remotes": ["0", "1"]
    },
    "B": {
      "remotes": ["1", "2"]
    },
    "C": {
      "remotes": ["2", "0"]
    }
  }
}
  • Full replication is supported by having all remotes own all the shards.
  • Unbalanced replication is supported by having some remotes own more shards than other remotes.
  • “Watcher” remotes are supported by having remotes that own no shards. Watcher remotes are not very useful in this release, and might be upgraded in a future release, so that they keep all documents without indexing them, allowing to “respawn” shards for other remotes.

useNetwork takes network.shards into account

When useNetwork: true is passed to a search query, it is expanded to multiple queries such that each shard declared in network.shards appears exactly once, associated with a remote that owns that shard.

This ensures that there is no missing or duplicate documents in the results.

_shard filters

When the network experimental feature is enabled, then it becomes possible to filter documents depending on the shard they belong to.

Given s-a and s-b the names of two shards declared in network.shards, then:

  • _shard = "s-a" in a filter parameter to the search or documents fetch will return the documents that belong to s-a.
  • _shard != "s-a" will return the documents that do not belong to s-a
  • _shard IN ["s-a", "s-b"] will return the documents that belong to s-a or to s-b.

You can use these new filters in manual remote federated search to create a partitioning over all shards in the network.

[!IMPORTANT] To avoid duplicate or missing documents in results, for manually crafted remote federated search requests, all shards should appear in exactly one query.

[!TIP] Search requests built with useNetwork: true already build a correct partitioning over shards. They should be preferred to manually crafted remote federated search requests in replicated sharding scenarios.

Update instructions

When updating your Meilisearch network using dumpless upgrade, please observe the following guidelines:

  1. Do not call the PATCH /network route until all remotes of the network are finished updating
  2. If using the search routes with useNetwork: true, call them on un-updated remotes. Calling it on already updated remotes will cause un-updated remotes to fail the search as they don’t know about the _shard filters.

By @dureuill in https://github.com/meilisearch/meilisearch/pull/6128

§ Remove vectorStoreSetting experimental feature

The new HNSW vector store (hannoy) has been stabilized and is now the only supported vector store in Meilisearch.

As a result, updating to v1.37.0 will migrate all remaining legacy vector store indexes (using arroy) to hannoy, and the vectorStoreSetting experimental feature is no longer available.

By @Kerollmops in https://github.com/meilisearch/meilisearch/pull/6176

Improve indexing performance for embeddings

We removed a computationally expensive step from vector indexing.

On a DB with 20M documents, this removes 300s per indexing batch of 1100s.

By @Kerollmops in https://github.com/meilisearch/meilisearch/pull/6175

§ 🔒 Security

  • Bump mini-dashboard (local web interface) which
    • now stores API key in RAM instead of in the localStorage
    • bumps dependencies with potential security vulnerabilities

By @Strift and @curquiza in https://github.com/meilisearch/meilisearch/pull/6186 and https://github.com/meilisearch/meilisearch/pull/6172

§ 🔩 Miscellaneous

Full Changelog: https://github.com/meilisearch/meilisearch/compare/v1.36.0...v1.37.0

下载链接