本文目录导读:

- MySQL LIKE 模糊搜索(小型项目)
- MySQL FULLTEXT 全文索引(中等规模)
- Elasticsearch 专业搜索引擎(大型项目)
- Sphinx 搜索引擎(中等规模高性能)
- 中文分词处理
- 性能优化建议
- 完整前端搜索交互
- 选择推荐
在PHP项目中实现搜索功能,根据项目规模和需求的不同,有多种实现方案,以下从简单到复杂,介绍几种主流方法。
MySQL LIKE 模糊搜索(小型项目)
最基础的方式,适合数据量小(万级以内)的场景。
// 搜索逻辑
function searchUsers($keyword) {
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$keyword = '%' . $keyword . '%';
// 使用预处理语句防止SQL注入
$stmt = $pdo->prepare("SELECT * FROM users
WHERE name LIKE :keyword
OR email LIKE :keyword
ORDER BY id DESC
LIMIT 20");
$stmt->execute([':keyword' => $keyword]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// 调用示例
$results = searchUsers($_GET['q'] ?? '');
优点:实现简单,无需额外服务
缺点:性能差(全表扫描),不支持中文分词,不支持模糊排序
MySQL FULLTEXT 全文索引(中等规模)
适合10万级数据量,需要精确匹配关键字的场景。
-- 先创建全文索引 ALTER TABLE articles ADD FULLTEXT INDEX idx_content (title, content);
function searchArticles($keyword) {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '');
// BOOLEAN MODE 支持 +(必须包含) -(排除) *(通配符)
$sql = "SELECT *, MATCH(title, content) AGAINST(:keyword IN BOOLEAN MODE) AS relevance
FROM articles
WHERE MATCH(title, content) AGAINST(:keyword IN BOOLEAN MODE)
ORDER BY relevance DESC
LIMIT 20";
$stmt = $pdo->prepare($sql);
$stmt->execute([':keyword' => $keyword . '*']); // 通配符允许部分匹配
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
注意:
- 需要 MySQL 5.6+ 支持中文全文索引(ngram)
- 创建表时指定:
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 - 配置最小词长:
ft_min_word_len=1(中文)
Elasticsearch 专业搜索引擎(大型项目)
适合百万级以上数据,需要复杂搜索、聚合分析、实时搜索的场景。
环境准备
# 安装 Elasticsearch(需要Java环境) wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.0-linux-x86_64.tar.gz tar -xzf elasticsearch-8.11.0-linux-x86_64.tar.gz cd elasticsearch-8.11.0/bin ./elasticsearch # 安装 PHP Elasticsearch 客户端 composer require elasticsearch/elasticsearch
使用示例
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Elasticsearch\ClientBuilder;
class SearchService {
private $client;
public function __construct() {
$this->client = ClientBuilder::create()
->setHosts(['http://localhost:9200'])
->build();
}
// 索引文档(将数据存入ES)
public function indexArticle($id, $title, $content) {
$params = [
'index' => 'articles',
'id' => $id,
'body' => [
'title' => $title,
'content' => $content,
'created_at' => date('Y-m-d H:i:s')
]
];
return $this->client->index($params);
}
// 搜索文档
public function search($keyword, $page = 1, $size = 20) {
$params = [
'index' => 'articles',
'body' => [
'query' => [
'bool' => [
'should' => [
['match' => ['title' => ['query' => $keyword, 'boost' => 3]]],
['match' => ['content' => $keyword]]
]
]
],
'highlight' => [
'fields' => [
'title' => ['pre_tags' => ['<em>'], 'post_tags' => ['</em>']],
'content' => ['fragment_size' => 100, 'number_of_fragments' => 3]
]
],
'from' => ($page - 1) * $size,
'size' => $size,
'sort' => ['_score' => 'desc']
]
];
$response = $this->client->search($params);
// 格式化结果
$results = [];
foreach ($response['hits']['hits'] as $hit) {
$source = $hit['_source'];
$highlight = $hit['highlight'] ?? [];
$results[] = [
'id' => $hit['_id'],
'title' => $highlight['title'][0] ?? $source['title'],
'content' => implode('...', $highlight['content'] ?? [$source['content']]),
'score' => $hit['_score']
];
}
return [
'total' => $response['hits']['total']['value'],
'results' => $results,
'page' => $page,
'size' => $size
];
}
}
// 使用示例
$searchService = new SearchService();
// 索引数据
$searchService->indexArticle(1, 'PHP搜索功能实现', '本文介绍如何在PHP中实现高效搜索...');
// 搜索
$result = $searchService->search('PHP搜索');
echo "共找到 {$result['total']} 条结果:";
foreach ($result['results'] as $item) {
echo "标题:{$item['title']}\n";
echo "{$item['content']}\n\n";
}
Sphinx 搜索引擎(中等规模高性能)
适合需要高性能全文搜索但不想引入ES复杂性的场景。
// 安装 Sphinx PHP 扩展
// pecl install sphinx
$sphinx = new SphinxClient();
$sphinx->setServer('localhost', 9312);
$sphinx->setMatchMode(SPH_MATCH_EXTENDED2);
$sphinx->setLimits(0, 20);
$sphinx->setSortMode(SPH_SORT_RELEVANCE);
// 设置筛选条件
$sphinx->setFilter('category_id', [1, 2, 3]);
$sphinx->setFilterRange('price', 100, 500);
// 执行搜索
$result = $sphinx->query('PHP搜索', 'articles');
if ($result && $result['total'] > 0) {
// 从 MySQL 获取完整记录
$ids = array_keys($result['matches']);
$stmt = $pdo->prepare("SELECT * FROM articles WHERE id IN (" . implode(',', $ids) . ")");
$stmt->execute();
$articles = $stmt->fetchAll();
}
中文分词处理
对于中文搜索,分词至关重要:
使用 SCWS(简易中文分词系统)
// 安装 scws 扩展
// pecl install scws
$sh = scws_open();
scws_set_charset($sh, 'utf8');
scws_set_dict($sh, '/usr/local/scws/etc/dict.utf8.xdb');
scws_set_rule($sh, '/usr/local/scws/etc/rules.utf8.ini');
$text = '这是一个中文搜索测试';
scws_send_text($sh, $text);
$words = [];
while ($tmp = scws_get_result($sh)) {
foreach ($tmp as $word) {
$words[] = $word['word'];
}
}
scws_close($sh);
print_r($words);
// 输出: ['这是', '一个', '中文', '搜索', '测试']
集成到搜索中
function tokenizeSearch($keyword) {
$tokens = tokenize($keyword); // 自定义分词函数
// 构建布尔查询
$conditions = [];
$params = [];
foreach ($tokens as $i => $token) {
$conditions[] = "title LIKE :search{$i} OR content LIKE :search{$i}";
$params[":search{$i}"] = "%{$token}%";
}
$where = implode(' AND ', $conditions); // AND表示必须所有词都出现
$sql = "SELECT * FROM articles WHERE {$where} ORDER BY relevance DESC LIMIT 20";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
性能优化建议
缓存策略
// 1. 查询结果缓存(Redis/Memcached)
$cacheKey = 'search:' . md5($keyword . $page);
$cached = $redis->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
$results = performSearch($keyword);
$redis->setex($cacheKey, 300, json_encode($results)); // 缓存5分钟
// 2. 热门搜索缓存
$redis->zIncrBy('hot_queries', 1, $keyword);
// 3. 搜索建议缓存
$suggestions = $redis->get('suggestions');
if (!$suggestions) {
$suggestions = buildAutocompleteIndex();
$redis->setex('suggestions', 3600, $suggestions);
}
数据库优化
-- 1. 添加复合索引 ALTER TABLE articles ADD INDEX idx_search (title, created_at); -- 2. 使用覆盖索引 SELECT id, LEFT(title, 100) as title_fragment FROM articles WHERE title LIKE '%keyword%'; -- 3. 分表策略(按时间) CREATE TABLE articles_2024_01 ...; CREATE TABLE articles_2024_02 ...;
完整前端搜索交互
<!-- HTML 搜索框 -->
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索..." autocomplete="off">
<div id="searchResults" class="dropdown-menu"></div>
</div>
<script>
// 防抖搜索
const debounce = (fn, delay = 300) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
};
document.getElementById('searchInput').addEventListener('input', debounce(async (e) => {
const query = e.target.value.trim();
if (query.length < 2) {
document.getElementById('searchResults').innerHTML = '';
return;
}
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
const data = await response.json();
renderResults(data);
} catch (error) {
console.error('搜索失败:', error);
}
}));
function renderResults(results) {
const container = document.getElementById('searchResults');
if (results.length === 0) {
container.innerHTML = '<div class="no-results">未找到相关结果</div>';
return;
}
container.innerHTML = results.map(item => `
<a href="/article/${item.id}" class="search-result-item">
<div class="result-title">${item.highlightTitle || item.title}</div>
<div class="result-snippet">${item.snippet}</div>
</a>
`).join('');
}
</script>
选择推荐
| 方案 | 适合场景 | 数据量 | 实现难度 |
|---|---|---|---|
| LIKE | 简单CMS、后台管理 | <1万 | |
| FULLTEXT | 博客、论坛 | <10万 | |
| Sphinx | 电商、垂直搜索 | <100万 | |
| Elasticsearch | 综合搜索、日志分析 | >100万 |
建议:
- 小项目从 MySQL FULLTEXT 起步
- 成长型项目直接上 Elasticsearch(后续不需要迁移)
- 始终做好 SQL 注入防护和 XSS 过滤
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播