Mybatis PageHelper 自动添加 limit 引发的血案

阿里云产品限时红包,最高 ¥1888 元,立即领取

现象描述

在浏览器上多次刷新同一个页面,发现同一区域的数据会时多时少。

查看接口响应发现同一接口多次请求会返回不一样的数据。

正常情况

页面显示

Mybatis PageHelper 自动添加 limit 引发的血案

接口响应

Mybatis PageHelper 自动添加 limit 引发的血案

异常情况

页面显示

Mybatis PageHelper 自动添加 limit 引发的血案

接口响应

Mybatis PageHelper 自动添加 limit 引发的血案

正常接口响应与异常接口响应数据量对比

Mybatis PageHelper 自动添加 limit 引发的血案

问题定位

猜测一:负载均衡问题

多个不同版本的应用实例,代码实现或者数据库数据不同导致。

经过排查,开发和测试环境都存在一样的问题,并且这两个环境都是单实例的。

猜测二:代码逻辑有误

走查了代码,发现这里只是一个简单的单表查询,并没有复杂逻辑。

同时针对单个接口反复测试,无法重现响应数据时多时少的问题。

目前看,这块的代码并没有很直观的错误。

猜测三:sql 多次执行返回不同结果

使用查询语句,多次重试,没有复现异常。

猜测四:

经过猜测二、三的排查,怀疑是页面上多个请求同时发起共同导致的问题。

于是反复刷新页面,查看后端日志,发现正常情况与异常情况时执行的 sql 不一致。

异常情况下,会在正常的 sql 后面添加 limit 子句。

Mybatis PageHelper 自动添加 limit 引发的血案

到这里,我只能猜测是 mybatis 动态生成的 sql 有问题。

那么什么原因导致会在正常的 sql 后添加 limit 子句,并且这个 5 是从哪里来的呢?

原因分析

在度娘上以 “mybatis 自动添加 limit” 为关键字搜索,发现由于 PageHelper 插件自动添加 limit 的问题很多。

Mybatis-PageHelper使用方法文章中有如下说明:

Mybatis PageHelper 自动添加 limit 引发的血案

Mybatis PageHelper 自动添加 limit 引发的血案

由此,可能的原因是在这个页面上的其他请求带有分页,同时由于后端对 Mybatis PageHelper 的使用不规范导致。

页面上有分页的请求

Mybatis PageHelper 自动添加 limit 引发的血案

在排查了该接口的代码后发现:代码中调用 PageHelper.startPage 方法的地方与真正执行查询的代码中间有很多复杂的业务处理。

问题原因基本清楚了。

解决方案

  1. 永久的解决方案:规范 PageHelper.startPage 方法的使用,只在真实查询要执行前,调用该方法,确保安全分页。
  2. 临时的解决方案:在出现问题的查询前面,调用 PageHelper.clearPage

目前按照临时解决方案进行了处理,反复测试以后,问题得到了解决。