PHP全文检索需先选对方案:中小项目用MySQL FULLTEXT索引(引擎为InnoDB、字段为TEXT/VARCHAR),大型项目用Elasticsearch;建索引要合规,搜索需过滤停用词、调优分词、预处理文本,避免因配置错误或数据脏导致查不到结果。

直接上手学 PHP 全文检索,别从“原理”开始。你遇到的绝大多数问题,其实是选错方案、没建对索引、或者关键词一输就查不到——先解决这些,再谈优化。
MySQL FULLTEXT 索引建不起来?检查这几点
FULLTEXT 不是加个关键字就能用的,它对表结构和数据类型有硬性要求:
- 存储引擎必须是
InnoDB(5.6+)或 MyISAM,MEMORY 或 ARCHIVE 不支持
- 字段类型只能是
CHAR、VARCHAR、TEXT;TINYTEXT 可以,但 ENUM、JSON 不行
- 建索引语句要写在
CREATE TABLE 里,或者用 ALTER TABLE ... ADD FULLTEXT,不能用 CREATE INDEX
- 中文搜索默认效果差,因为 MySQL 内置分词器按空白和标点切词,不是按字或词切——“数据库优化”会被当成一个词,搜“数据库”就匹配不上
常见错误现象:
ERROR 1214 (HY000): The used table type doesn't support FULLTEXT indexes 就是引擎不对;
Warning: #1287 'FULLTEXT' is deprecated 是用了老版本 MyISAM 的布尔模式语法。
用 match() against() 搜不到结果?重点看参数和数据
MATCH(title, body) AGAINST(:term IN NATURAL LANGUAGE MODE) 看似简单,但实际容易卡在细节:
- 自然语言模式下,MySQL 会自动过滤停用词(如“的”“和”“在”),且词长小于 4 的词默认不索引(可通过
ft_min_word_len 调整,但需重启 MySQL)
- 搜索词如果全是停用词,比如搜“PHP 的使用”,可能整个查询返回空——不是代码错,是 MySQL 直接跳过匹配
-
IN BOOLEAN MODE 支持 +、-、* 等操作符,但字段值里含 + 符号时(如标题含“C++教程”),需转义或改用自然语言模式
- 相关性得分(
relevance)是浮点数,值可能非常小(如 0.0002),排序时容易被忽略,建议显式加上 ORDER BY MATCH(...) AGAINST(...) DESC
示例:想让“php”“mysql”都作为独立词索引,又支持短词,得在 my.cnf 里设
ft_min_word_len = 2,然后重建索引,不是改完配置就生效。
文件级搜索用 file() + strpos(),为什么一搜大文件就卡死?
这种写法适合几百 KB 以内的文本,比如配置文件或日志片段:
-
file() 会把整个文件读进内存,10MB 文件 ≈ 占用 10MB PHP 内存,超限直接 Fatal error: Allowed memory size exhausted
-
strpos() 是逐行匹配,无法处理跨行关键词(如关键词换行断开),也不支持模糊或近义匹配
- 用户输入没过滤就拼进
exec("grep '$kw' file.txt"),等于敞开命令注入大门——$kw = "test; rm -rf /" 就能跑通
真要处理单文件搜索,更稳妥的是用
fopen() +
fgets() 流式读取,每行只存一行内容,内存可控;若需高亮或正则,用
preg_match() 替代
strpos(),但记得加
PREG_OFFSET_CAPTURE 获取位置。
Elasticsearch 客户端连不上?先确认三件事
PHP 调 Elasticsearch 报
cURL error 7: Failed to connect 或空响应,90% 是环境链路问题:
- PHP 进程能否访问 ES 地址?用
curl -X GET http://localhost:9200 在 CLI 下手动测,别只信浏览器能打开
- ES 是否监听了外部地址?默认只绑
127.0.0.1,Docker 或远程部署时需改 network.host: 0.0.0.0 并开防火墙端口
- PHP 客户端是否用了过期的 API?
elasticsearch/elasticsearch v8.x 已移除 type 参数,还写 'type' => 'doc' 就会 400 错误
中文分词不是装个 IK 插件就自动生效的——你得在创建索引时显式指定
analyzer,比如:
"mappings": {
"properties": {
"title": { "type": "text", "analyzer": "ik_smart" }
}
}
否则还是走默认的 standard 分析器,照样按字切。
全文检索最常被忽略的一点:**没有预处理就没有好结果**。不管是 MySQL 还是 ES,原始文本里的 HTML 标签、多余空格、全角标点、乱码字符,都会污染分词和匹配。上线前至少做一次
strip_tags() +
mb_convert_kana()(转全角为半角)+
preg_replace('/\s+/', ' ', $text) 清洗,比调参有用得多。