我搜了一下,发现网络上很少有关于社交网络的点赞功能的设计和实现。究其原因是很多人没遇到过这样的需求,真实场景太过于复杂,写出来的文章容易被打脸!而我今天就是冲着打脸来的!
在开始之前,我们参考一下微博的点赞案例!
大V、明星的一条微博的点赞数可能有几十万,甚至百万以上。那么这个“点赞”功能是如何设计的呢?
有的人说,这还不好设计,一张表搞定,看下面的表:
CREATE TABLE `t_link` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID', `art_id` bigint(20) DEFAULT NULL COMMENT '文章ID', `user_id` bigint(11) DEFAULT NULL COMMENT '点赞用户id', `like_time` datetime DEFAULT NULL COMMENT '点赞时间', `status` int(11) NOT NULL COMMENT '状态', PRIMARY KEY (`id`) );
然后,每次文章有人点赞时就加一条记录,取消点赞就update一条记录。
我们先不说这样设计合不合理,普通的博客,这样设计没什么问题。因为访问量就小,直接操作MySQL数据库问题也不大。但是问题在于当你的用户量大,访问量大的时候,类似微博的大V之类的,粉丝众多的时候,就像我们开头所说的一篇文章上百万个赞很正常!
那么这个时候,你再直接操作数据库,系统肯定是要玩蛋的!
这个时候,我猜有人会想到了Redis。嗯,用 Redis 能起到一定的作用。
关于点赞,我们先操作 Redis,等一定的时间,或者一定的数据量,然后再批量的持久化到数据库。
但是呢?用 Redis 也有几个问题,那就是不能让所有的数据都缓存起来吧!微博每天产生那么多文章,产生那么多的点赞记录。光存储在磁盘那数据量也大的超乎想象吧!微博现在的 Redis 都是几十T的大小。光缓存点赞数据,肯定不行。我们得找出哪些是热门的数据,然后把热门的给缓存起来。粉丝小的,几天以前甚至是很久以前的是不是缓存的必要性就得考虑了!
再说上面的数据表设计,每天会产生多少的点赞记录,不可想象。你一定得分表分库设计吧!
分表分库你是根据什么分呢?不管你根据什么分,使用的时候都会遇到麻烦!比如,查询哪些用户点了赞,查询我的好友中谁点了赞,查询某个人对某些文章点了赞等。你分表分库后,查询就不方便了。
再说上面引入 Redis 后,也会带来很多新的问题!比如:开发复杂,这个比直接写mysql的方案要复杂很多, 需要考虑的地方也很多;不能保证数据安全性,redis挂掉的时候会丢失数据, 同时不及时同步redis中的数据, 可能会在redis内存置换的时候被淘汰掉等。
但是你不能说 Redis 有缺点你就不用它吧,用还是要用。关键就看你如何用?下面说说我的打脸思路!
在redis中弄一个set存放所有被点赞的文章,post_user_like_set_{$post_id},对每个post以post_id作为key, 搞一个set存放所有对该post点赞的用户;
post_user_like_{$post_id}_{$user_id} 将每个用户对每个post的点赞情况放到一个hash里面去。
为啥用hash?只所以用hash是因为完全可以用hash来存储一个点赞的对象, 对应数据库的一行记录。
当然有同学会说用key, value也可以, 将所有的数据序列化(json_encode等),后全部放到value里面去。 hash可以很方便的修改某个字段, 而序列化和反序列化的操作。
post_{$post_id}_counter 对每个post维护一个计数器, 用来记录当前在redis中的点赞数,这里我们只用counter记录尚未同步到mysql中的点赞数(可以为负), 每次刷回mysql中时将counter中的数据和数据库已有的赞数相加即可。
用户点赞/取消赞:获取user_id, post_id, 查询该用户是否已经点过赞, 已点过则不允许再次点赞,这里需要注意的是用户点赞的记录可能在数据库中, 也可能在缓存中, 所以查询的时候缓存和数据库都要查询, 缓存没有再查询数据库。
关于点赞就写这么多吧,主要是在往下写,脸打的更痛。我就抛个砖,等其他各路大神来反击写文章吧!
: » 设计一个社交网站的点赞最佳方案
原创文章,作者:端木书台,如若转载,请注明出处:https://blog.ytso.com/tech/java/251907.html