挑战某音短视频核心推荐算法-原理篇
背景
某音,我不得不把你卸载, 因为你知道我太多秘密.
大家都知道抖音的算法牛掰, 看过什么就推荐你什么. 让你越看越依赖, 越用越久, 他的广告费却赚得盆满钵满, 你说气不气人, 该不该卸载.
比如我看过王祖贤, 刘亦菲的小视频, 你就不断给我推, 唯恐全天下不知道我喜欢王祖贤, 刘亦菲拍的倩女幽魂?
家里领导一发现, 一吃醋, 这么一着急, 榴莲都不够用, 关键榴莲还要我买.
所以把某音卸载还不解恨, 我决定掀桌子.
你的推荐算法不是牛吗, 你不是很丝滑吗?
今天, 我要把你的核心推荐算法按在地上摩擦, 把这个算法的门槛降低到地板难度. 让所有人都能用上, 让天下冒出无数个某音的友商, 卷死你.
算法的核心
1、给视频贴标签. 简单, 阿里云就有类似的视频服务, 可以完成涉黄赌毒、暴力、zz敏感等的识别过滤, 以及根据特征打标. 并给出视频标签s, 以及每个标签对应的权重.
2、刷新视频的推荐指数. 简单, 根据 浏览量 * 各标签的权重 计算出视频的各标签的推荐分数.
3、视频的地域池设计. 简单, 要让本地的好视频可以有上升到全国推荐的池子, 同时要让付费的广告主的视频有VIP通道(推荐特权): 设计三个池: 本地视频池(geohash, table_suffix. 或者以邮编, 或电话区号为后缀.)、全国流量池、付费推荐视频池
说白了就是视频浏览量越多, 就把视频放到更大的池子里. 池子的个数, 级别可以根据业务需求自由设计.
4、用户喜好和权重计算. 简单, 1方面是注册的时候勾选喜好, 另一方面是根据用户浏览的视频的(主、副标签及权重, 算出用户每个标签的喜好). 可以使用 JSONB / array 存储.
5、推荐视频. 从3个池子中提取视频, 简单, 本地池: 根据用户的位置查询对应的本地表, 根据喜好标签搜索喜好的视频, 根据权重Limit返回条数; 全国池和付费推荐池以此类推; 总共就查3张表.
6、过滤已读视频. 简单又不简单, 提取过程中, 把用户已读的视频ID剔除即可.
不简单的地方1: 一个人可能浏览了很多视频; 视频被很多人浏览过; 所以列表会非常大, 如果每读一次写一条记录, 会很多条, 使用not in过滤会非常慢, 怎么办?
简单, 使用roaring bitmap, hll, datasketch 把已读列表存储成1条记录, 由于是lossy type或压缩type, 哪怕存数亿个值也只需要几十KB.
《沉浸式学习PostgreSQL|PolarDB 1: 短视频推荐去重、UV统计分析场景》
PS: 《PostgreSQL 15 preview - Use a hash table to speed up NOT IN(values)》
hll其实有点类似于bloom, 可以参考 《UID编码优化 - 用户画像前置规则 (bloom, 固定算法等)》 bit占位图示参考:
不简单的地方2: 如果已读列表非常大, 会耗费大量的CPU和IO过滤已读, 才能拿到未读的可推荐视频. 怎么解?
可以借鉴广度优先和深度优先的思路, 默认是采用类似“广度优先”搜索策略, 所以会遇到大量已读. 但是我们可以使用partial index, 加个hash mod条件, 相对于全局视频数据就变成类似“深度优先”, 可以大幅度减少大量已读. 详细请看后面的SQL设计和索引设计.
不简单的地方3: 把已读列表 正向存在视频表(已读的用户)? 还是 反向存在用户表(已读的视频)?
简单, 我推荐反向存在用户表里, 不会有锁冲突问题.
不简单的地方4: 实时更新还是异步更新已读列表?
简单, 推荐异步存储, 先存储在redis/程序临时缓存, 后期合并到数据库内. 如果是异步更新, 在收到数据库提取的推荐视频后, 程序端要根据缓存再过滤一下.
7、如果资源不足导致雪崩了怎么办?
方法1, 预加载, 例如每次给用户批量推荐100条视频, 当用户刷到80条时, 后台已经在请求后面的100条了. 这样用户就会感到丝般柔滑.
方法2, 降级到随机推荐. 使用随机采样, 不管数据量多大, 毫秒召回.
今天, 你只需要1个PostgreSQL或PolarDB数据库, 普通macbook pro每秒就可以按喜好推荐26.7万个视频, 卷死某音.
其实我在之前公众号的文章中已经透露了一部分demo: 平民的劳斯莱斯, 笔记本玩转短视频推荐
欢迎关注我的github (https://github.com/digoal/blog) , 学习数据库不迷路.
近期正在写公开课材料, 未来将通过视频号推出.
欢迎大家关注我的公众号: PostgreSQL码农集散地
我将会在公众号发布更详细的Demo.