本文目录导读:

- 最简单的 SQL LIKE 查询(适合数据量小)
- MySQL 全文索引(支持前缀)
- 使用 Trie 树(前缀树) - 内存方案
- 使用 Redis 有序集合(Sorted Set)
- 混合方案:MySQL + Redis 缓存
- 前端配合(AJAX 实时请求)
- 性能优化建议
在PHP项目中实现基于前缀匹配的搜索建议联想,通常有几种常见方案,下面按实现复杂度从低到高介绍。
最简单的 SQL LIKE 查询(适合数据量小)
// 用户输入前缀
$prefix = trim($_GET['q'] ?? '');
$prefix = mysqli_real_escape_string($conn, $prefix);
$sql = "SELECT DISTINCT keyword FROM suggestions
WHERE keyword LIKE '$prefix%'
ORDER BY LENGTH(keyword), keyword
LIMIT 10";
$result = mysqli_query($conn, $sql);
$suggestions = [];
while ($row = mysqli_fetch_assoc($result)) {
$suggestions[] = $row['keyword'];
}
echo json_encode($suggestions);
缺点:前缀通配符导致无法利用索引(除非有LIKE 'abc%'类型的索引),大数据量时性能差。
MySQL 全文索引(支持前缀)
-- 创建全文索引 ALTER TABLE suggestions ADD FULLTEXT INDEX ft_keyword (keyword);
$prefix = mysqli_real_escape_string($conn, $prefix);
// 使用布尔模式 + * 通配符实现前缀匹配
$sql = "SELECT DISTINCT keyword FROM suggestions
WHERE MATCH(keyword) AGAINST('$prefix*' IN BOOLEAN MODE)
LIMIT 10";
注意:MySQL 全文索引默认最小词长度是4(InnoDB),短词可能不生效。
使用 Trie 树(前缀树) - 内存方案
适合固定词库,启动时加载到内存:
class TrieNode {
public $children = [];
public $isEnd = false;
public $value = null;
}
class Trie {
private $root;
public function __construct() {
$this->root = new TrieNode();
}
public function insert($word) {
$node = $this->root;
for ($i = 0; $i < strlen($word); $i++) {
$char = $word[$i];
if (!isset($node->children[$char])) {
$node->children[$char] = new TrieNode();
}
$node = $node->children[$char];
}
$node->isEnd = true;
$node->value = $word;
}
public function searchPrefix($prefix) {
$node = $this->root;
for ($i = 0; $i < strlen($prefix); $i++) {
$char = $prefix[$i];
if (!isset($node->children[$char])) {
return [];
}
$node = $node->children[$char];
}
// 收集所有以该前缀开头的词
$results = [];
$this->collectWords($node, $results);
return $results;
}
private function collectWords($node, &$results) {
if ($node->isEnd) {
$results[] = $node->value;
}
foreach ($node->children as $char => $child) {
$this->collectWords($child, $results);
}
}
}
// 使用示例
$trie = new Trie();
// 从数据库加载所有关键词
$keywords = ['apple', 'application', 'appetite', 'banana', ...];
foreach ($keywords as $word) {
$trie->insert(strtolower($word));
}
$prefix = strtolower($_GET['q'] ?? '');
$suggestions = $trie->searchPrefix($prefix);
echo json_encode(array_slice($suggestions, 0, 10));
使用 Redis 有序集合(Sorted Set)
利用 Redis 的前缀搜索特性:
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 添加关键词(可以批量)
$keywords = ['apple', 'application', ...];
foreach ($keywords as $word) {
$redis->zAdd('autocomplete', 0, $word);
}
// 前缀搜索 - 使用 ZRANGEBYLEX
$prefix = strtolower($_GET['q'] ?? '');
$min = $prefix;
$max = $prefix . "\xff"; // 最大前缀值
$suggestions = $redis->zRangeByLex('autocomplete', "[$min", "[$max", 0, 10);
echo json_encode($suggestions);
优点:高性能,适合大量关键词。
混合方案:MySQL + Redis 缓存
将常用搜索词缓存到 Redis:
function getSuggestions($prefix) {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = "suggest:{$prefix}";
// 1. 先查 Redis 缓存
if ($redis->exists($cacheKey)) {
return json_decode($redis->get($cacheKey), true);
}
// 2. 缓存未命中,查 MySQL
$prefix = mysqli_real_escape_string($conn, $prefix);
$sql = "SELECT keyword FROM suggestions
WHERE keyword LIKE '$prefix%'
ORDER BY frequency DESC, LENGTH(keyword)
LIMIT 10";
$result = mysqli_query($conn, $sql);
$suggestions = [];
while ($row = mysqli_fetch_assoc($result)) {
$suggestions[] = $row['keyword'];
}
// 3. 写入缓存(有效期5分钟)
if (!empty($suggestions)) {
$redis->setex($cacheKey, 300, json_encode($suggestions));
}
return $suggestions;
}
前端配合(AJAX 实时请求)
// jQuery 示例
let debounceTimer;
$('#search-input').on('input', function() {
clearTimeout(debounceTimer);
const query = $(this).val().trim();
if (query.length < 2) {
$('#suggestions').hide();
return;
}
debounceTimer = setTimeout(() => {
$.ajax({
url: '/api/suggestions.php',
data: { q: query },
dataType: 'json',
success: function(data) {
let html = '';
data.forEach(item => {
// 高亮匹配部分
const highlighted = item.replace(
new RegExp('(' + query + ')', 'gi'),
'<strong>$1</strong>'
);
html += `<li>${highlighted}</li>`;
});
$('#suggestions').html(html).show();
}
});
}, 200); // 200ms 防抖
});
性能优化建议
| 方案 | 数据量 | 响应时间 | 实现复杂度 |
|---|---|---|---|
| SQL LIKE | < 1万 | 50-200ms | 低 |
| 全文索引 | 1-10万 | 10-50ms | 中 |
| Trie树 | 10-100万 | 1-10ms | 中高 |
| Redis Sorted Set | 100万+ | < 1ms | 中 |
| Elasticsearch | 千万级 | < 100ms | 高 |
推荐方案:
- 小型项目:SQL LIKE + 简单缓存
- 中型项目:Redis Sorted Set
- 大型项目:Elasticsearch 或专业的搜索服务
选择方案时要综合考虑数据量、并发量、维护成本和团队技术栈。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播