docs
docs copied to clipboard
[guide]朋友圈使用存储服务的最佳解决方案
假设前提:实现微信朋友圈的功能
功能点
- [x] 给朋友圈点赞
- [x] 查看本条朋友圈有哪些人点了赞
如果单纯上面两个功能,那么假定朋友圈对象是一个 AV.Object('Moment') 那么最好的存储方式如下:
let moment = new AV.Object('Moment');
moment.addUnique('likes',AV.User.current().objectId);
moment.save();
上述的实现最大的问题是在朋友圈逐渐增多之后,会出现如下功能查询的缓慢(参照聊天目前 _Conversation 表里面的 m 列的慢查询问题)
查看当前用户给多少条朋友圈点赞
因此,根本 @sdjcw 的建议可以在 _User 对象里面反向存储一个数组,保存当前用户给多少条朋友圈点过赞
let moment = new AV.Object('Moment');
moment.addUnique('likes',AV.User.current().objectId);
moment.save().then(obj =>{
AV.User.current().addUnique('liked',obj.objectId);
});
然后每天晚上可以使用云引擎的定时任务,去进行数据订正,以朋友圈的点赞(likes)数组为数据基准去修正 _User 表上的 liked 数组的内容,就可以做到尽可能的完善。
依旧不完美
但是上述方案依旧不完美,因为一个用户点赞很多,并且随着朋友圈的增加的话,_User 表中 liked 会变得很大,最后如果很大,并且如果用户还针对这个数组进行了查询操作就会出发服务端针对 Array 类型的 contains 查询的瓶颈,因此一旦用户需要实现:
查看当前用户给多少条朋友圈点赞并且单个用户点赞的朋友圈可能超过 1000 个甚至是不可估量的数值
请使用中间表去构建。
更完善的解决方案
在云引擎里面使用 redis 存储以下查询结果:
- 某一个朋友圈被多少人点赞了
- 某一个用户给多少条朋友圈点赞了
然后在云函数里面将上述查询结果包装成云函数,客户端直接调用可以获取结果做前端展现即可,将查询和写入原子化分离,这样前端的压力全都走到 redis,而写入都继续在原来的存储服务里面继续写入中间表。
这种解决方案也是 twitter 等大型社交应用场景下的典型案例。