本文目录导读:

针对Java模糊查询(LIKE)的优化,以下是几种有效的提速方案,按推荐优先级排序:
数据库索引优化(最基础)
前缀模糊查询
-- 使用普通B-Tree索引即可 CREATE INDEX idx_name ON users(name); -- 查询必须使用前缀匹配 SELECT * FROM users WHERE name LIKE '张%';
全文索引(推荐)
// MySQL全文索引查询 @Query(value = "SELECT * FROM articles WHERE MATCH(title, content) AGAINST(?1 IN BOOLEAN MODE)", nativeQuery = true) List<Article> searchByFullText(String keyword);
-- 创建全文索引
CREATE FULLTEXT INDEX idx_fulltext ON articles(title, content);
-- 查询语法
SELECT * FROM articles WHERE MATCH(title, content) AGAINST('+关键词*' IN BOOLEAN MODE);
Elasticsearch搜索引擎(高性能方案)
基础配置
// 1. 添加依赖
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
// 2. 实体映射
@Document(indexName = "users")
public class UserDocument {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String name;
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String description;
}
// 3. Repository
public interface UserSearchRepository extends ElasticsearchRepository<UserDocument, Long> {
List<UserDocument> findByNameContaining(String keyword);
}
// 4. 服务层查询
@Service
public class SearchService {
@Autowired
private UserSearchRepository searchRepository;
public List<UserDocument> searchByName(String keyword) {
return searchRepository.findByNameContaining(keyword);
}
}
高级查询(支持模糊、高亮)
public class ElasticsearchService {
public List<UserDocument> fuzzySearch(String keyword) {
// 构建模糊查询
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 模糊查询
queryBuilder.withQuery(QueryBuilders.fuzzyQuery("name", keyword)
.fuzziness(Fuzziness.AUTO));
// 或通配符查询
queryBuilder.withQuery(QueryBuilders.wildcardQuery("name", "*" + keyword + "*"));
// 高亮显示
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("name");
queryBuilder.withHighlightBuilder(highlightBuilder);
return elasticsearchTemplate.queryForPage(queryBuilder.build(), UserDocument.class).getContent();
}
}
分库分表+中间表策略
创建搜索专用表
-- 1. 创建搜索中间表
CREATE TABLE user_search_index (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
search_text VARCHAR(500),
INDEX idx_search_text(search_text)
) ENGINE=InnoDB;
-- 2. 数据同步(定时任务或触发器)
INSERT INTO user_search_index(user_id, search_text)
SELECT id, CONCAT(name, ',', email, ',', phone) FROM users;
-- 3. 搜索查询
SELECT u.* FROM users u
INNER JOIN user_search_index si ON u.id = si.user_id
WHERE si.search_text LIKE '%关键词%'
LIMIT 20;
缓存策略(Redis)
缓存热点搜索词
@Service
public class SearchCacheService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String SEARCH_CACHE_PREFIX = "search:keyword:";
private static final long CACHE_TTL = 300; // 5分钟
public List<User> searchWithCache(String keyword) {
String cacheKey = SEARCH_CACHE_PREFIX + keyword;
// 1. 尝试从缓存获取
List<User> cachedResult = (List<User>) redisTemplate.opsForValue().get(cacheKey);
if (cachedResult != null) {
return cachedResult;
}
// 2. 缓存未命中,查询数据库
List<User> result = userRepository.findByNameContaining(keyword);
// 3. 异步写入缓存
CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(cacheKey, result, CACHE_TTL, TimeUnit.SECONDS);
});
return result;
}
}
SQL优化技巧
强制索引与查询优化
@Repository
public class UserRepositoryImpl {
@PersistenceContext
private EntityManager entityManager;
public List<User> searchByKeyword(String keyword) {
// 1. 使用HINT强制索引
Query query = entityManager.createNativeQuery(
"SELECT * FROM users USE INDEX(idx_name) WHERE name LIKE ?1", User.class);
query.setParameter(1, keyword + "%"); // 前缀查询
// 2. 限制返回行数
query.setMaxResults(100);
return query.getResultList();
}
// 3. 分批查询,避免大结果集
public List<User> searchByBatch(String keyword, int batchSize) {
List<User> results = new ArrayList<>();
int offset = 0;
List<User> batch;
do {
batch = entityManager.createNativeQuery(
"SELECT * FROM users WHERE name LIKE ?1 LIMIT ?2 OFFSET ?3", User.class)
.setParameter(1, "%" + keyword + "%")
.setParameter(2, batchSize)
.setParameter(3, offset)
.getResultList();
results.addAll(batch);
offset += batchSize;
} while (batch.size() == batchSize);
return results;
}
}
实际案例:综合优化方案
// 完整优化方案
@Service
@Slf4j
public class OptimizedSearchService {
@Autowired
private UserRepository userRepository;
@Autowired
private UserSearchRepository searchRepository; // ES
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 智能搜索:先ES,再数据库,最后缓存
*/
public Page<User> smartSearch(String keyword, int page, int size) {
long startTime = System.currentTimeMillis();
try {
// 1. 尝试ES搜索(高性能)
Page<UserDocument> esResults = searchRepository.search(
buildNativeSearchQuery(keyword),
PageRequest.of(page, size)
);
if (esResults.hasContent()) {
log.info("ES search took {}ms", System.currentTimeMillis() - startTime);
return convertToUserPage(esResults);
}
} catch (Exception e) {
log.warn("ES search failed, falling back to database", e);
}
// 2. ES失败时使用数据库+缓存
String cacheKey = "search:" + keyword + ":" + page + ":" + size;
// 从缓存获取
Page<User> cached = (Page<User>) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
log.info("Cache hit for keyword: {}", keyword);
return cached;
}
// 3. 数据库查询(使用优化索引)
Page<User> dbResults = userRepository.findByNameStartingWith(
keyword,
PageRequest.of(page, size)
);
// 4. 写入缓存(异步)
if (dbResults.hasContent()) {
CompletableFuture.runAsync(() -> {
redisTemplate.opsForValue().set(cacheKey, dbResults, 300, TimeUnit.SECONDS);
});
}
log.info("Database search took {}ms", System.currentTimeMillis() - startTime);
return dbResults;
}
}
优化对比表
| 方案 | 性能提升 | 复杂度 | 适用场景 |
|---|---|---|---|
| 索引优化 | 2-10倍 | 低 | 前缀查询 |
| 全文索引 | 5-20倍 | 中 | 全文搜索 |
| Elasticsearch | 10-100倍 | 高 | 大规模搜索 |
| 缓存策略 | 3-10倍 | 中 | 热点数据 |
| 分表策略 | 3-5倍 | 高 | 超大数据表 |
关键优化建议
- 避免使用
%keyword%:这种查询无法使用索引 - 限制结果集大小:使用
LIMIT或分页 - 使用覆盖索引:查询字段全部在索引中
- 考虑业务容忍度:对实时性要求不高的场景,可以接受秒级延迟
根据你的实际数据量和并发情况,选择最合适的优化方案,通常建议先尝试数据库索引优化,再考虑引入ES等重型方案。