使用 Java +mysql+redis 实现一个简易的类似随机的数据获取算法,有哪些比较好的方案?目前我都有点怀疑需求是不是有问题了。

2021-01-10 18:05:13 +08:00
 CandyMuj

说明

  公司的小项目,没有打算使用搜索引擎或大数据相关的技术。

首先说一下产品的需求:

  目前有个视频列表查询,需实现每次启动 app 获取的第一页数据不同即可(如果不做处理,使用 mysql 每次获取数据的顺序都是一定的,则每个用户每次启动 app 获取的第一页数据都是一样的);还需要保证在用户没有获取完所有的视频前不再获取之前已经获取过的视频。即以每个用户为单位,看过的视频不能再被查出来,除非数据库的数据都被这个用户获取过一遍。产品不允许有重复的视频在用户没有看完所有视频前再被查询出来,无论数据量是大还是小。

举例说明

假设

​ 数据库总数据:10 条
​ id 为自增:1-10
​ 每页查询:4 条
​ 默认排序为 id 顺序自增

当前方案

如果保证每次返回的数据 id 是顺序自增的那么是没问题的,如果是倒序自增,也没问题,每次重启 app 取数据的时候 id>? 变为 id<? 即可。

遇到的问题

有一个特殊情况,管理员可以设置精选视频,可以把一些视频进行置顶,那么获取的数据就不是顺序或倒序自增的了,顺序就会乱。

而且还存在增删操作。

问题也就出现了,第二次启动 app 查询的第一页数据 id7 和 9 这两条数据就和上一次启动 app 查询的数据重复了;并且 3 4 6 8 这些数据永远不会被查询出来。问题就是这样,未实现产品需求。 (数据没有全部查询过一遍,距上一次查询就出现了重复数据)

这种情况和数据量的大小没有较大关系(如果把自增的最大的一个 id 推荐到了第一页的最后一条,那么就永远只能查到前四条数据)。

还需要考虑管理员设置精选或者进行增删后的数据的实时性问题。设置精选和增删操作如果频繁有哪些影响。

产品提供的一个方案

将每个用户看过的视频进行记录,然后在查询的时候进行剔除,如果数据不够一页就从头开始获取一次;这种方式就必不会出现未看完就重复的问题,但是有严重的性能问题。

逻辑是没问题的,但是数据量少还好,如果数据量很大那么性能影响很严重。

小声 bb,产品告诉我,同时满足需求和性能不就是开发应该干的事嘛,我很想说想要性能和需求同时满足也得看需求是否合理吧。

如果有 10w 用户并且有 10w 数据,在最极端的情况下,假设这 10w 用户有 99999 条数据都看过了,那么 redis 就会存储 10w*99999 的数据量,并且在查询 mysql 的时候语句就变成 not in(99999 个 id),想想就恐怖,如果数据量更大呢?

但产品认为不要考虑这么多我们系统最多只有几千条数据,用户量可能会很多,但即使这样数据量也比较大 10w*几千 ,并且也没有这样设计系统的,不合理。


  不局限于目前已有的这些方案或技术栈(使用除 java+mysql+redis 以外的技术也可),只要能实现这个需求的目的就可以:以用户为单位,数据在没有全部查询过一次的情况下,不能出现重复数据。期间需考虑管理员可以在任意时刻,或很频繁的进行精选和增删操作。

  各位有没有什么好的想法,以及使用 java+mysql+redis 的技术栈能否实现,若不能实现,是否有其他实现方式?

  再提一句,这个产品实际上是个 Android 开发。

  帮忙出出主意吧,先谢谢各位了,最近已经被折磨的焦头烂额了!

4013 次点击
所在节点    Java
51 条回复
awanganddong
2021-01-10 18:41:30 +08:00
将视频列表整个加载到 redis 内存中,
然后用户已浏览记录也放入内存中,求这两者之间的差集。
然后对差集进行排序。

这里边时间与空间复杂度没考虑。
zxCoder
2021-01-10 18:54:12 +08:00
问题描述没看懂

直接按你原来的方案然后存下标不行吗

1 2 3 4 5 6 7 8 9 10
7 1 9 2 3 5 4 10 6 8
ylrshui
2021-01-10 19:10:05 +08:00
笨方法:为每个用户维护一个未查询列表
MoYi123
2021-01-10 19:13:05 +08:00
我用 pg 尝试过类似的功能,用 hll 存用户看过的视频,和你的需求应该差不多 https://github.com/mmooyyii/mmooyyii/blob/master/docs/database/tiplist1.md
lpts007
2021-01-10 19:58:04 +08:00
上次在 V 站看到的,用户 id 哈希后 mod 视频数量,每次顺着取,记录下每个用户当前值。完事。
zieglar
2021-01-10 20:04:01 +08:00
redis 的 sdiff 命令,比较 set 返回差异值
renmu123
2021-01-10 20:37:41 +08:00
我提供一个非技术的方法。
遇到这种事就直接怼回去,就说以现在技术水平做不到这件事,如果硬要做可能会产生性能问题。如果产品还在强硬,就把两人的聊天记录都整理好保存到项目软件中(以防之后出现问题倒打一耙),然后公开艾特你的老大和他以及他的老大说明其中问题,如果觉得没问题,那你就按他说的做。这样基本能保证之后就算出问题你也不用背锅,如果你的老大够给力会直接帮你怼回去。
CandyMuj
2021-01-10 21:38:15 +08:00
@renmu123 多谢,也是挺有用的一个办法,所有类似的事都可以这么处理的,做可以,出了问题我不负责,而且我之前也明确说过会出问题。遇到这种事是需要保存一些证据,尽量让自己少担责。如果遇到通情达理或者懂点技术的上级可能没问题。
如果遇到不懂技术或者也不通情的上级,然后产品或上级给你来一句:“这不就是你该做的事吗?做不了两全其美,那就是你技术有问题。” 这就把我整蒙了啊。
CandyMuj
2021-01-10 21:43:41 +08:00
@MoYi123 谢谢
CandyMuj
2021-01-10 21:45:56 +08:00
@MoYi123 是挺类似的,学习了
yzbythesea
2021-01-10 21:47:04 +08:00
可以在手机端处理。App 缓存客户的视频列表然后在本地保存一个 bloom filter 记录客户看过的视频。到时候直接选择不在 bloom filter 里的视频 ID 然后向后台发送请求。
CandyMuj
2021-01-10 21:47:14 +08:00
@awanganddong 数据全都加载到 redis,如果只是记录 id 的话,那么在用 Sql 进行查询的时候可能不太方便的,而且数据还会存在频繁的增删操作,应该还是得以 mysql 为核心的。
yzbythesea
2021-01-10 21:49:22 +08:00
另外就是增加 redis 台数,反正你这个是可以很轻松地横向扩展的。我觉得钱到位,产品说得这个方案是推荐做法,也没有任何性能担忧。
CandyMuj
2021-01-10 21:51:35 +08:00
说一下和另一个同行进行讨论后的一个大致可行方案吧,使用最笨的办法,单独使用一张表记录用户观看过的视频(就类似一个观看记录的功能);建立好索引,即使数据量很大速度也不会太慢;
主要问题就是在用户看完所有视频后需要清除之前已看的数据重新记录,在删除的时候需要重建索引,为了不影响用户正常使用,可以使用队列或者 redis 发布订阅来进行异步的删除,这样数据就存在延迟,就可能出现我一边在插入一边在删除的情况,查询也存在问题(本应该从头开始查,但此时还没有删除之前的观看记录,因此实际查询的时候还会剔除之前已观看的)。
解决这个的办法就是在往队列插入删除任务的时候同时记录需要删除的历史观看记录的最大 id,在删除的时候就通过 userid=? and historyId <= ?,如果数据量大可以进一步优化,进行分批删除。
还有个问题就是管理员如果修改了精选视频的排序,是否需要用户实时展示最新的排列顺序,即是否也要清空观看记录的问题或者其他的处理方式(例如,只清除所有用户历史记录中管理员新设置精选的视频 id,让这些视频可以被用户再次查询,此时就不考虑重复问题,因为设置精选就是为了增加曝光量)。如果不考虑这一点的话就不用管了
rrfeng
2021-01-10 21:53:48 +08:00
先实现:根据上次访问的 lastid 分页

再实现:精选的 lastid

拼在一起即可。
CandyMuj
2021-01-10 21:56:40 +08:00
@zieglar 谢谢,和一楼的方法类似 需要数据在 redis 才好操作,因此筛选出 id 后使用 sql 操作存在性能问题,应该以 msyql 为核心
CandyMuj
2021-01-10 21:57:48 +08:00
@rrfeng 谢谢,最开始就是这个,主要问题就在于这个数据的顺序会变,如果顺序不变就没问题 可以看看 14 楼的方案
CandyMuj
2021-01-10 21:58:45 +08:00
不一一回复了,感谢所有回复的朋友,好的方案我会学习一下的,谢谢啦! ^^
CandyMuj
2021-01-10 22:00:55 +08:00
@rrfeng 不过维护两个 lastid 这个想法挺不错的,是一个好的想法,谢谢。
CandyMuj
2021-01-10 22:01:56 +08:00
@ylrshui 笨方法有时反而是最有效的方法,只要优化的好也能成为好的方法,哈哈哈!

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

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

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

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

© 2021 V2EX