如何处理 api 分页导致的数据重复或丢失

2016-12-20 10:26:11 +08:00
 bigbyto

我们的 api 目前是这样设计的

/users/?page=2&pre_page=20

客户端提交页数和每页的数量,服务端返回如下

{
   "code": 0,
   "pagination": {
      "page": 1,
      "limit": 20,
      "total": 100
   }
   
   "data": {}
}

不过这样会出现数据重复或丢失。比如当前用户正在 app 翻页刷新,如果正好在后台删除了一条消息,那么就会因为数据变化导致分页时有一条数据丢失了。

后来想到一个解决方案,通过 cursor 分页

/users/?cursor=2015-01-01 15:20:30&limit=10

上面就可以解决 app 端翻页时数据出现变化的情况,不过依然会有 2 个严重的问题。

  1. 时间戳重复会导致永久丢失某条数据
  2. 如果存在筛选条件(如饿了么按照经纬度排序获取数据),此方法失效

对于第一个问题,我们想到使用主键作为过滤条件(主键递增),可以解决重复的问题。但第二个问题似乎想不出什么好的办法。

不知道大家怎么处理分页问题,有好的建议或方案还望不吝赐教。

11563 次点击
所在节点    程序员
38 条回复
learnshare
2016-12-20 12:58:29 +08:00
@dotudeth
@bigbyto 排序之后必须要拿第一页, itemId 是被忽略的
814084764
2016-12-20 13:41:04 +08:00
分页,连网易的微博都做不好,你还想做好?太天真了~ [手动滑稽]
Magic347
2016-12-20 14:00:34 +08:00
数据库读写不分离一下吗?
NeinChn
2016-12-20 14:05:29 +08:00
的确没有太好的办法吧...
一般都是用 SELECT * FROM table WHERE sort_field > last_id ORDER by sort_field LIMIT size.
要么就前端处理重复,后端每次多返回一些数据...
alouha
2016-12-20 15:03:40 +08:00
看 xx app 的分页那叫一个烂,之前遇到过你说的问题,当时偷懒,让客户端对重复数据进行处理 /捂脸
JasperYanky
2016-12-20 15:17:09 +08:00
题外话 强烈建议 下一页不要客户端拼参数,服务端的接口中直接指定 next url
zvving
2016-12-20 16:57:15 +08:00
一般都是按毫秒级的时间戳来分页的,别想太复杂
kamal
2016-12-20 17:48:26 +08:00
你看看豆瓣的评论怎么分页的
zclzhangcl
2016-12-20 17:52:35 +08:00
晚上刷百度贴吧时,发现贴吧的分页做的很差,经常连续很多都是前面已经出现过的。
这个问题不是那么好解决,或者说没那么重要
bigbyto
2016-12-20 18:24:29 +08:00
@zclzhangcl 目前看来还是无法用简单的方法去处理分页问题。不过像 feed 流这种业务场景,倒是可以用 twitter 的 since_id 和 max_id 来处理,因为 feed 数据本身就是有序的(发布时间)。不过一旦出现其他排序条件,普通的方法还是得跪。
bigbyto
2016-12-20 18:25:59 +08:00
@JasperYanky 为什么不要客户端拼参数呢? 这一版的 api 我们目前还没正式使用,目前打算服务端返回一个"next"字段取代"page",当然还是由客户端这边提交过去。
whow
2016-12-20 20:25:59 +08:00
取排序后分页最后一项,将参与排序的字段拼接成一个 next ,客户端下次请求的时候把 next 作为参数回传到服务端,服务端通过这个字段计算出下一页。
```
?next=&size=
```
```
{
list:[],
next:""
}
```
mrliusg
2016-12-20 20:50:40 +08:00
逻辑删除,然后返回给客户端的时候不显示出来就好了吧?
或者直接在服务端删除,请求 20 条数据,只返回 19 条就好啦
jyf
2016-12-20 21:17:03 +08:00
多少无关紧要 请求 20 条 返回 22 条和 17 条都没什么大的关系 逻辑上没有问题就好了

对于用户过滤这种可以考虑第一页的时候就把结果的主键给放 redis 的 sortedset 里 后面如果新增可以在缓存期内无视 而删除则无非是多了一个而已
yidinghe
2016-12-20 21:22:18 +08:00
分页逻辑天然就有这个问题,最好的方式是接续查询,用上一页最后一条记录作为查询下一页的条件,这样查出来既不会多也不会少。
JasperYanky
2016-12-20 23:19:38 +08:00
@bigbyto 我倾向 所有需要分页的 API 外层都有一个 next 字段,里面是下次页的 url ;至于为什么不要客户端拼,很简单,你要是这个版本按照 id 分页 下个版本按照时间分页, API 能分分钟改掉并且完全不影响客户端~
owt5008137
2016-12-21 12:05:58 +08:00
你就不能给每条消息分配一个 ID ?然后拉到的消息和已有的 ID 相同就 update ,否则才 insert ?
zclzhangcl
2016-12-22 12:06:56 +08:00
@mrliusg 你这种处理方式会有问题,返回的总数与实际数量不符。对于一些严格的场景,这样是不行的。
如果需要严格的话,还是按 @yidinghe 的方式来。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/328806

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX