最近接手了一个老项目,单表查询用 MyBatis-Plus 写得飞起,但一到多表关联+模糊搜索就卡成 PPT。痛定思痛,决定引入 Elasticsearch 优化查询性能,结果踩坑无数……记录下这次从 ORM 到搜索引擎的升级历程,分享给同样被慢查询折磨的你。
1. 问题定位:MP 的舒适区边界
MyBatis-Plus 的 QueryWrapper
在单表操作中确实优雅:
// 条件查询示例(单表)
List<User> users = userService.lambdaQuery()
.eq(User::getStatus, 1)
.like(User::getName, "张")
.list();
但遇到跨表 JOIN + 高并发模糊查询时,MySQL 直接裂开:
- 大表
LIKE '%关键词%'
导致全表扫描 - 分页
COUNT
计算拖慢响应(即使用了PageHelper
)
结论:MP 适合 OLTP 简单场景,OLAP 复杂查询得换方案。
2. 技术选型:ES 还是 ClickHouse?
面对海量数据搜索,纠结了两个方案:
方案 | 优点 | 缺点 |
---|---|---|
Elasticsearch | 实时搜索快,支持分词高亮 | 资源占用高,学习曲线陡峭 |
ClickHouse | 列式存储,聚合分析强 | 不适合频繁更新/点查 |
最终选择 ES,因为:
- 业务需求以模糊搜索+排序为主(如商品名称、用户昵称)
- 已有 ES 集群,可复用运维资源
3. 实战:SpringBoot 集成 ES
(1) 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
注意版本匹配(Spring Data ES 和 ES 服务端版本需兼容)。
(2) 实体类映射
用注解定义 ES 索引结构和分词器:
@Document(indexName = "products", createIndex = false)
public class Product {
@Id
private Long id;
@Field(type = FieldType.Text, analyzer = "ik_max_word") // 中文分词
private String name;
@Field(type = FieldType.Double)
private Double price;
}
(3) 混合查询策略
写操作:双写 MySQL 和 ES(本地事务+消息队列补偿)
@Transactional
public void addProduct(Product product) {
// 1. 写入 MySQL
productMapper.insert(product);
// 2. 发MQ消息异步写入ES(防止事务失败污染ES)
rocketMQTemplate.send("es-update-topic", product);
}
读操作:走 ES 查询,兜底查 MySQL
public Page<Product> search(String keyword, int page, int size) {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", keyword)) // 分词匹配
.withPageable(PageRequest.of(page, size))
.build();
return elasticsearchRestTemplate.search(query, Product.class);
}
4. 性能对比
压测结果(100万数据,并发 200):
查询类型 | MySQL + MP | ES |
---|---|---|
单字段精确查询 | 120ms | 15ms(↑8倍) |
多字段模糊查询 | 2800ms(全表扫) | 50ms(↑56倍) |
排序分页 | 900ms | 30ms(↑30倍) |
代价:ES 索引延迟约 1s(近实时),存储占用是 MySQL 的 2 倍。
5. 深度踩坑
- 分词器选型:默认分词器对中文不友好,需安装
ik
插件 - 数据同步一致性:双写时用
canal
监听 MySQL Binlog 更可靠 - ES 动态映射陷阱:字段类型自动推断可能导致查询异常,建议预定义 mapping
总结:
- 简单 CRUD:继续用 MyBatis-Plus,别过度设计
- 复杂搜索/聚合:ES 是真香,但要做好资源隔离+监控(ES 吃内存大户!)
- 混合架构:学会在不同场景选择合适工具,没有银弹