使用 Java +mysql+redis 实现一个简易的类似随机的数据获取算法,有哪些比较好的方案?目前我都有点怀疑需求是不是有问题了。
说明
公司的小项目,没有打算使用搜索引擎或大数据相关的技术。
首先说一下产品的需求:
目前有个视频列表查询,需实现每次启动 app 获取的第一页数据不同即可(如果不做处理,使用 mysql 每次获取数据的顺序都是一定的,则每个用户每次启动 app 获取的第一页数据都是一样的);还需要保证在用户没有获取完所有的视频前不再获取之前已经获取过的视频。即以每个用户为单位,看过的视频不能再被查出来,除非数据库的数据都被这个用户获取过一遍。产品不允许有重复的视频在用户没有看完所有视频前再被查询出来,无论数据量是大还是小。
举例说明
假设
数据库总数据:10 条
id 为自增:1-10
每页查询:4 条
默认排序为 id 顺序自增
当前方案
- 启动 app 查询第一页数据的 id:1 2 3 4
- redis 记录当前用户查询的最后一条数据的 id:userId:4 (最后一条记录 id )
- 重启 app 查询第一页数据的 id: 此时就限制数据 id > 4 即 5,6,7,8
- redis 记录当前用户查询的最后一条数据的 id:userId:8
- 重启 app 查询第一页数据的 id: 此时就限制数据 id > 8 即 8,9,10 还差一条才满足一页,因此从 1 开始取,最终返回的数据为 8,9,10,1
- redis 记录当前用户查询的最后一条数据的 id:userId:1
- …
如果保证每次返回的数据 id 是顺序自增的那么是没问题的,如果是倒序自增,也没问题,每次重启 app 取数据的时候 id>? 变为 id<? 即可。
遇到的问题
有一个特殊情况,管理员可以设置精选视频,可以把一些视频进行置顶,那么获取的数据就不是顺序或倒序自增的了,顺序就会乱。
而且还存在增删操作。
- 顺序 id 为:1 2 3 4 5 6 7 8 9 10
- 后台设置精选后的顺序变为:7 1 9 2 3 5 4 10 6 8
- 如果再按照当前方案,就会出现问题:
- 启动 app 查询第一页数据的 id:7 1 9 2
- redis 记录当前用户查询的最后一条数据的 id:userId:2
- 重启 app 查询第一页数据的 id:此时就限制数据 id > 4
-
那么数据库的筛选后的排序及数据结果为:7 9 5 10 6 8 -
返回的数据就变成了:7 9 5 10
-
- redis 记录当前用户查询的最后一条数据的 id:userId:10
- 重启 app 查询第一页数据 id:此时就限制数据 id > 10
-
那么数据库的筛选后的排序及数据结果为:7 1 9 2 (因为>10 的数据没有,就从头开始取)
-
- redis 记录当前用户查询的最后一条数据的 id:userId:2
- …
问题也就出现了,第二次启动 app 查询的第一页数据 id7 和 9 这两条数据就和上一次启动 app 查询的数据重复了;并且 3 4 6 8 这些数据永远不会被查询出来。问题就是这样,未实现产品需求。 (数据没有全部查询过一遍,距上一次查询就出现了重复数据)
这种情况和数据量的大小没有较大关系(如果把自增的最大的一个 id 推荐到了第一页的最后一条,那么就永远只能查到前四条数据)。
还需要考虑管理员设置精选或者进行增删后的数据的实时性问题。设置精选和增删操作如果频繁有哪些影响。
产品提供的一个方案
将每个用户看过的视频进行记录,然后在查询的时候进行剔除,如果数据不够一页就从头开始获取一次;这种方式就必不会出现未看完就重复的问题,但是有严重的性能问题。
- 顺序 id 为:1 2 3 4 5 6 7 8 9 10
- 第一次数据 id:1 2 3 4
- redis 记录当前用户已看数据 id:userid:1,2,3,4
- 第二次数据 id: not in(1,2,3,4) 查出 5,6,7,8
- redis 记录当前用户已看数据 id:userid:1,2,3,4,5,6,7,8
- 第三次数据 id:not in(1,2,3,4,5,6,7,8) 查出 9 10 1 2 (数据不够从头开始获取)
- …
逻辑是没问题的,但是数据量少还好,如果数据量很大那么性能影响很严重。
小声 bb,产品告诉我,同时满足需求和性能不就是开发应该干的事嘛,我很想说想要性能和需求同时满足也得看需求是否合理吧。
如果有 10w 用户并且有 10w 数据,在最极端的情况下,假设这 10w 用户有 99999 条数据都看过了,那么 redis 就会存储 10w*99999 的数据量,并且在查询 mysql 的时候语句就变成 not in(99999 个 id),想想就恐怖,如果数据量更大呢?
但产品认为不要考虑这么多我们系统最多只有几千条数据,用户量可能会很多,但即使这样数据量也比较大 10w*几千 ,并且也没有这样设计系统的,不合理。
不局限于目前已有的这些方案或技术栈(使用除 java+mysql+redis 以外的技术也可),只要能实现这个需求的目的就可以:以用户为单位,数据在没有全部查询过一次的情况下,不能出现重复数据。期间需考虑管理员可以在任意时刻,或很频繁的进行精选和增删操作。
各位有没有什么好的想法,以及使用 java+mysql+redis 的技术栈能否实现,若不能实现,是否有其他实现方式?
再提一句,这个产品实际上是个 Android 开发。
帮忙出出主意吧,先谢谢各位了,最近已经被折磨的焦头烂额了!