
Meilisearch 是一款专为开发者设计的开源搜索引擎。它能在毫秒内返回结果,优雅地处理错别字,并支持多维度筛选——全部运行在自托管的 VPS 上。本指南涵盖 Ubuntu 的完整安装、systemd 服务配置、Nginx 反向代理,以及与 PHP 和 Node.js 的集成。
为什么选择 Meilisearch 而非 SQL LIKE
- 速度 — 基于索引的查询,即使面对数百万条记录也能在 50 毫秒内响应
- 容错搜索 — 输入 "wirless" 也能找到 "wireless"
- 相关度排序 — 结果自动按相关度排序
- 多维筛选 — 无需复杂 SQL 即可按类别、价格、品牌筛选
- 高亮显示 — 开箱即用,自动标记匹配词条
系统要求:Meilisearch 会将整个索引加载到内存中。最低需要 1 GB 内存;生产环境建议 2 GB 或以上。配备 2 GB 以上内存的 VPS 是理想选择。
在 Ubuntu 上安装 Meilisearch
第一步:下载并安装
# Official installer
curl -L https://install.meilisearch.com | sh
# Move binary to PATH
sudo mv ./meilisearch /usr/local/bin/
# Verify
meilisearch --version
第二步:创建专用用户和数据目录
sudo useradd -r -s /bin/false meilisearch
sudo mkdir -p /var/lib/meilisearch/data
sudo chown -R meilisearch:meilisearch /var/lib/meilisearch
第三步:创建 systemd 服务
sudo nano /etc/systemd/system/meilisearch.service
[Unit]
Description=Meilisearch Search Engine
After=network.target
[Service]
User=meilisearch
Group=meilisearch
WorkingDirectory=/var/lib/meilisearch
ExecStart=/usr/local/bin/meilisearch \
--db-path /var/lib/meilisearch/data \
--http-addr 127.0.0.1:7700 \
--master-key YOUR_MASTER_KEY_HERE \
--env production
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
将 YOUR_MASTER_KEY_HERE 替换为强随机密钥(至少 16 个字符)。可使用以下命令生成:openssl rand -hex 32
第四步:启用并启动
sudo systemctl daemon-reload
sudo systemctl enable meilisearch
sudo systemctl start meilisearch
sudo systemctl status meilisearch
# Test the API
curl http://127.0.0.1:7700/health
Nginx 反向代理
通过添加 Nginx 服务器块,将 Meilisearch 通过 HTTPS 对外暴露:
server {
listen 443 ssl;
server_name search.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/search.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/search.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:7700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
使用 Node.js 索引文档
npm install meilisearch
const { MeiliSearch } = require('meilisearch');
const client = new MeiliSearch({
host: 'https://search.yourdomain.com',
apiKey: 'YOUR_MASTER_KEY_HERE'
});
const index = client.index('products');
// Add documents
await index.addDocuments([
{ id: 1, name: 'iPhone 15', category: 'smartphone', price: 32900 },
{ id: 2, name: 'Samsung Galaxy S24', category: 'smartphone', price: 28900 },
]);
// Search
const results = await index.search('iphone', {
limit: 10,
attributesToHighlight: ['name']
});
console.log(results.hits);
与 PHP 集成
composer require meilisearch/meilisearch-php
<?php
require 'vendor/autoload.php';
use Meilisearch\Client;
$client = new Client('https://search.yourdomain.com', 'YOUR_MASTER_KEY_HERE');
$index = $client->index('products');
// Index documents
$index->addDocuments([
['id' => 1, 'name' => 'Test Product', 'price' => 999],
]);
// Search
$results = $index->search('product');
foreach ($results->getHits() as $hit) {
echo $hit['name'] . PHP_EOL;
}
API 密钥安全:对于客户端 JavaScript,请创建仅用于搜索的 API 密钥,而非直接暴露主密钥。使用 POST /keys 配合 "actions":["search"] 生成权限受限的密钥。
调整排序规则与可过滤属性
Meilisearch 开箱即有合理的默认配置,但您可以针对每个索引自定义排序和过滤规则以匹配特定场景。在过滤查询中使用任何属性之前,必须先将其声明为可过滤属性——Meilisearch 需要为此构建专用结构以实现快速过滤。
声明可过滤和可排序属性
const index = client.index('products');
// Declare attributes available for filtering
await index.updateFilterableAttributes([
'category',
'price',
'brand',
'in_stock'
]);
// Declare attributes available for sorting
await index.updateSortableAttributes([
'price',
'created_at',
'rating'
]);
// Search with filter and sort
const results = await index.search('laptop', {
filter: 'category = "electronics" AND price < 30000 AND in_stock = true',
sort: ['price:asc'],
limit: 20
});
自定义排序规则
// View current ranking rules
await index.getRankingRules();
// Update ranking rules (append a custom rule)
await index.updateRankingRules([
'words',
'typo',
'proximity',
'attribute',
'sort',
'exactness',
'rating:desc' // Custom: boost higher-rated items
]);
监控、健康检查与备份
在生产环境中,监控和备份可保护搜索索引免遭意外数据丢失,并帮助您及早发现性能问题。
健康检查与统计接口
# Check Meilisearch health
curl -H 'Authorization: Bearer MASTER_KEY' \
http://127.0.0.1:7700/health
# Global stats (all indexes)
curl -H 'Authorization: Bearer MASTER_KEY' \
http://127.0.0.1:7700/stats
# Stats for a specific index
curl -H 'Authorization: Bearer MASTER_KEY' \
http://127.0.0.1:7700/indexes/products/stats
创建备份转储文件
# Trigger a dump via the API
curl -X POST 'http://127.0.0.1:7700/dumps' \
-H 'Authorization: Bearer MASTER_KEY'
# Dump files land in:
ls /var/lib/meilisearch/data/dumps/
# Alternatively, rsync the entire data directory
sudo rsync -av /var/lib/meilisearch/data/ /backup/meilisearch/
使用 Cron 自动化每日备份
sudo crontab -e
# Back up every day at 02:00
0 2 * * * rsync -a /var/lib/meilisearch/data/ /backup/meilisearch/$(date +\%Y-\%m-\%d)/
| API 接口 | 方法 | 用途 |
|---|---|---|
/health |
GET | 服务健康状态 |
/stats |
GET | 索引统计与磁盘用量 |
/dumps |
POST | 创建完整数据转储 |
/indexes/{uid}/documents |
DELETE | 删除索引中的所有文档 |
容错设置与停用词
容错搜索是 Meilisearch 的旗舰功能之一。您可以自定义其纠正拼写错误的力度,并定义停用词——如 "the"、"and"、"in" 等无搜索意义的词——在文档匹配时忽略这些词。
为每个索引配置容错设置
await index.updateTypoTolerance({
enabled: true,
minWordSizeForTypos: {
oneTypo: 5, // allow 1 typo in words of 5+ chars
twoTypos: 9 // allow 2 typos in words of 9+ chars
},
disableOnWords: ['iphone', 'samsung'], // brand names must match exactly
disableOnAttributes: ['serial_number', 'sku']
});
定义停用词
await index.updateStopWords([
'the', 'a', 'an', 'is', 'in', 'on', 'at', 'for', 'with',
'and', 'or', 'of', 'to', 'from', 'by'
]);
// Inspect the current stop words
await index.getStopWords();
停用词可减小索引体积并提升相关度——当 Meilisearch 忽略填充词时,有意义的词条在排序公式中获得更大权重。对于用户以自然语言短语搜索的商品目录而言,这尤为重要。
即时搜索与前端实时搜索
即时搜索——用户每输入一个字符即显示结果——正是 Meilisearch 最闪耀的场景。低于 50 毫秒的响应时间让 UI 即便在普通 VPS 上也能给人即时感。关键在于将防抖输入监听器与仅供搜索的 API 密钥相结合,并暴露给浏览器。
原生 JavaScript 即时搜索
<input type="text" id="searchBox" placeholder="Search products...">
<div id="searchResults"></div>
<script>
const searchBox = document.getElementById('searchBox');
const resultsDiv = document.getElementById('searchResults');
let debounceTimer;
searchBox.addEventListener('input', function() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const query = this.value.trim();
if (query.length < 2) { resultsDiv.innerHTML = ''; return; }
const res = await fetch(
'https://search.yourdomain.com/indexes/products/search',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer SEARCH_ONLY_API_KEY'
},
body: JSON.stringify({
q: query,
limit: 8,
attributesToHighlight: ['name', 'description'],
highlightPreTag: '<mark>',
highlightPostTag: '</mark>'
})
}
);
const data = await res.json();
resultsDiv.innerHTML = data.hits.map(hit => `
<div class="result-item">
<strong>${hit._formatted?.name || hit.name}</strong>
<span>$${hit.price.toLocaleString()}</span>
</div>
`).join('');
}, 200); // 200ms debounce
});
</script>
200 毫秒的防抖可防止每次按键都发出请求。响应中的 _formatted 字段包含每个匹配属性的高亮版本,可直接在浏览器中渲染。
为即时搜索配置 Nginx CORS 头
# In your Meilisearch Nginx server block
add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
location / {
if ($request_method = 'OPTIONS') { return 204; }
proxy_pass http://127.0.0.1:7700;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
性能建议:如果 Meilisearch 与您的 Web 应用共用同一台 VPS,建议通过 PHP 后端接口转发搜索请求,而不是在 JavaScript 中直接调用 Meilisearch。这样既能隐藏 API 密钥,又能完全消除 CORS 配置需求。
更新与删除文档
Meilisearch 的文档管理 API 简单直观。所有写操作(添加、更新、删除)均为异步执行——API 立即返回任务 ID,Meilisearch 在后台处理变更。这种设计即便在索引大批量数据时也能保持 API 的高速响应。
更新与删除操作
// Update one or more documents (partial update supported)
await index.updateDocuments([
{ id: 1, name: 'iPhone 15 Pro', price: 42900 }
]);
// Delete a single document by its primary key
await index.deleteDocument(1);
// Delete multiple documents matching a filter
await index.deleteDocuments({ filter: 'price < 1000' });
// Check the status of an async task
const task = await index.addDocuments([...]);
const status = await client.getTask(task.taskUid);
console.log(status.status); // 'enqueued' | 'processing' | 'succeeded' | 'failed'
查看任务队列
# List all tasks
curl -H 'Authorization: Bearer MASTER_KEY' \
'http://127.0.0.1:7700/tasks'
# Get a specific task by ID
curl -H 'Authorization: Bearer MASTER_KEY' \
'http://127.0.0.1:7700/tasks/TASK_ID'
对于大量导入,请持续轮询任务接口,直到状态变为 succeeded,再运行首次搜索查询——文档在索引完成前不可被搜索到。
多索引搜索与联合查询
多索引搜索允许您在单个 HTTP 请求中查询多个索引。这非常适合同时展示商品、文章和分类的全局搜索框——将三次请求减少为一次。
// Search across multiple indexes in one call
const results = await client.multiSearch({
queries: [
{ indexUid: 'products', q: 'iphone', limit: 5 },
{ indexUid: 'articles', q: 'iphone', limit: 3 },
{ indexUid: 'categories', q: 'smartphone', limit: 3 }
]
});
console.log(results.results[0].hits); // products
console.log(results.results[1].hits); // articles
多索引搜索特别适用于按类型分组显示结果的即时搜索下拉框——商品在上、文章居中、分类在下——仅凭一个运行在您 VPS 上的自托管引擎,即可为用户带来类似 Google 的搜索体验。
Meilisearch vs Elasticsearch vs Typesense
为 VPS 选择自托管搜索引擎时,三款开源选项最为主流。各自的优势因项目规模和技术复杂度而有所不同。
| 特性 | Meilisearch | Elasticsearch | Typesense |
|---|---|---|---|
| 安装难度 | 低——单一二进制文件 | 高——需要 Java,配置复杂 | 低——与 Meilisearch 类似 |
| 最低内存 | 1 GB | 2–4 GB | 1 GB |
| 容错搜索 | 出色(内置) | 需手动配置 | 良好(内置) |
| 最适合 | 电商、博客、中型应用 | 日志分析、企业级应用 | 电商、SaaS |
对于正在启动新项目或运行中型应用的开发者,Meilisearch 在简洁性、速度和低资源占用之间提供了最佳平衡。Elasticsearch 则是需要企业级强大日志聚合与分析能力的团队的正确选择。
需要 VPS 部署搜索引擎?
Linux VPS 每月起价 500 泰铢,提供完整 Root 权限与 SSD 存储——运行 Meilisearch、Elasticsearch 或任何您选择的搜索方案。
查看 VPS 套餐