本文目录导读:

在PHP项目中实现搜索联想词(自动补全/搜索建议)通常有以下几种主流方案,我会从简单到复杂逐一介绍:
原生PHP + MySQL 简单实现(适合小项目)
数据库设计
-- 搜索日志表
CREATE TABLE search_history (
id INT AUTO_INCREMENT PRIMARY KEY,
keyword VARCHAR(100) NOT NULL,
search_count INT DEFAULT 1,
INDEX idx_keyword (keyword)
);
PHP搜索接口
<?php
// search_suggest.php
header('Content-Type: application/json; charset=utf-8');
$keyword = $_GET['keyword'] ?? '';
$keyword = trim($keyword);
if (mb_strlen($keyword) < 1) {
echo json_encode([]);
exit;
}
$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '');
$stmt = $db->prepare("SELECT DISTINCT keyword FROM search_history
WHERE keyword LIKE :keyword
ORDER BY search_count DESC
LIMIT 10");
$stmt->execute([':keyword' => $keyword . '%']);
$results = $stmt->fetchAll(PDO::FETCH_COLUMN);
echo json_encode($results);
前端实现
<input type="text" id="search" placeholder="输入搜索关键词" autocomplete="off">
<div id="suggestions"></div>
<script>
document.getElementById('search').addEventListener('input', function(e) {
const keyword = e.target.value;
if (keyword.length < 1) {
document.getElementById('suggestions').innerHTML = '';
return;
}
fetch(`search_suggest.php?keyword=${encodeURIComponent(keyword)}`)
.then(response => response.json())
.then(data => {
let html = '';
data.forEach(item => {
html += `<div class="suggestion-item">${item}</div>`;
});
document.getElementById('suggestions').innerHTML = html;
});
});
</script>
使用 Redis 缓存方案(推荐,性能更好)
数据存储结构
<?php
// 使用 Redis 有序集合存储热门搜索词
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->zIncrBy('hot_searches', 1, $keyword); // 搜索次数+1
// 存储联想词(使用集合)
$prefix = mb_substr($keyword, 0, 1);
$redis->sAdd("suggest:{$prefix}", $keyword); // 按首字分组存储
$redis->expire("suggest:{$prefix}", 86400 * 7); // 设置7天过期
联想词获取
<?php
function getSuggestions($keyword, $limit = 10) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$results = [];
$prefix = mb_substr($keyword, 0, 1);
// 先查询精确前缀匹配
$members = $redis->sMembers("suggest:{$prefix}");
foreach ($members as $word) {
if (strpos($word, $keyword) === 0) {
$results[] = $word;
}
if (count($results) >= $limit) break;
}
return $results;
}
使用全文搜索引擎(推荐大规模应用)
Elasticsearch 方案
<?php
require 'vendor/autoload.php';
use Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->build();
$params = [
'index' => 'search_suggest',
'body' => [
'suggest' => [
'keyword_suggest' => [
'prefix' => $_GET['keyword'],
'completion' => [
'field' => 'suggest',
'size' => 10
]
]
]
]
];
$response = $client->search($params);
$suggestions = [];
foreach ($response['suggest']['keyword_suggest'][0]['options'] as $option) {
$suggestions[] = $option['text'];
}
echo json_encode($suggestions);
数据索引创建
// 创建索引映射
$params = [
'index' => 'search_suggest',
'body' => [
'mappings' => [
'properties' => [
'suggest' => [
'type' => 'completion'
]
]
]
]
];
$client->indices()->create($params);
// 批量索引数据
$params = ['body' => []];
$keywords = ['PHP教程', 'PHP框架', 'JavaScript', 'Python入门'];
foreach ($keywords as $kw) {
$params['body'][] = [
'index' => ['_index' => 'search_suggest']
];
$params['body'][] = [
'suggest' => ['input' => $kw]
];
}
$client->bulk($params);
优化建议
防抖处理
// 前端添加防抖
let debounceTimer;
document.getElementById('search').addEventListener('input', function(e) {
clearTimeout(debounceTimer);
const keyword = e.target.value;
debounceTimer = setTimeout(() => {
fetchSuggestions(keyword);
}, 300); // 300ms延迟
});
结果缓存
// 使用文件缓存
$cacheKey = md5($keyword);
$cacheFile = "/tmp/suggest_{$cacheKey}.json";
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 300)) {
echo file_get_contents($cacheFile);
exit;
}
// 查询数据库...
// 保存缓存
file_put_contents($cacheFile, json_encode($results));
性能优化
- 限制返回数量:通常10-20条足够
- 使用索引:数据库LIKE查询要加索引
- 预热缓存:热门搜索词提前加载到缓存
- 定期清理:删除长时间未使用的搜索词
完整示例项目结构
search-suggest/
├── api/
│ └── suggest.php # 联想词API接口
├── admin/
│ └── manage.php # 后台管理搜索词
├── config/
│ └── database.php # 数据库配置
├── static/
│ ├── css/
│ │ └── style.css
│ └── js/
│ ├── suggest.js # 前端联想逻辑
│ └── app.js # 主应用
├── index.html # 搜索页面
└── README.md
选择建议
| 方案 | 数据量 | 实时性 | 实现难度 | 推荐场景 |
|---|---|---|---|---|
| MySQL LIKE | <10万条 | 高 | 低 | 小型网站 |
| Redis | <100万条 | 很高 | 中 | 中型项目 |
| Elasticsearch | 无限 | 高 | 高 | 大型搜索系统 |
最佳实践:建议使用 Redis + MySQL 组合方案,对热门词使用Redis缓存,冷门词直接查询MySQL,这样既保证了性能又节省了内存。