MeiliSearch 1.55.0 进阶实战与运维
MeiliSearch 1.55.0 进阶实战:searchCutoffMs、停用词与同义词、多索引管理、API key 权限体系、dump 备份与跨实例恢复迁移的完整运维闭环
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→…),可增删排序规则与权重 中(规则顺序/自定义规则,非评分公式) Elasticsearch BM25 评分公式,可整体替换(如 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 提供两种备份机制:
| 机制 | 触发方式 | 内容 | 用途 |
|---|---|---|---|
| dump | API 手动(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 的标准升级流程:
- 升级前备份:
POST /dumps导出(见 5.2); - 替换二进制为新高版本(见《MeiliSearch 1.55.0 Ubuntu 22.04 安装指南》第九章);
- 直接重启启动:相邻版本的数据库文件可原地打开;若新版本提示数据库格式需要升级,使用
--upgrade-db参数(1.55.0 实测存在的参数,配合启动执行原地数据库格式升级); - 跨大版本(官方提示不保证兼容)时:旧版本导出 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 参数同步。
九、参考链接
- Settings API 参考:https://www.meilisearch.com/docs/reference/api/settings
- Keys API 参考:https://www.meilisearch.com/docs/reference/api/keys
- Dump 与快照:https://www.meilisearch.com/docs/learn/self_hosted/dumps 与 https://www.meilisearch.com/docs/learn/self_hosted/snapshots
- 升级指南:https://www.meilisearch.com/docs/learn/update_and_migration/updating
- 相关文档:《MeiliSearch 1.55.0 Ubuntu 22.04 安装指南》《MeiliSearch 1.55.0 配置参数详解》《MeiliSearch 1.55.0 快速入门与集成示例》