序:
假期前,无心工作,心中只想为祖国母亲庆生。摸鱼也要有点意义:今天就来总结下前几天在写业务的时候遇到一个比较复杂的排序问题,记录在这里与大家分享。
1. 业务场景
由于涉及公司的一些内部业务,为避免一些奇奇怪怪的事情(你懂得)发生,我在这里只写下实现思路:
数据库中的一些素材信息
需要后端查询并返回展示,正常的类似这种查询操作业务不是很难,这里的需求有点不一样:
首先:
素材种类分为:
- 默认素材(default:1为默认,0为正常)(默认唯一)
- 普通素材(vip:0)
- 会员素材(vip:1)
- 用户自定义的素材(可能是会员素材也可能是普通素材)(由于每个用户自定义不一样,在另外一张用户自定义素材表:personal)
然后,每个用户可以自定义一种素材作为自己的封面,产品爸爸对排序的要求是:素材列表支持分页查询,用户每次请求返回这个素材列表的时候:如果用户有自定义素材,优先返回,否则按照默认的顺序返回,即:
当有自定义时:用户自定义素材 > 默认素材 > 普通素材 > 会员素材
当无自定义时:默认素材 > 普通素材 > 会员素材
最后,素材的类型(默认、普通、会员)可在管理后台设置,用户自定义素材由用户设置。
正文:
面对这样一个排序功能,我一开始还是比较淡定的,心中窃喜:简单!思路奉上:
查询的时候用 Mongoose
的 sort
排序,按照 default
逆序、vip
顺序 的方式排列,
排好的list
就是:默认素材 > 普通素材 > 会员素材,
如果用户有自定义设置,来一个二次查询,把自定义素材放到 list
最前面,由于用户自定义的素材可能是默认、普通、会员,所以同时找到删掉 list
中的自定义素材,这样就确保自定义素材在 list
中只有一个,
排好的就是:用户自定义素材 > 默认素材 > 普通素材 > 会员素材
但是,分页怎么做呢,用 limit
+ offset
这样会出现一个问题,就是:
第一页数据:01234
第二页数据:56789
…
这时候,A 用户在浏览数据,浏览到第一页时,B 用户往第一页数据插入一条:X,此时此刻:
A 用户看到的数据:
第一页数据:01234
第二页数据:45678
…
这样就导致当用户在分页浏览数据时,有别的用户插入数据,下一页的第一条 是 上一页的最后一条,如果用户的添加操作过多,会出现很多重复的数据。
所以,对于分页,我一般用的是 sid 分页的方法(随意找了一篇文章,我觉得 sid 的原理说清楚了,简单就是)。
但是,既要用 sid
分页,又要对所有素材排序,总让人觉得有点矛盾。
说下最后采用的方法吧:
这里多了管理后台
和 Redis
的操作:
在运营在管理后台添加或者修改素材的时候,查询一次数据库,按照:默认 > 普通 > 会员 排序取出,以 有序集合
的数据类型将素材数据存入Redis
:
当用户无自定义素材时,用户每次获取 list
查询 Redis
就好
当用户有自定义设置时候,获取 list
,将自定义放到 0
分数位置,并删除该素材在有序集合中的位置
此时:sid
就是有序结合的每个元素的分数。
总结:
- 这样做的好处是管理人员在设置的时候就将排好序的素材列表存入
Redis
,不会出现分页数据重复的情况 - 本质上:用户每次只需要查
Redis
, 根据用户是否有自定义设置来做数组元素的插入与删除。