零停机时间索引部署
假设您的数据库中有一些更改,您需要在生产环境中将这些更改与您的 Meilisearch 索引同步。您会怎么做?

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
,反之亦然(enqueued
任务保持不变)。
交换后,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 构建的内容、您的用例以及您的具体需求。
无论您选择哪个选项,我们都期待收到您的来信!