php搜索引擎

 人参与 | 时间:2026-10-17 07:08:09

PHP写搜索引擎?搜索引很多人第一反应是嗤笑。但别急着关页面——十年前我用PHP给一家本地建材市场做过一套站内搜索,搜索引硬是搜索引扛住了每天二十万次查询,直到三年后他们换了Java团队才被重构。搜索引那套系统核心代码不到两千行,搜索引用的搜索引还是最土的MySQL全文索引。

真正的搜索引关键是词法分析。PHP的搜索引字符串函数强大得离谱,但没人告诉你分词才是搜索引中文搜索的命门。我试过SCWS扩展,搜索引准确率还行,搜索引但部署麻烦;后来干脆自己写了个基于词典的搜索引二元切分——把用户输入的句子按标点、虚词、搜索引常见前缀后缀切成两字词块。搜索引比如“实木地板”,搜索引切出来是“实木”和“地板”,查数据库时用LIKE '%实木%地板%',配合MySQL的布尔全文模式,匹配速度比想象中快得多。代价是偶尔会切出“实木地”这种怪胎,但用户根本不会注意到,因为搜索结果页会把关键词高亮,而“实木地”恰好能被“实木地板”包含。

索引表结构必须单独设计。别指望直接在商品表上跑LIKE,那会拖垮数据库。我建了三张表:词表、文档表、倒排表。词表存每个词和它的ID,文档表存商品内容压缩后的摘要,倒排表存词ID和商品ID的映射关系。插入商品时用PHP的preg_split把标题和描述拆成词,然后批量更新倒排表。查询时先查词表拿到词ID,再查倒排表拿到商品ID集合,最后用IN()去取商品详情——这一步用PHP的array_intersect做交集,比MySQL的JOIN在数据量小的时候快一个数量级。

有个坑必须说:PHP的数组在内存里是哈希表,所以倒排表如果全塞进数组会爆内存。我的解决办法是每次查询只取前五百个词ID对应的商品ID,然后用array_slice截断。用户翻页超过十页就提示“缩小关键词范围”,实际上百分之九十的用户只看前两页。这个设计被同事骂“偷懒”,但后来看日志,搜索跳出率反而降了——因为结果太精准了,用户懒得翻页。

缓存层用文件就够了。PHP的APCu或者Redis当然更好,但小项目没必要。我用的是一张MySQL的cache表,存md5后的查询语句、结果JSON和过期时间。每次搜索先查cache表,命中就直接返回;没命中就执行查询,把结果序列化存进去。注意别存原始SQL,要存处理后的词ID列表和排序规则。这套方案在并发一百的时候响应时间稳定在两百毫秒左右,比用APCu慢不了多少,但省了维护内存的麻烦。

排序逻辑得动点脑筋。默认按时间倒序太蠢,我加了热度因子:商品浏览量除以一百加上发布时间的时间戳。SQL里用ORDER BY (views/100 + UNIX_TIMESTAMP(created_at)) DESC,效果出奇地好——新品和老爆款混合出现,用户点得开心,商家也满意。后来我甚至加了随机因子,让同一个关键词两次搜索的结果顺序略有不同,避免用户觉得“每次都一样”而无聊。

最逗的是,这套系统上线后,建材市场的老板专门跑来问我:“这搜索是不是会读心术?我昨天搜‘防滑砖’,今天搜‘浴室地砖’,出来的结果居然有重叠。”我解释了半天倒排表和词权重,他似懂非懂,但从此逢人就夸技术厉害。其实底层逻辑简单得很——浴室地砖本来就会包含“防滑”这个属性,只是我建索引时把商品属性表的字段也加进文档内容里了。

PHP的搜索性能瓶颈从来不在语言本身,而在写代码的人有没有把数据结构想透。别迷信Elasticsearch,也别觉得PHP就该被淘汰。如果你的网站数据量在百万级以下,PHP加MySQL完全能撑起一个让用户满意的搜索引擎。关键在于分词策略、索引设计和缓存机制——这三样做好了,剩下的都是体力活。哦对了,记得把PHP的memory_limit调高一点,否则处理大结果集时会被杀进程。别问我怎么知道的。

顶: 155踩: 6