《从 MyBatis-Plus 到 Elasticsearch:一个后端的性能优化踩坑实录》

​

最近接手了一个老项目,单表查询用 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​​,因为:

  1. 业务需求以​模糊搜索+排序​为主(如商品名称、用户昵称)
  2. 已有 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. 深度踩坑​​

  1. ​分词器选型​ :默认分词器对中文不友好,需安装 ik 插件
  2. ​数据同步一致性​ :双写时用 canal 监听 MySQL Binlog 更可靠
  3. ​ES 动态映射陷阱​:字段类型自动推断可能导致查询异常,建议预定义 mapping

​​总结​​:

  • ​简单 CRUD​:继续用 MyBatis-Plus,别过度设计
  • ​复杂搜索/聚合​ :ES 是真香,但要做好​资源隔离+监控​(ES 吃内存大户!)
  • ​混合架构​ :学会在不同场景选择合适工具,​没有银弹​
相关推荐
余槐i13 小时前
200万行表加索引锁死写入:PostgreSQL CONCURRENTLY 的三类等待与重建路径
数据库·postgresql·性能优化·索引·explain
打工仔折腾 AI16 小时前
FaceFusion本地换脸实战:Windows整合包、模型选择与遮罩调参记录
人工智能·windows·后端·python·深度学习·性能优化·ai agent 实战
@#¥&~是乱码鱼啦17 小时前
ArkWeb 混合手记 07|性能优化、内存管控、内核预热、崩溃防护,整套系列终章
性能优化
天空鸟_时光不老17 小时前
13-亮点提炼:把技术说辞翻译成业务价值
java·spring boot·spring·spring cloud·mybatis
lvqinglou17 小时前
学习Sql-part1 for婷姐
sql·学习·性能优化
JMchen17 小时前
性能优化——让Native代码飞起来
android·c++·性能优化
yunwei3717 小时前
eBPF 入门实践教程十七:编写 eBPF 程序统计随机/顺序磁盘 I/O
linux·后端·性能优化
ZHOUPUYU17 小时前
PHP 8.2 的核心高级特性,包括只读类、类型系统增强
android·性能优化·php
ZHOUPUYU18 小时前
PHP 8.0 高级技术实战:从新特性到性能优化
android·性能优化·php
Frag0ut18 小时前
Chrome扩展程序管理技巧:安全安装、权限审查与高效使用指南
chrome·性能优化·谷歌浏览器·浏览器插件·扩展程序管理·权限审查·冲突排查