前段时间给自己的业余项目写代码的时候遇到了一个问题就是如何设计点赞,评论,收藏的存储结构。这3个功能是社交软件必不可少的组成部份,但是设计起来确一点都不简单。
先说评论
之前在7k做zeze社区的时候我就发现了评论的复杂性,最大的问题是数据量大,以前做社区的时候一个楼层下可以有几万个评论,一个贴子可以有几百个楼层,一个论坛可以有好多贴子,数据增长很快。但是现在app的评论系统都比较简洁,不会像网页那么复杂,如果不支持嵌套回复的话,那就简单多了,表结构设计如下:
id: 评论id,自增主键
item_id: 被评论项目的id,int
author_id: 作者id,int
comment: 评论内容,varchar
这个模型没有解决大数据扩展的问题,但是已经算是非常简洁了,对于未来分表的设想是在中间加一张索引表,映射某一个范围的itemid,某个authorid的所有评论到一个分片中,这样按父id,和发布人id来索引评论的时候都简单。
其次是点赞
这个问题的难度在于用户可以反复点击,所以要求接口响应足够快,占用资源足够少,不然同时多个用户连续点赞取消赞就会给服务器带来很大的压力了。最初我的设想是把点赞设计为用户和帖子的一个关系,如果用户点赞了某一个帖子,用户就和这个贴子有关系,如果取消了点赞这个关系就断开了。那么点赞表就是一个多对多的关系表了,按帖子和用户查询都可以很快。这种实现的坏处是如果用户反复点击赞接口时,都会有一个查询去判断用户是否赞过这个帖子了,因为所有的产品都是要求用户 不能反复点赞同一个帖子,需要程序自己来维护点赞操作的幂等性,也就是每次写库之前先find一次,如果有就更新时间,如果没有就插入。毫无疑问这个接口会很慢而且占用很多资源,所以要找另一个更优化的方案。我后来决定使用redis的zset来存储点赞信息,以帖子id作为key,以点赞用户的uid作为value,score是点赞时间,由于集合自身是不允许出现重复的value的,所以每次都可以直接zadd把点赞用户更新进去,不需要人工维护数据唯一,太省事了,不过如果还要按用户索引被点赞过的帖子,那就得再新增一个key,以uid作为key,以帖子id作为value,不能支持多字段查询是redis的设计导致的,不过更新2个redis的key可比更新1行mysql快多了,最好使用multi保证原子操作。
取消赞,收藏,取消收藏我都是用同样的方式做的,只要确保redis服务器不挂,不丢数据就行。