学完这篇你能得到什么:能判断自己的 PHP 站点该用 MySQL FULLTEXT 还是 Elasticsearch,并且照着把两种方案的最小可用版本跑起来。
第一步:先搞清两者的本质差别
| 维度 | MySQL FULLTEXT | Elasticsearch |
|---|
| 额外部署 | 无,跟着数据库走 | 单独服务,吃 1-2G 内存起 |
| 数据一致性 | 天然一致,写入即搜 | 需同步,天然延迟 |
| 中文支持 | 需 ngram 分词器(5.7.6+ 内置) | 装 IK 分词器,效果好得多 |
| 相关度排序 | 有,但粗糙 | BM25,可加权、可自定义 |
| 高亮/纠错/拼音/同义词 | 基本没有 | 全都有 |
| 适合规模 | 十万级帖子以内 | 百万级、复杂检索 |
一句话结论:起步阶段用 FULLTEXT,搜索成为核心功能后再上 Elasticsearch。Clara BBS 这类无框架 PHP 系统环境要求是 PHP 7.4-8.5 + MySQL 5.7+、无需 Composer,FULLTEXT 完全够用,且零额外运维。
第二步:MySQL FULLTEXT 最小实现
中文必须先指定 ngram 分词器,否则整段中文会被当成一个词,搜不到:
ALTER TABLE posts
ADD FULLTEXT ft_post (title, content) WITH PARSER ngram;
ngram_token_size 默认是 2(即按两字切分),改它需要改 my.cnf 后重启 MySQL,且在建索引之前决定好。
查询写法:
SELECT id, title,
MATCH(title, content) AGAINST ('缓存穿透' IN BOOLEAN MODE) AS score
FROM posts
WHERE MATCH(title, content) AGAINST ('缓存穿透' IN BOOLEAN MODE)
ORDER BY score DESC
LIMIT 20;
PHP 侧就是普通预处理查询,把关键词绑定进去即可。
注意:WHERE 里的 MATCH ... AGAINST 必须和索引列、顺序完全一致,否则 MySQL 直接报错或退化成全表扫描。SELECT 里的那个 MATCH 只是算分,不重复写一遍 WHERE 就用不上索引。
注意:BOOLEAN MODE 下 +词 表示必须包含、-词 表示排除、"短语" 表示精确短语。不加符号时多词是「或」关系,很容易搜出一堆不相关内容。
第三步:Elasticsearch 最小实现
装好 ES 和 IK 分词器后建索引,analyzer 必须指定 ik_max_word:
PUT /bbs
{
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_max_word", "boost": 2 },
"content": { "type": "text", "analyzer": "ik_max_word" },
"created_at": { "type": "date" }
}
}
}
如果你的 PHP 环境不能装 Composer(Clara BBS 就是这种情况),别硬上官方客户端,直接用 cURL 打 REST 接口就够了:
$ch = curl_init('http://127.0.0.1:9200/bbs/_search');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'query' => ['multi_match' => [
'query' => $kw,
'fields' => ['title^2', 'content'],
]],
'highlight' => ['fields' => ['content' => new stdClass]],
]),
]);
$res = json_decode(curl_exec($ch), true);
数据同步是真正的成本所在:发帖、改帖、删帖都要写一次索引。稳妥做法是「主流程写库 + 异步补索引」,用计划任务做增量同步(Clara BBS 内置 Cron::register 注册任务、前台懒触发,配一条系统 crontab 每分钟调用 /cron 即可),比在业务代码里同步调 ES 安全——ES 挂了不影响发帖。
注意:千万别在用户发帖的主流程里同步请求 ES。ES 抖动或重启时,请求会卡住几秒,直接把发帖体验拖垮。写库成功就该返回。
第四步:怎么选
按这几条对号入座:
- 帖子数在十万以内、搜索不是核心卖点 → FULLTEXT,零运维,写完就完事
- 需要「搜索后按版块/时间/作者多条件组合筛选 + 排序」 → Elasticsearch,MySQL 做这种组合查询会很难受
- 需要搜索高亮、拼音容错、同义词扩展 → Elasticsearch
- 服务器只有 2G 内存的小机器 → 别装 ES,JVM 就把内存吃干了一半
- 团队没人运维过 ES → 先用 FULLTEXT,等真扛不住再迁移,索引重建随时可做
折中方案:FULLTEXT 负责日常搜索,把搜索日志(Clara BBS 有搜索日志表)留着,等看到「搜不到结果」的比例居高不下,就是该迁移的信号。
小结
- 中文场景下 MySQL 必须用
WITH PARSER ngram 建 FULLTEXT 索引,否则搜不到
- 查询时
WHERE 子句里必须重复写 MATCH ... AGAINST 才走索引
- Elasticsearch 的优势在分词质量、多条件组合和相关性排序,代价是部署与数据同步
- 无 Composer 环境可以用 cURL 直连 ES REST 接口,不必强上官方 SDK
- 索引同步务必异步化,用计划任务补,不要卡在发帖主流程里