#10 大作业的视频数据来源 | 青训营笔记

284 阅读7分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 10 天
本次编程学习的基本环境配置如下
OS: macOS 13.1
IDE: Goland 2022.3
Go Version: 1.18
Python Version: 3.6

概要

今天稍稍将大作业的任务进度向前推进一点, 在云服务器上安装一些常用软件和工具,用于最终生产环境的测试,也简要说一下对大作业开发的看法。一家之言,如有不当,还请各位不吝赐教

需求分析

作为一个小组作业,首要任务是对任务量的合理分配,这就需要对项目需求有整体认识。下面的代码片段摘自官方给的Demo.

// basic apis
apiRouter.GET("/feed/", controller.Feed)
apiRouter.GET("/user/", controller.UserInfo)
apiRouter.POST("/user/register/", controller.Register)
apiRouter.POST("/user/login/", controller.Login)
apiRouter.POST("/publish/action/", controller.Publish)
apiRouter.GET("/publish/list/", controller.PublishList)

// extra apis - I
apiRouter.POST("/favorite/action/", controller.FavoriteAction)
apiRouter.GET("/favorite/list/", controller.FavoriteList)
apiRouter.POST("/comment/action/", controller.CommentAction)
apiRouter.GET("/comment/list/", controller.CommentList)

// extra apis - II
apiRouter.POST("/relation/action/", controller.RelationAction)
apiRouter.GET("/relation/follow/list/", controller.FollowList)
apiRouter.GET("/relation/follower/list/", controller.FollowerList)
apiRouter.GET("/relation/friend/list/", controller.FriendList)
apiRouter.GET("/message/chat/", controller.MessageChat)
apiRouter.POST("/message/action/", controller.MessageAction)

视频流怎么做?用户视频如何上传

目前能想到的信息

视频首先由特定的用户上传到服务器(经过可能存在的审核过程), 才能被其他用户刷到, 因此视频相关的信息有: 视频的标题、封面、所有者、视频的标签以及视频的访问数据(点赞、评论等).

用户对其他视频的访问会对我们生成视频流产生帮助, 虽然我们无法对这些虚拟用户进行准确的画像 比如:如果用户访问了某个Uploader的视频或者带有某些标签的视频, 我们就可以多向用户推荐这些视频.

用户的视频观看记录可以存放在Redis这样的高并发内存数据库中以便随时读取出来, 受内存限制, 我们只能保存其一段时间内的视频观看记录, 这应该是一个可调的参数.

一种简单的推荐算法

现在假设我们要向用户推荐10条视频, 那么可以这样做。对用户近期观看的视频做分析, 找出其观看较多的3个Uploader和3个标签. 然后在其观看较多的Uploader中找相同标签的视频. 如果超过10个, 则将这10个视频的链接发给用户. 如果不足10个, 则找其他Uploader中相同标签的视频, 凑齐10个. 如果该类视频实在太少, 就找别的种类的视频, 总之要凑够数。

我们可以对以上算法做一点小小的修正, 以增强用户体验. 如果我们要向用户推荐10条视频, 那么8条按以上规则推荐, 另外两条是完全随机的视频

数据怎么存储

视频的元信息需要持久化存储到关系型数据库, 如MYSQL中, 包括视频的标题、封面、所有者、视频的标签以及当前的状态(待审核、已审核待上线、待修改、已删除)。

视频的封面和视频本身属于超大的不定规模的二进制数据, 可以存到文件系统中, 以FTP或基于FTP的方式向外访问, 但这种方式有很多弊端, 比如视频链接可能会长期有效, 给服务器造成很大负担, 如果视频被删除, 需要同步删除视频本身以避免视频泄露, 以及潜在的FTP安全漏洞.

基于以上考量, 这样的二进制数据存储到对象存储服务, 如MinIO中. 经过了解, Minio可以产生仅在一段时间内有效的URL传递到前端. MinIO官方也有Go的SDK方便开发者使用.

点赞和评论的数据怎么存储.

用MySQL这样的RDBMS去存这些数据不可能是一个明智的选择, 点赞请求和评论数据的获取很可能会消耗大量的资源, 成为整个系统的瓶颈。

点赞数据和评论数据有什么特点呢? 这些数据的一致性要求不会特别高, 特别是点赞数据。我们可以选择NoSQL去存储, 比如MongoDB.

另外有一个很奇怪的现象, 可能很多人都有注意到. Bilibili(一个在线综合视频网站)会记录用户对视频的点赞记录, 但这个记录貌似是有长度的. 当我时常翻看那些经典视频的时候, 经常会发现我已经投币了但是点赞按钮却是熄灭的. 这说明这个视频我曾经三连过, 但用户的点赞记录"消失"了. 当然, 只是当前用户看不到自己曾经点赞了, 视频的点赞量应该是准确的, 否则就很难有上百万点赞的视频了。

尝试用MongoDB(Redis也可以)去存储某视频被点赞的数量, 每隔一段时间(30s)持久化到MySQL上;

评论是一个树形结构, MongoDB应该也能搞定(需要验证), 每隔一段时间持久化到MySQL上(序列化成XML);

以上只是一厢情愿的想法, 具体还需要之后做验证.

用户信息怎么存

用户信息可以分为用户的认证信息(ID, 密码)、社交信息(昵称、个性签名、邮箱、性别等)以及用户之间的关系.考虑使用不同的数据结构存储(顺便完成垂直划分)

认证信息和社交信息

用户的认证信息涉及到登录和注册两个部分, 注册时用户需要提供邮箱和密码, 注册完成才允许用户登录. 这是一个一致性要求很高的场景, 必定涉及和RDBMS的交互. 因此认证信息直接存到MySQL中.

用户完成登录后, 应当给用户一个一定时间内可用的令牌, 以减少用户认证对数据库的访问, 这个令牌可以存到Redis中. 令牌过期后, 用户需要重新认证.

用户登录和注册仅在极小情况下成为系统瓶颈。接下来是社交信息, 昵称、个性签名、邮箱、性别这些信息的修改相对也是低频的. 可以考虑直接存储到MySQL中.

用户之间的关系

用户之间的关系是一个多对多模型, 按照关系理论, 需要一个大型的中间表存储这种关系(类似于SC数据库中的成绩表)。对多对多关系建模可以使用图数据库(如Neo4j), 但显然有些大材小用了

我们考虑在用户表中增加其关注列表和粉丝列表, 可以用JSON表示. 关注列表必须有上限(Bilibili貌似是2000), 粉丝列表可以可以比关注列表稍大一些, 能应对用户的几次刷新即可. 关注列表和粉丝列表会在用户登录后加载到Redis中或者后端服务器上.

考虑到用户的关注是一个低频操作(除了某些行为艺术家), 因此可以充分利用内存做缓存, 先将用户的关注动作放在内存中, 参与之后的推流, 然后在一段时间后延迟持久化到RDBMS中(对于每次关注动作, 如果之后没有相应的取关动作, SQL操作应该有2次)

如果有这样一个用户坚持不懈, 想要获取到某个Uploader的全部粉丝的昵称呢?(Bilibili的某Up主: 最上川下山就曾经在敬汉卿的所有粉丝中, 分析出敬汉卿最想吃掉的几个粉丝.)

如果我们的软件能有这样的用户, 或许我们就真的应该考虑一下上Neo4j的事情(当然, 仅仅存储一个关注网络的话, 自己用邻接表实现一个也是可以的).

用户的聊天信息

这个是序列化的数据, 可以以会话为单位分批存储. 如果考虑撤回功能的话, 可以加一个延迟写入(可撤回的时间和延迟写入的时间相关).

写在最后

现在整个项目还处在完全空想的阶段, 之后在实践中一定会有所改动, 相关方案在后续笔记中更新。作为一个后端小白, 希望这个项目能有所帮助.