
我们的 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 dou4cc 2016-12-20 10:36:38 +08:00 不要搞太复杂 把页面做成自动更新的,使后台对数据的操作实时反映在页面上 |
2 stamaimer 2016-12-20 10:37:47 +08:00 via iPhone 你可以看看 twitter 咋做的 |
3 BOYPT 2016-12-20 10:39:50 +08:00 学习 twitter 的 api 设计,使用 sinceid , lastid ; 推算 page 难维护难想难写容易错。 |
4 q397064399 2016-12-20 10:40:20 +08:00 这个不应该是后端的事情么? 脏读 可以通过事务控制来 防止 |
5 learnshare 2016-12-20 10:40:35 +08:00 大部分场景下不需要考虑客户端状态同步 如果真的需要,可以把删除动作同步到客户端;然后分页使用 itemId 作为判断依据,而不是 page + size |
6 xiaoyangs 2016-12-20 10:43:10 +08:00 @learnshare 说得对,客户端不要搞太复杂。用 itemid 来分页吧。 |
7 Ge4Los 2016-12-20 10:51:17 +08:00 用游标加长度来做参数。这种类似的 feed 流的接口,内容总在更新,客户端接口就适合游标了。 |
8 loading 2016-12-20 10:59:32 +08:00 via Android 传入数据库的 id 字段 |
10 bigbyto OP |
11 murmur 2016-12-20 11:04:39 +08:00 丢了就丢了呗,你看新浪微博刷新一次直接时间起飞到上个世纪,一样股票大涨 |
12 chairuosen 2016-12-20 11:08:11 +08:00 丢就丢了+1 |
13 ty89 2016-12-20 11:10:45 +08:00 按 lastid 来分页,只适合按照时间排序类似微博这种,万一后来来个需求让你按照热度、点赞数、评论数来生序降序就呵呵了 |
15 quericy 2016-12-20 11:29:51 +08:00 我们就是动态排序的,按照 Redis 里 SortSet 得分从高到低 当时想到个分页方案是以得分作为游标,这样每次翻页的时候获得得分更低的指定条数数据. 而顶贴的时候得分会更新得更高,不会出现重复的问题, 但是可能会漏掉几条翻页过程中被顶上去的帖子. 不过这个方案没通过,最后还是用了传统分页+客户端对近几页的帖子 ID 去重 |
16 ofblyt 2016-12-20 11:35:42 +08:00 个人觉得只能是按照 id 排序来进行分页,包括美团在内的公司对于多维度排序的处理也只是将主要纬度的数据分别建表,按各个纬度的 id 进行排序分页 |
17 Miy4mori 2016-12-20 11:58:10 +08:00 via Android 这有什么影响吗?不要做的太复杂。 |
18 morethansean 2016-12-20 12:00:42 +08:00 via Android 为什么不按照时间戳呢 |
19 dotudeth 2016-12-20 12:00:55 +08:00 @learnshare 如果这个列表还掺杂着排序的操作呢(比如在后台设了权重值)。 |
20 sorra 2016-12-20 12:09:42 +08:00 办法是有,数据库与缓存做 diff 比较,但是颇为复杂。 写文一篇 http://www.qingjingjie.com/blogs/24 |
21 learnshare 2016-12-20 12:58:29 +08:00 |
22 814084764 2016-12-20 13:41:04 +08:00 分页,连网易的微博都做不好,你还想做好?太天真了~ [手动滑稽] |
23 Magic347 2016-12-20 14:00:34 +08:00 数据库读写不分离一下吗? |
24 NeinChn 2016-12-20 14:05:29 +08:00 的确没有太好的办法吧... 一般都是用 SELECT * FROM table WHERE sort_field > last_id ORDER by sort_field LIMIT size. 要么就前端处理重复,后端每次多返回一些数据... |
25 alouha 2016-12-20 15:03:40 +08:00 看 xx app 的分页那叫一个烂,之前遇到过你说的问题,当时偷懒,让客户端对重复数据进行处理 /捂脸 |
26 JasperYanky 2016-12-20 15:17:09 +08:00 题外话 强烈建议 下一页不要客户端拼参数,服务端的接口中直接指定 next url |
27 zvving 2016-12-20 16:57:15 +08:00 一般都是按毫秒级的时间戳来分页的,别想太复杂 |
28 kamal 2016-12-20 17:48:26 +08:00 你看看豆瓣的评论怎么分页的 |
29 zclzhangcl 2016-12-20 17:52:35 +08:00 晚上刷百度贴吧时,发现贴吧的分页做的很差,经常连续很多都是前面已经出现过的。 这个问题不是那么好解决,或者说没那么重要 |
30 bigbyto OP @zclzhangcl 目前看来还是无法用简单的方法去处理分页问题。不过像 feed 流这种业务场景,倒是可以用 twitter 的 since_id 和 max_id 来处理,因为 feed 数据本身就是有序的(发布时间)。不过一旦出现其他排序条件,普通的方法还是得跪。 |
31 bigbyto OP @JasperYanky 为什么不要客户端拼参数呢? 这一版的 api 我们目前还没正式使用,目前打算服务端返回一个"next"字段取代"page",当然还是由客户端这边提交过去。 |
32 whow 2016-12-20 20:25:59 +08:00 取排序后分页最后一项,将参与排序的字段拼接成一个 next ,客户端下次请求的时候把 next 作为参数回传到服务端,服务端通过这个字段计算出下一页。 ``` ?next=&size= ``` ``` { list:[], next:"" } ``` |
33 mrliusg 2016-12-20 20:50:40 +08:00 逻辑删除,然后返回给客户端的时候不显示出来就好了吧? 或者直接在服务端删除,请求 20 条数据,只返回 19 条就好啦 |
34 jyf 2016-12-20 21:17:03 +08:00 多少无关紧要 请求 20 条 返回 22 条和 17 条都没什么大的关系 逻辑上没有问题就好了 对于用户过滤这种可以考虑第一页的时候就把结果的主键给放 redis 的 sortedset 里 后面如果新增可以在缓存期内无视 而删除则无非是多了一个而已 |
35 yidinghe 2016-12-20 21:22:18 +08:00 via Android 分页逻辑天然就有这个问题,最好的方式是接续查询,用上一页最后一条记录作为查询下一页的条件,这样查出来既不会多也不会少。 |
36 JasperYanky 2016-12-20 23:19:38 +08:00 @bigbyto 我倾向 所有需要分页的 API 外层都有一个 next 字段,里面是下次页的 url ;至于为什么不要客户端拼,很简单,你要是这个版本按照 id 分页 下个版本按照时间分页, API 能分分钟改掉并且完全不影响客户端~ |
37 owt5008137 2016-12-21 12:05:58 +08:00 via Android 你就不能给每条消息分配一个 ID ?然后拉到的消息和已有的 ID 相同就 update ,否则才 insert ? |
38 zclzhangcl 2016-12-22 12:06:56 +08:00 |