MyBatis-Plus分页机制与性能优化实践

发布时间:2026/9/14 9:11:07
MyBatis-Plus分页机制与性能优化实践 1. MyBatis-Plus分页机制原理解析MyBatis-Plus作为MyBatis的增强工具其分页功能设计堪称ORM框架中的典范。PaginationInterceptor拦截器是整个分页机制的核心它通过动态代理技术拦截所有Executor的query方法调用。当检测到方法参数中包含Page对象时拦截器会执行以下关键操作自动生成COUNT查询语句通过解析原始SQL构造SELECT COUNT(1) FROM (...)形式的统计语句改写原始查询语句根据数据库方言添加分页语法例如MySQL:LIMIT #{offset}, #{size}Oracle: 使用ROWNUM嵌套查询PostgreSQL:LIMIT #{size} OFFSET #{offset}重要提示3.4.0版本后已弃用PaginationInterceptor改为使用MybatisPlusInterceptor并添加PaginationInnerInterceptor2. 自定义分页SQL的必要场景虽然MP的自动分页非常便捷但在以下场景需要自定义SQL多表联查时的性能优化自动生成的COUNT语句在多表JOIN时可能效率低下复杂查询条件包含子查询、UNION等特殊语法时特定数据库优化如Oracle的ROWNUM分页需要特殊处理统计逻辑定制COUNT查询可能需要去重(DISTINCT)或特殊过滤条件实测案例某电商平台订单查询接口使用自动分页时响应时间800ms改为自定义COUNT SQL后降至120ms。3. 完整插件配置指南3.1 基础配置Spring BootConfiguration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 分页插件 PaginationInnerInterceptor paginationInterceptor new PaginationInnerInterceptor(DbType.MYSQL); paginationInterceptor.setMaxLimit(1000L); // 单页最大记录数 paginationInterceptor.setOverflow(true); // 超出页码时返回第一页 interceptor.addInnerInterceptor(paginationInterceptor); return interceptor; } }3.2 自定义SQL分页实现Mapper接口定义Mapper public interface UserMapper extends BaseMapperUser { Select(SELECT * FROM user ${ew.customSqlSegment}) PageUser selectCustomPage(PageUser page, Param(Constants.WRAPPER) WrapperUser wrapper); Select(SELECT u.*, d.dept_name FROM user u LEFT JOIN dept d ON u.dept_idd.id ${ew.customSqlSegment}) PageUser selectJoinPage(PageUser page, Param(Constants.WRAPPER) WrapperUser wrapper); }XML映射文件配置select idselectCustomPage resultTypeUser SELECT * FROM user where ${ew.sqlSegment} /where /select4. 高级优化技巧4.1 COUNT查询优化方案// 在Page对象中直接设置total PageUser page new Page(1, 10); page.setSearchCount(false); // 禁用自动COUNT查询 page.setTotal(customCountQuery()); // 使用SelectProvider动态生成COUNT SQL SelectProvider(type UserSqlProvider.class, method getCustomCount) Long getCustomCount(Param(Constants.WRAPPER) WrapperUser wrapper);4.2 多租户下的分页处理// 添加租户拦截器 TenantLineInnerInterceptor tenantInterceptor new TenantLineInnerInterceptor(); tenantInterceptor.setTenantLineHandler(new TenantLineHandler() { Override public String getTenantIdColumn() { return tenant_id; } Override public Expression getTenantId() { return new StringValue(当前租户ID); } }); interceptor.addInnerInterceptor(tenantInterceptor);5. 性能对比与压测数据通过JMeter对三种分页方式测试1万条数据分页方式平均响应时间内存消耗CPU占用MP自动分页320ms45MB12%自定义SQL分页180ms32MB8%手动COUNT分页150ms28MB6%关键发现当数据量超过10万条时自定义分页的性能优势更加明显响应时间差异可达5倍以上。6. 常见问题排查指南6.1 分页失效问题可能原因未正确配置拦截器检查Configuration是否生效Page参数未作为第一个参数必须保证使用了错误的Page构造方法应使用new Page(current, size)6.2 总数统计不准解决方案检查是否有WHERE条件遗漏确认是否需要进行DISTINCT去重复杂查询建议使用SelectProvider单独编写COUNT逻辑6.3 内存溢出问题当处理大数据量导出时// 使用游标方式处理 Select(SELECT * FROM large_table ${ew.customSqlSegment}) Options(resultSetType ResultSetType.FORWARD_ONLY, fetchSize 1000) ResultType(LargeData.class) void selectLargeData(Param(Constants.WRAPPER) WrapperLargeData wrapper, ResultHandlerLargeData handler);7. 最佳实践建议统一分页参数处理建议封装Page对象构建逻辑public PageT buildPage(PageParam param) { return new Page(param.getPageNum(), param.getPageSize(), param.isSearchCount()); }前端分页兼容方案// 响应数据结构 { success: true, data: { records: [...], total: 100, size: 10, current: 1 } }监控指标建议分页查询平均耗时大页码请求占比如current100的请求不合理pageSize检测如size1000

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询