头图

问题

有一个分页查询用户的接口,本来已经通过测试并且上线运行了。突然在测试环境报BUG,每一页有不同程度的数据重复,例如,第一页有张三,第三页又出现张三了。

主要的代码如下:

PageMethod.startPage(query.getCurrPage(), query.getPageSize());
List<User> users = userMapper.pages(query);
return new PageInfo<>(users);

第一反应是,可能 MyBatis-Plus 出问题了,或者是使用错误了。

MyBatis-Plus 官方文档推荐的方式,和出问题的代码一致(版本不同)

//第二种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectIf(1);

那么问题只可能出现在SQL

分析SQL

表结构如下:

CREATE TABLE `gov_contract_customer_relation` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `no` varchar(50) NOT NULL COMMENT '编号',
  `name` varchar(50) NOT NULL COMMENT '姓名',
  `age` int DEFAULT NULL COMMENT '年龄',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `no_UNI_IDX` (`no`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';

出现问题的分页SQL如下:

SELECT
    *
FROM t_user
WHERE age > 20
ORDER BY create_time DESC
LIMIT 0, 10;

问题表现为 LIMIT 0, 10LIMIT 0, 50 的结果中,前10条数据不一致。

ORDER BY 子句,测试期间没有写操作,不同的分页容量,按以往经验前10条数据应该一致。那么问题可能是 create_time 的值造成的。

查询所有的 create_time 发现,所有的值全部一样。因为是测试环境,测试同学批量导入数据,所以 create_time 值都一样。

原因

如果没有 ORDER BY 子句,MySQL 不保证以任何特定顺序返回。我们可能会观察到类似默认排序的可重复结果,但 MySQL 没有对此做出保证,所以这是不可靠的。

ORDER BY 子句用于排序的值全部相同时,会造成和没有 ORDER BY 子句一样的结果。

解决方案

SELECT
    *
FROM t_user
WHERE age > 20
ORDER BY create_time DESC, no ASC
limit 0, 10;

增加排序字段,避免无法排序字段值全部相同。最好使用有唯一约束的字段作为排序依据。


等灯的邓
19 声望0 粉丝