MeiliSearch 1.55.0 进阶实战与运维

MeiliSearch 1.55.0 进阶实战:searchCutoffMs、停用词与同义词、多索引管理、API key 权限体系、dump 备份与跨实例恢复迁移的完整运维闭环

MeiliSearchMeiliSearchLinux搜索引擎2026/10/17

MeiliSearch 1.55.0 进阶实战与运维

目标:掌握 MeiliSearch 1.55.0 的运维进阶能力——搜索超时(searchCutoffMs)、停用词与同义词调优、多索引管理、API key 权限体系、dump 备份与跨实例恢复迁移,全部操作基于真实索引与数据实测。已在 Ubuntu 22.04 + MeiliSearch 1.55.0(production 模式,movies 索引 31944 条文档)环境逐条实测通过。

一、环境信息

本文所有命令与回显均在以下环境中实测:

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • MeiliSearch 版本:1.55.0(production 模式,监听 127.0.0.1:7700,systemd 托管)
  • Master Key:q1w2e3r4t5y6u7i8o0p(演示值)
  • 示例索引:movies(官方数据集,31944 条电影文档,字段 id/title/overview/genres/poster/release_date)
  • release_date 为 Unix 秒时间戳(如 1670976000 = 2022-12-14)

二、概览:设置体系与运维对象

MeiliSearch 的运维对象分三层:**索引设置(settings)**决定相关性搜索行为(分词、容错、过滤、排序、超时等);API key决定谁能访问哪些索引的哪些操作;数据备份(dump/snapshot)与升级迁移保障数据安全与版本演进。除搜索外所有写操作均为异步任务(返回 taskUid,轮询 GET /tasks/{taskUid} 确认 succeeded),本文每个设置操作后均附任务验证。

先读取 movies 索引的完整设置快照,作为本文各节的基线:

curl -s http://127.0.0.1:7700/indexes/movies/settings -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -m json.tool

实测输出(关键项节选):

{
    "displayedAttributes": ["*"],
    "searchableAttributes": ["*"],
    "filterableAttributes": ["genres", "release_date"],
    "sortableAttributes": ["release_date"],
    "rankingRules": ["words", "typo", "proximity", "attributeRank", "sort", "wordPosition", "exactness"],
    "stopWords": [],
    "synonyms": {},
    "distinctAttribute": null,
    "typoTolerance": {
        "enabled": true,
        "minWordSizeForTypos": {"oneTypo": 5, "twoTypos": 9},
        "disableOnWords": [],
        "disableOnAttributes": [],
        "disableOnNumbers": false
    },
    "faceting": {"maxValuesPerFacet": 100, "sortFacetValuesBy": {"*": "alpha"}},
    "pagination": {"maxTotalHits": 1000},
    "searchCutoffMs": 1500,
    ...
}

💡 技术拓展:rankingRules 是 MeiliSearch 相关性排序的规则流水线,默认七条按序生效。同类引擎的相关性机制对比:

引擎相关性机制可定制性
MeiliSearch固定规则流水线(words→typo→proximity→…),可增删排序规则与权重中(规则顺序/自定义规则,非评分公式)
ElasticsearchBM25 评分公式,可整体替换(如 script_score)高(评分可编程)
Typesense与 MeiliSearch 类似的规则权重模型中

MeiliSearch 刻意隐藏评分公式以换取开箱即用的相关性质量;运维调优的主战场是 typoTolerance、stopWords、synonyms、searchableAttributes 这几项,正是本章重点。

三、搜索体验调优(settings 实战)

3.1 searchCutoffMs:搜索超时截断

searchCutoffMs 限制单次搜索的最长执行毫秒数,超时即返回当前已有的最佳结果(而非报错),用于兜底大索引/复杂查询的响应时间,保护线上体验。实测设置为 150:

curl -s -X PUT 'http://127.0.0.1:7700/indexes/movies/settings/search-cutoff-ms' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '150'

实测输出:

{"taskUid":6,"indexUid":"movies","status":"enqueued","type":"settingsUpdate","enqueuedAt":"2026-10-16T15:37:19.587325623Z"}

回读确认(设置任务完成后):

curl -s http://127.0.0.1:7700/indexes/movies/settings/search-cutoff-ms -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

150

生效验证:正常规模查询的处理耗时远低于 150ms(实测 processingTimeMs 为个位数毫秒),不会被截断;仅当数据量或查询复杂度超限时截断机制才介入。默认值 1500(毫秒),按业务 P99 预算调整。

3.2 停用词(stopWords)

停用词从分词结果中剔除,不参与索引与搜索,典型如英文冠词/介词。实测添加 the/a/an/of:

curl -s -X PUT 'http://127.0.0.1:7700/indexes/movies/settings/stop-words' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '["the", "a", "an", "of"]'

实测输出:

{"taskUid":4,"indexUid":"movies","status":"enqueued","type":"settingsUpdate","enqueuedAt":"2026-10-16T15:37:13.573787087Z"}

回读(注意响应已按字母序规范化):

curl -s http://127.0.0.1:7700/indexes/movies/settings/stop-words -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

["a","an","of","the"]

效果:搜索 the dark knight 中 the 不再占用匹配权重,dark knight 成为有效查询(对照实测见 3.3 的搜索回显)。

3.3 同义词(synonyms)

同义词把一个词映射到一组等价/关联词,支持一对多。实测添加 batman → dark knight 与 hk → hong kong:

curl -s -X PUT 'http://127.0.0.1:7700/indexes/movies/settings/synonyms' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '{"batman": ["dark knight"], "hk": ["hong kong"]}'

实测输出:

{"taskUid":5,"indexUid":"movies","status":"enqueued","type":"settingsUpdate","enqueuedAt":"2026-10-16T15:37:16.580367585Z"}

回读:

curl -s http://127.0.0.1:7700/indexes/movies/settings/synonyms -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

{"batman":["dark knight"],"hk":["hong kong"]}

搜索 dark knight 时同义词把 Batman 系列一并拉进候选(配合 3.2 停用词,the 不干扰匹配):

curl -s -X POST 'http://127.0.0.1:7700/indexes/movies/search' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '{"q": "dark knight", "limit": 5}' | python3 -c "import json,sys; d=json.load(sys.stdin); [print(h['id'], h['title']) for h in d['hits']]"

实测输出:

155 The Dark Knight
49026 The Dark Knight Rises
123025 Batman: The Dark Knight Returns, Part 1
141559 Legends of the Dark Knight: The History of Batman
142061 Batman: The Dark Knight Returns, Part 2

缩写同义词同样生效:搜 hk 实测命中 125 条(hong kong 相关影片被召回)。

3.4 typoTolerance:容错阈值与禁用

容错默认阈值 oneTypo=5(长度≥5 的词容忍 1 个错字)、twoTypos=9(长度≥9 容忍 2 个错字),可整体调整:

⚠️ 注意:1.55.0 中 /settings/typo-tolerance 端点仅接受 PATCH 方法,用 PUT 会返回 405(实测):

HTTP 405

正确写法:

curl -s -X PATCH 'http://127.0.0.1:7700/indexes/movies/settings/typo-tolerance' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '{"minWordSizeForTypos": {"oneTypo": 6, "twoTypos": 10}}'

实测输出:

{"taskUid":12,"indexUid":"movies","status":"enqueued","type":"settingsUpdate","enqueuedAt":"2026-10-16T15:44:01.705001613Z"}

(HTTP 202 已受理)回读确认:

curl -s http://127.0.0.1:7700/indexes/movies/settings/typo-tolerance -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

{"enabled":true,"minWordSizeForTypos":{"oneTypo":6,"twoTypos":10},"disableOnWords":[],"disableOnAttributes":[],"disableOnNumbers":false}

oneTypo 从 5 调到 6:更短的词不再参与 1 错字容错(如 4 字母的词打错一字将不命中),可减少误召回;反向调低则更宽容。disableOnAttributes 可对指定字段关闭容错(如标题字段要求精确匹配)。

3.5 searchableAttributes:限定搜索范围

默认 ["*"] 全字段参与全文匹配。改为只搜标题与简介:

curl -s -X PUT 'http://127.0.0.1:7700/indexes/movies/settings/searchable-attributes' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '["title", "overview"]'

回读与生效验证:

curl -s http://127.0.0.1:7700/indexes/movies/settings/searchable-attributes -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

["title","overview"]

此时搜索词若只出现在 genres 字段(如 "Romance")将无法通过 genres 命中(实测查询 Romance 的命中均来自 title/overview 正文的匹配,第一条为标题即 Romance 的电影 id=258480)。该设置同时决定相关性计算的字段优先级(数组越靠前权重越高)。恢复全字段:

curl -s -X PUT 'http://127.0.0.1:7700/indexes/movies/settings/searchable-attributes' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '["*"]'

四、多索引管理

一个 MeiliSearch 实例可托管任意多个索引,各索引独立拥有文档、主键与全套 settings。实测创建、查询、删除:

4.1 创建与列表

curl -s -X POST http://127.0.0.1:7700/indexes -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '{"uid": "products", "primaryKey": "sku"}'

实测输出:

{"taskUid":7,"indexUid":"products","status":"enqueued","type":"indexCreation","enqueuedAt":"2026-10-16T15:37:39.103915897Z"}
curl -s 'http://127.0.0.1:7700/indexes?limit=10' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -c "import json,sys; d=json.load(sys.stdin); [print(i['uid'], i['primaryKey'], i['createdAt']) for i in d['results']]; print('total:', d['total'])"

实测输出:

movies id 2026-10-16T15:30:32.856199603Z
products sku 2026-10-16T15:37:39.104559015Z
total: 2

4.2 删除索引

curl -s -X DELETE 'http://127.0.0.1:7700/indexes/products' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

{"taskUid":8,"indexUid":"products","status":"enqueued","type":"indexDeletion","enqueuedAt":"2026-10-16T15:37:41.122347011Z"}

确认删除(任务成功后):

curl -s 'http://127.0.0.1:7700/indexes' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -c "import json,sys; d=json.load(sys.stdin); print('after delete total:', d['total'], [i['uid'] for i in d['results']])"

实测输出:

after delete total: 1 ['movies']

💡 说明:索引删除是不可逆操作(文档与设置一并清除),生产上建议删除前 POST /dumps 做整体备份。

五、备份 dump 与跨实例恢复迁移

5.1 dump 与快照对比

MeiliSearch 提供两种备份机制:

机制触发方式内容用途
dumpAPI 手动(POST /dumps)索引+文档+设置的完整快照,压缩单文件 .dump备份归档、跨版本/跨实例迁移
snapshot--schedule-snapshot 定时数据库目录级别的定期快照本机崩溃快速恢复

选择建议:迁移与归档用 dump(版本兼容、可导入任意实例);本机容灾用定时 snapshot(恢复更快,但不跨版本)。本文实测 dump 全流程。

5.2 创建 dump

curl -s -X POST http://127.0.0.1:7700/dumps -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

{"taskUid":9,"indexUid":null,"status":"enqueued","type":"dumpCreation","enqueuedAt":"2026-10-16T15:38:11.409295239Z"}

轮询任务确认成功(注意任务过滤参数是复数 types,单数 type 会报 Unknown parameter——实测报错:Unknown parameter \type`: did you mean `types`?`):

curl -s 'http://127.0.0.1:7700/tasks?types=dumpCreation&limit=1' -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -c "import json,sys; t=json.load(sys.stdin)['results'][0]; print(t['uid'], t['status'], t['details'], t['duration'])"

实测输出:

9 succeeded {'dumpUid': '20261016-153811410'} PT0.502769789S

确认落盘文件(--dump-dir 指定的目录):

ls -lh /var/lib/meilisearch/dumps/

实测输出:

total 5.9M
-rw-r--r-- 1 meilisearch meilisearch 5.9M Oct 16 15:38 20261016-153811410.dump

5.3 用 --import-dump 恢复到全新实例(模拟迁移)

以下演示「数据目录全新 + dump 导入」的完整恢复流程(升级/迁移同流程)。停止服务并腾空数据目录(保留 dump):

systemctl stop meilisearch
mv /var/lib/meilisearch/data /var/lib/meilisearch/data.bak

以服务用户在可写工作目录中启动并导入 dump(工作目录权限问题见第八章 Q3):

su -s /bin/bash meilisearch -c 'cd /var/lib/meilisearch && nohup meilisearch --http-addr 127.0.0.1:7700 --master-key q1w2e3r4t5y6u7i8o0p --db-path /var/lib/meilisearch/data --import-dump /var/lib/meilisearch/dumps/20261016-153811410.dump > /tmp/ms-import.log 2>&1 &'

导入过程日志(实测节选):

2026-10-16T15:42:08.217776Z  INFO meilisearch: Importing a dump of meilisearch version=V6 date=2026-10-16 15:38:11.410390027 +00:00:00
2026-10-16T15:42:08.222484Z  INFO meilisearch: Importing index `movies`.
2026-10-16T15:42:08.224388Z  INFO meilisearch: Importing the settings.
2026-10-16T15:42:10.485449Z  INFO meilisearch: All documents successfully imported.
Server listening on:    "http://127.0.0.1:7700"

导入完成后验证数据与设置完整恢复:

curl -s http://127.0.0.1:7700/indexes/movies/stats -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -c "import json,sys; print('numberOfDocuments:', json.load(sys.stdin)['numberOfDocuments'])"
curl -s http://127.0.0.1:7700/indexes/movies/settings/synonyms -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p'

实测输出:

numberOfDocuments: 31944
{"batman":["dark knight"],"hk":["hong kong"]}

文档量(31944)与同义词设置全部恢复,验证通过。之后停掉手动进程、恢复 systemd 托管(systemctl start meilisearch)即可。

5.4 版本升级迁移流程

结合 dump 的标准升级流程:

  1. 升级前备份:POST /dumps 导出(见 5.2);
  2. 替换二进制为新高版本(见《MeiliSearch 1.55.0 Ubuntu 22.04 安装指南》第九章);
  3. 直接重启启动:相邻版本的数据库文件可原地打开;若新版本提示数据库格式需要升级,使用 --upgrade-db 参数(1.55.0 实测存在的参数,配合启动执行原地数据库格式升级);
  4. 跨大版本(官方提示不保证兼容)时:旧版本导出 dump → 新版本实例 --import-dump 导入(见 5.3)。

六、API key 权限体系

6.1 默认密钥组

配置 master key 后,实例自动生成四个默认 API key(供分发给不同信任级别的调用方):

curl -s http://127.0.0.1:7700/keys -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' | python3 -c "import json,sys; [print(k['name'],'|',k['key'][:20]+'...','| actions:',k['actions'],'| indexes:',k['indexes']) for k in json.load(sys.stdin)['results']]"

实测输出:

Default Search API Key | 29b02e415117de61143b... | actions: ['search'] | indexes: ['*']
Default Admin API Key | b58d1e0d30323c439391... | actions: ['*'] | indexes: ['*']
Default Read-Only Admin API Key | c85d4e9a10434010266d... | actions: ['*.get', 'keys.get'] | indexes: ['*']
Default Chat API Key | fd1944e190b703a58548... | actions: ['chatCompletions', 'search'] | indexes: ['*']

四把默认钥匙:仅搜索(发给前端)、全权管理(服务端)、只读管理(监控/审计)、聊天补全。业务上更常见的是按索引细分的自定义 key。

6.2 创建自定义受限 key

创建「只能搜索 movies 索引」的 key(2026-09-01 前有效——实测演示用 2030 过期):

curl -s -X POST http://127.0.0.1:7700/keys -H 'Authorization: Bearer q1w2e3r4t5y6u7i8o0p' -H 'Content-Type: application/json' --data-binary '{"name": "movies-search-ro", "description": "Search only on movies", "actions": ["search"], "indexes": ["movies"], "expiresAt": "2030-01-01T00:00:00Z"}'

实测输出:

{"name":"movies-search-ro","description":"Search only on movies","key":"8865e33ee6e48bcafca5ad5984b8dacf970f3c76248dd7fb9611d00a188996ed","uid":"d4172b19-9819-4e94-ac40-a533d7f56d67","actions":["search"],"indexes":["movies"],"expiresAt":"2030-01-01T00:00:00Z","createdAt":"2026-10-16T15:37:54.066033641S","updatedAt":"2026-10-16T15:37:54.066033641Z"}

字段含义:key 是分发给调用方的令牌(master key 可随时重置默认 key,自定义 key 由引擎按 UID 派生);actions 为操作白名单(search/.get/ 全权等);indexes 为索引白名单(支持 * 通配);expiresAt 到期自动失效。

6.3 权限边界实测

用受限 key 搜索 movies(应放行):

curl -s -o /dev/null -w '%{http_code}\n' -X POST 'http://127.0.0.1:7700/indexes/movies/search' -H 'Authorization: Bearer 8865e33ee6e48bcafca5ad5984b8dacf970f3c76248dd7fb9611d00a188996ed' -H 'Content-Type: application/json' --data-binary '{"q":"batman"}'

实测输出:

200

用受限 key 创建索引(应拒绝):

curl -s -X POST http://127.0.0.1:7700/indexes -H 'Authorization: Bearer 8865e33ee6e48bcafca5ad5984b8dacf970f3c76248dd7fb9611d00a188996ed' -H 'Content-Type: application/json' --data-binary '{"uid": "nope"}' -w '\nHTTP %{http_code}\n'

实测输出:

{"message":"The provided API key is invalid.","code":"invalid_api_key","type":"auth","link":"https://docs.meilisearch.com/errors#invalid_api_key"}
HTTP 403

越权请求统一返回 403 invalid_api_key(引擎不泄露 key 的实际权限范围,属安全设计)。

6.4 key 的更新与吊销

自定义 key 支持改名、改描述(PATCH /keys/{keyOrUid});到期时间与 actions 不可修改,需要变更时创建新 key、验证后删除旧 key(DELETE /keys/{keyOrUid},master key 操作)。吊销即删除,分发出去的旧令牌立刻失效。

七、日常运维速查

操作命令(均带 master key 请求头)
健康检查(无需鉴权)GET /health
版本信息GET /version
索引统计GET /indexes/{uid}/stats
任务列表GET /tasks?limit=20&statuses=enqueued,processing
失败任务排查GET /tasks?statuses=failed,看 error 字段
导出备份POST /dumps → 轮询任务 → 取 --dump-dir 下 .dump
恢复/迁移新实例启动加 --import-dump <file>
全量设置回读GET /indexes/{uid}/settings
密钥列表GET /keys

任务排障实例:所有 settings/document/index 操作完成后都应有对应 succeeded 任务;若任务 failed,其 error 字段给出 code/message/link(如 invalid_search_filter 的引号问题、document_not_found 等,实测案例见《MeiliSearch 1.55.0 快速入门与集成示例》第九章)。

八、注意事项与常见问题

Q1:设置更新后没有立即生效?

设置与文档一样走异步任务:PUT/PATCH 返回 taskUid 后需等任务 succeeded 才生效(实测各节均已轮询确认)。批量改设置时逐项轮询,或用 GET /tasks?statuses=enqueued,processing 确认队列清空后再验证行为。

Q2:同义词/停用词该在写入数据前还是后设置?

任意时刻都可设置;设置变更会触发对应索引的重建任务(数据量大时该任务耗时数秒到分钟级),期间搜索服务不中断。生产变更建议低峰执行并预留任务完成时间。

Q3:以普通用户手动启动报 Permission denied (os error 13)。

MeiliSearch 启动时要求当前工作目录可写(与数据目录权限是两回事)。实测复现:以 meilisearch 用户在 /(无写权限)下启动报错退出,cd /var/lib/meilisearch 后同一命令正常启动。结论:手动以服务用户运行时先 cd 到可写目录;systemd 部署无需处理(WorkingDirectory 缺省为服务用户主目录)。

Q4:dump 导入时已有数据会怎样?

--import-dump 遇到 db-path 已有数据库会报错退出(防止误覆盖);确要覆盖先清空 data 目录,或加 --ignore-dump-if-db-exists 跳过导入保留旧库。dump 文件缺失时可用 --ignore-missing-dump 让启动不中断(两参数都必须与 --import-dump 成对使用)。

Q5:任务过滤参数写 type=dumpCreation 报错。

参数是复数 types(实测报错回显:Unknown parameter \type`: did you mean `types`?,并列出全部合法参数 limit/from/reverse/batchUids/uids/canceledBy/types/statuses/indexUids/afterEnqueuedAt 等)。同理状态过滤用 statuses=enqueued,processing,succeeded,failed`。

Q6:searchCutoffMs 设太小会有什么影响?

超时截断返回的是「当前已有的最佳结果」而非错误,设得过小(如 10ms)会让大查询频繁截断、结果质量下降;默认 1500ms,一般仅在明确需要严格 P99 上限时下调到业务预算。

Q7:如何安全轮换 master key?

master key 变更会使全部默认 API key 重新派生(key 值变化),已分发的自定义 key 亦失效。流程:新建/更新调用方配置 → 双 key 并行期 → 切换流量 → 更新 systemd/Compose 配置并重启实例。变更窗口内注意 dump 恢复命令里的 key 参数同步。

九、参考链接