本文目录导读:

- 避免左模糊(最核心原则)
- 使用全文索引(适合大文本字段)
- 使用Elasticsearch(适合大数据量、复杂搜索)
- 前缀索引与复合索引优化
- 强制使用索引或查询提示
- 分区表(按范围分区)
- 代码层面优化
- 数据库配置优化
- 最佳实践选择
在PHP项目中进行模糊查询(特别是LIKE查询),优化索引的关键在于避免左模糊(如 LIKE '%keyword%')以及利用数据库特定的索引功能,以下是具体的优化策略和代码示例:
避免左模糊(最核心原则)
问题:LIKE '%keyword' 或 LIKE '%keyword%' 会导致全表扫描,因为索引无法从中间或末尾开始匹配。
解决方案:
- 右模糊:
LIKE 'keyword%'可以利用普通B+树索引。 - 完全匹配: 或
IN利用索引。
示例:
-- 可以利用索引(如果name上有索引) SELECT * FROM users WHERE name LIKE '张三%'; -- 无法利用索引(全表扫描) SELECT * FROM users WHERE name LIKE '%张三%';
使用全文索引(适合大文本字段)
对于长文本或需要搜索多个词的场景,使用全文索引(MySQL的FULLTEXT索引,PostgreSQL的GIN索引)。
MySQL全文索引示例:
-- 创建全文索引
ALTER TABLE articles ADD FULLTEXT INDEX idx_content (title, content);
-- 查询(比LIKE快很多)
SELECT * FROM articles
WHERE MATCH(title, content) AGAINST('关键词' IN BOOLEAN MODE);
注意:
- 全文索引支持自然语言搜索、布尔搜索等。
- 只支持MyISAM和InnoDB引擎(InnoDB从MySQL 5.6开始支持)。
使用Elasticsearch(适合大数据量、复杂搜索)
如果项目数据量大、需要高性能模糊搜索,建议集成Elasticsearch。
流程:
- 将数据同步到Elasticsearch。
- 使用Elasticsearch的
match_phrase_prefix或wildcard查询。 - PHP通过HTTP API查询ES。
PHP示例(使用elasticsearch-php库):
$client = ClientBuilder::create()->build();
$params = [
'index' => 'products',
'body' => [
'query' => [
'match_phrase_prefix' => [
'name' => '苹果'
]
]
]
];
$response = $client->search($params);
前缀索引与复合索引优化
1 前缀索引(节省索引空间,提高查询速度)
当字段值很长时(如URL、长文本),可以只索引前N个字符:
ALTER TABLE users ADD INDEX idx_name_prefix (name(10));
注意:只对右模糊有效,且索引选择性会下降。
2 复合索引(覆盖查询条件)
如果模糊查询常与其他字段组合,创建复合索引:
-- 假设常按 status + name 模糊搜索 ALTER TABLE users ADD INDEX idx_status_name (status, name); -- 查询 SELECT * FROM users WHERE status = 1 AND name LIKE '张三%';
强制使用索引或查询提示
如果MySQL优化器没有选择最优索引,可以使用FORCE INDEX:
SELECT * FROM users FORCE INDEX (idx_name) WHERE name LIKE '张%';
分区表(按范围分区)
当数据量极大(千万级+),可以按时间或其他条件分区,减少每次扫描的数据量:
CREATE TABLE orders (
id INT,
order_date DATE,
customer_name VARCHAR(50)
) PARTITION BY RANGE (YEAR(order_date)) (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025)
);
查询时加上分区条件:
SELECT * FROM orders WHERE order_date >= '2024-01-01' AND customer_name LIKE '李%';
代码层面优化
1 限制返回行数
$stmt = $pdo->prepare("SELECT * FROM users WHERE name LIKE :keyword LIMIT 20");
$stmt->execute([':keyword' => $keyword . '%']);
2 缓存结果
对不常变的数据,使用Redis缓存模糊查询结果:
$cacheKey = "search:" . md5($keyword);
$result = $redis->get($cacheKey);
if (!$result) {
// 执行数据库查询
$result = $db->query(...);
$redis->setex($cacheKey, 3600, serialize($result));
}
数据库配置优化
- 增加
innodb_buffer_pool_size:提高缓存命中率。 - 调整
query_cache_size(MySQL 5.7及之前):对重复查询有效(但不建议在8.0中使用)。 - 使用SSD硬盘:显著提高随机I/O性能。
最佳实践选择
| 场景 | 推荐方案 |
|---|---|
| 少量数据(<1万条) | 普通B+树索引 + 右模糊 |
| 中等数据(1万-100万) | 全文索引(FULLTEXT)或Elasticsearch |
| 大量数据(>100万) | Elasticsearch 或 分区表 + 全文索引 |
| 必须使用左模糊 | 必须使用Elasticsearch(或Sphinx) |
| 多字段组合查询 | 复合索引 + 右模糊 |
| 实时性要求高 | 优先使用数据库索引(全文/普通) |
核心建议:在PHP项目中,尽量不要直接使用LIKE '%keyword%',改为结合Elasticsearch或全文索引来实现模糊搜索功能,如果必须用MySQL,至少保证使用右模糊(LIKE 'keyword%')并创建合适的索引。