本月初发布了 Meilisearch 的 v0.30 版本。我们的主要目标是使 Meilisearch 工作流程尽可能流畅。毕竟,我们正在向 v1 版本迈进,我们的目标比以往任何时候都更近。为此,实现零停机时间的索引部署是一个至关重要的步骤。我们很高兴地说,v0.30 版本解决了这个问题,使更新生产环境中的索引比以往任何时候都更容易。继续阅读以了解更多关于此功能的工作原理!
挑战
假设您的数据库中有一些更改需要与生产环境中的 Meilisearch 索引同步。您该怎么做?好吧,如果您使用的是 Meilisearch v0.29 版本,您可能会
更新您的索引
🙅♀️ 很简单,对吧?但如果您尝试过,您应该已经知道……

在索引接收搜索查询时更新您的生产索引可能会导致结果不一致,甚至丢失信息。您不希望提供这样的搜索体验,对吧?否则,您就不会使用 Meilisearch 😁
删除索引,创建一个具有相同名称的新索引,并重新索引数据
⌛ 每一步都需要一些时间。即使只是几秒钟,也是几秒钟的停机时间……而且至少需要三个请求!这里几秒钟加上那里几秒钟,可能会导致几分钟的停机时间!不,非常感谢。
创建一个具有不同名称的新索引,在其中暂存更改,然后修改所有客户端以指向新索引
🤔 好吧,这不是最糟糕的选择。但同样,更新客户端会导致停机时间 😞 在某些情况下,当客户端更新被推送到生产环境时,甚至不是开发人员说了算。例如,以 iOS 应用程序为例:Apple 必须在您发布新版本之前对其进行审核,这可能会严重影响您的发布计划。
创建一个中间重定向层以避免停机时间
🧞 很聪明!虽然这是一个很好的方法,但它并不是最直接的方法。它需要架构知识、时间和额外的工具。
您难道不高兴您再也不用做这些事情了吗?
银弹
Meilisearch 一直致力于提供最佳的开发人员体验。
我们的团队过去几个月一直在努力工作,经过多次迭代,他们想出了一个与开发人员堆栈无缝集成的解决方案:**索引交换**。
让我向您展示它是如何工作的。
假设您有一个生产环境中的索引——indexA
——您的客户端在其中进行搜索。您希望将主数据库中的更改与 Meilisearch 同步。为此,您需要按照以下步骤操作。
步骤 1:创建一个包含最新数据的全新索引
首先,您需要创建一个索引——我们称之为 indexA_new
——它代表您要部署到搜索客户端的 indexA
的新版本。从您的数据库中添加最新文档,并在必要时更新索引设置。
不要忘记检查与索引创建相关的所有任务是否已成功完成,包括 indexCreation
、settingsUpdate
和 documentAdditionOrUpdate
任务类型。您可以使用 /tasks
路由 来获取有关其进度的信息。
步骤 2:测试新索引
在将索引发送到生产环境之前,您需要确保一切按预期工作。

确保更新任何您可能使用更新后的数据引入的新字段的设置。例如,您可能希望将新字段添加到 searchableAttributes
、filterableAttributes
和/或 sortableAttributes
中。不要忘记在添加新数据时仔细检查搜索结果的相关性!
👉 请记住,searchableAttributes
列表不仅指定可搜索的字段,还决定属性排名顺序。确保您可能引入的任何新字段都按正确的顺序添加到列表中。
步骤 3:交换索引
一旦您的 indexA_new
成功创建、填充数据并经过测试,它就可以使用**索引交换**进行部署。为此,向 /swap-indexes
端点发送一个 POST
请求。在有效负载中指定您要交换的索引。由于它是交换操作,顺序无关紧要。
curl \
-X POST 'http://localhost:7700/swap-indexes' \
-H 'Content-Type: application/json' \
--data-binary '[
{ "indexes": ["indexA", "indexA_new"] }
]'
👉 在一个受保护的 Meilisearch 实例中,用于交换索引的 API 密钥必须对 indexes.swap
操作以及您要交换的索引有访问权限。如果没有,Meilisearch 将抛出一个 invalid_api_key
错误。有关创建具有特定权限的 API 密钥的更多信息,请参阅文档。
您可以使用响应中的 taskUid
使用 GET /tasks/{task_uid}
端点 跟踪请求的状态。成功的索引交换应该如下所示
{
"uid": 23,
"indexUid": null,
"status":"succeeded",
"type":"indexSwap",
"details":{
"swaps": [
{"indexes": ["indexA", "indexA_new"]},
]
},
"duration": "PT1S",
"enqueuedAt": "2021-08-10T14:29:17.000000Z",
"startedAt": "2021-08-10T14:29:18.000000Z",
"finishedAt": "2021-08-10T14:29:19.000000Z"
}
您的索引已在没有任何停机时间的情况下交换!indexA
的文档、设置和任务历史记录——除了任何排队的任务——已与 indexA_new
的文档、设置和任务历史记录交换。任务历史记录中所有提及 indexA
的内容都已替换为 indexA_new
,反之亦然(排队的
任务保持不变)。

交换后,indexA_new
将保存过时的内容。您可以删除它或将其保留为备份,以防出现问题,您需要交换回来。安全第一!
就是这样!只有三个步骤,如果您喜欢冒险,只有两个步骤。难道还能再好些吗?
蛋糕上的樱桃——是的,复数——🍰
如果我说您可以用一个请求交换多个索引,您会怎么想?
我没有开玩笑。一个请求可以交换任意多的索引对。Meilisearch 可以同时部署所有更改。客户端将同时访问所有索引的新版本,没有任何停机时间。
curl \
-X POST 'http://localhost:7700/swap-indexes' \
-H 'Content-Type: application/json' \
--data-binary '[
{
"indexes": ["indexA", "indexA_new"]
},
{
"indexes": ["indexB", "indexB_new"]
},
{
"indexes": ["indexC", "indexC_new"]
}
]'
在上面的示例中,三个交换操作将同时且原子地发生。
等等,什么?
是的,您可以再读一遍。它是原子的!要么所有索引都成功交换,要么一个都没有。要么所有内容都交换,要么一个都没有。
为什么这很重要?它可以防止数据库中的部分更改,确保一致性,从而提供一流的搜索体验。
结论
正如我之前提到的,此功能已经开发了好几个月,一切都始于用户反馈。我们路线图上的“交换索引”卡片 获得了 38 票,几乎有同样多的评论解释了此功能必不可少的用例。以下是我们最喜欢的一些评论
- “在尝试更改生产数据库上的规则以调整以获得最佳结果时,它将非常有用。”
- “如果索引意外创建了错误的名称,或者需要出于各种原因对其进行重命名/更改,这将有所帮助。”
- “我们需要它来清理所有已删除的项目。”
这些只是一些例子。这种见解对我们的团队非常有帮助,因为它让我们能够塑造产品以满足用户的需求。
虽然非常方便——您可以全面了解所有已提交、正在开发和已发布的功能想法——路线图 只是提供反馈的选项之一。
我认为,GitHub 上的产品讨论 是解释您的需求的最佳场所之一。您可以直接与 Meilisearch 产品团队联系,而且由于它是公开的,任何人都可以参与并丰富这个过程。
最后一个选项是我们全新的Discord 服务器。不要犹豫,加入我们,谈谈您使用 Meilisearch 创建的内容、您的用例和您的特定需求。
无论您选择哪个选项,我们都期待您的反馈!