APP订单列表查询展示功能,向下刷新拉取了重复的数据,偶尔还会缺失数据,和后台的数据对不上,订单中心获取订单列表的接口是个按支付时间倒排的分页接口,分页实现方法:
select * from order where[condition] order by mtime desc limit pageNum*pageSize,pageSize
新建测试表,插入数据。
# 模拟订单表
CREATE TABLE`order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ctime` bigint(20) NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
# 数据初始化
INSERT INTO`order`(`ctime`)VALUES (1);
INSERT INTO`order`(`ctime`)VALUES (2);
INSERT INTO`order`(`ctime`)VALUES (3);
INSERT INTO`order`(`ctime`)VALUES (4);
INSERT INTO`order`(`ctime`)VALUES (5);
INSERT INTO`order`(`ctime`)VALUES (6);
INSERT INTO`order`(`ctime`)VALUES (7);
INSERT INTO`order`(`ctime`)VALUES (8);
INSERT INTO`order`(`ctime`)VALUES (9);
INSERT INTO`order`(`ctime`)VALUES (10);
# 第一页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
***手机页面展示***
10 9 8 7 6
# 新增两条数据
INSERT INTO`order`(`ctime`) VALUES (11);
INSERT INTO`order`(`ctime`) VALUES (12);
# 第二页
SELECT * FROM `order` ORDER BY ctime DESC limit 5,5;# 查询结果
id ctime
7 7
6 6
5 5
4 4
3 3
# 重复数据:6和7
***手机页面展示***
10 9 8 7 6, 7 6 5 4 3
原因:
查询第一页完后,获取到的数据是6~10,随后新增11和12两条数据,进行查询第二页,获取到的数据是3~7,此时6和7被重复拉取了
# 第一页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
***手机页面展示***
10 9 8 7 6
# 删除id=6的数据
DELETE FROM `order` WHERE id = 6;
# 第二页
SELECT * FROM `order` ORDER BY ctime DESC limit 5,5;# 查询结果
id ctime
4 4
3 3
2 2
1 1
# 缺失数据:5
***手机页面展示***
10 9 8 7 6, 4 3 2 1
原因
第二页的数据完全依赖第一页的数据,第一页的数据变化影响第二页的数据;主要还是每次分页查询都是从起点分页,分页之前的数据变化都会影响接下来的分页。
验证新增
# 使用当前排序字段ctime当作curPosition参数
# 第一页。传curPosition为空,mybatis条件查询不带该参数。
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
此时 curPosition = 6;
***手机页面展示***
10 9 8 7 6
# 新增两条数据
INSERT INTO`order`(`ctime`) VALUES (11);
INSERT INTO`order`(`ctime`) VALUES (12);
# 第二页
SELECT * FROM `order` WHERE ctime<'#{curPosition=6}' ORDER BY ctime DESC limit 0,5;# 查询结果
id ctime
5 5
4 4
3 3
2 2
1 1
# 查询正常
***手机页面展示***
10 9 8 7 6, 5 4 3 2 1
验证删除
# 第一页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
此时 curPosition = 6;
***手机页面展示***
10 9 8 7 6
# 删除id=6的数据
DELETE FROM `order` WHERE id = 6;
# 第二页
SELECT * FROM `order` WHERE ctime<'#{curPosition=6}' ORDER BY ctime DESC limit 0,5;# 查询结果
id ctime
5 5
4 4
3 3
2 2
1 1
#查询正常
***手机页面展示***
10 9 8 7 6, 5 4 3 2 1
利弊分析
使用curPosition记录位置查询的方式,**好处在于,在第一次查询确定初始位置之后,后面的数据一定不会出现数据重复和缺失。**在手机端进行列表划动时,新刷新出来的数据都是在前一批数据的curPosition基础上查询到的。但这也带来一个问题,我们的整个查询好像只能从排序的方向依次查询,因为每次查询都依赖上一次的curPosition。假如我们要进行跳页查询,将会带来一些问题。令pageSize=5,比如查完第一页,得到curPosition = a,接下来想查询第10页,可以使用 where ctime所以在面对跳页查询时,这样的方法就不太可取了。好在是目前需求是解决手机端划动列表的加载,并不会出现跳页面的情况,但是在web端展示就不能使用这样的sql了。还有一个谈不上缺陷的缺陷就是,手机页面展示不包括在curPosition时间点之后已经更新过的数据,比如在验证新增的时候看不到新增的11、12,验证删除的时候已经删除的6还是会展示在页面上,这点在后面的方法二中会得到解决。
验证新增
# 第一页。
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
***手机页面展示***
10 9 8 7 6
# 新增两条数据
INSERT INTO`order`(`ctime`) VALUES (11);
INSERT INTO`order`(`ctime`) VALUES (12);
# 第二页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5*2;# 查询结果
id ctime
12 12
11 11
10 10
9 9
8 8
7 7
6 6
5 5
4 4
3 3
# 查询正常
***手机页面展示***
12 11 10 9 8,7 6 5 4 3
验证删除
# 第一页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5;
# 查询结果
id ctime
10 10
9 9
8 8
7 7
6 6
***手机页面展示***
10 9 8 7 6
# 删除id=6的数据
DELETE FROM `order` WHERE id = 6;
# 第二页
SELECT * FROM `order` ORDER BY ctime DESC limit 0,5*2;# 查询结果
id ctime
10 10
9 9
8 8
7 7
5 5
4 4
3 3
2 2
1 1
#查询正常
***手机页面展示***
10 9 8 7 5, 4 3 2 1
利弊分析
可以发现使用这样的方式,数据的一致性是最高的,几乎所有的数据库更改都会展示在客户端页面上,更不可能出现重复和缺失的情况。随之而来,每次请求都会请求limit 0,pageSize*pageNum的所有数据,而且客户端需要对获取的数据重新渲染展示,无疑对数据库和app端增加了很大的开销。
第一次进入订单列表时,请求接口查询得到所有数据,在客户端进行分页展示。此后再往下划时,不需要再请求后端了,只需要把第一次请求后的数据展示出来即可。
该方法实在太不常见,弊端太明显,不做具体分析了。
总的来说,在仅考虑手机端分页查询接口的情况下:方法一在实际项目中实行的可能性更大一些。对数据实时性要求很严格的情况下,可以考虑方法二。
展示数据实时性 | 请求次数 | 前后端压力 | 数据库压力 | |
---|---|---|---|---|
方法一 | 查询瞬间 | 一次一查 | 正常 | 正常 |
方法二 | 与数据库同步 | 一次一查 | 前端压力大 | 数据量随次数逐渐增多 |
方法三 | 第一次进入页面瞬间 | 查一次 | 前端压力大 | 一次请求大量数据 |