Web开发点赞竟然有这么多值得思考的地方!
基本上每个跟社交有关系的网站,都有点赞。
微博,知乎,B站,头条,掘金....
可是这个点赞功能如是何实现的呢?
你这个点赞的动作,这个世界发生了什么事情呢?
功能点
以文章/评论点赞为例子
- 点赞
- 取消点赞
- 获取点赞数
- 检查当前用户是否有点赞
常规的CRUD思想
如果按我们平常所想,用户触发点赞按钮,向后台发起请求:
-
点赞/取消点赞
-
检查当前用户是否有点赞
- 如果有点赞,取消点赞,返回结果,取消点赞成功
-
如果没有点赞,添加一条点赞记录,返回结果,提示点赞成功
-
点赞数
点赞数可以统计到被点赞的内容里,每次点赞时去修改即可。
- 查询用户是否有点赞
通过用户id查询记录,存在表示已经点赞了,否则表示没有点赞。
引发的问题
前面的思考方式,是我们一个人在玩是可以的。
我们思考一下,假如两个人同时点赞呢?甚至多个人同时点赞呢?
简单点思考,在同一个时间片,两个用户点赞了。
- 检查是否有点赞
这个好像问题不大,大家都是查询。
- 修改文章/评论的点赞数量
假设当前是10,两个人同时点赞,那两个人写入的都是11,这样子的话,是不是就有问题了呀。
如果用Mysql,那么单条点赞记录插入的话,那么点赞的数量增长是非常快的。
假如每天会产生10000条内容,每个内容平均点赞100,那就是100万条点赞记录了。
一般超过500万记录就建议分表了,以这样的增长,是不是不好处理呢?
如果我们使用Mongodb的话,每个文章/评论,对应着一个点赞记录,而这里面包含着点赞用户的id。
这样子的增长速度,貌似没有直接像mysql那样插入数据快。
但是问题又来了呀!
当快速连续点赞的时候,会怎么样子呢?就会有两条记录了吧。
所以要解决这种快速点赞的问题。
如果两个人同时点赞,取出数据,再判断,再写入,那也是不对的。有一个人的点赞是丢失的。
这样子又出现问题了。
如何解决以上问题呢?
- 连续快速点赞
- 并发点赞
- 数量快速增长
如果使用Mysql,查询是否已经点赞过,查询是没有问题的 ,不存在数据安全问题。
更新呢?两个内容,插入一条点赞记录,这个不涉及到数据安全问题。但是,修改先赞总数,则会可能出现数据安全问题。
两个人同时读取到了10,然后加上,写入11。这样的结果是不对的。
如果使用悲观锁,效率低,用乐观锁,对数据版本进行判断,一样的话才写入,不一样则不写入,读取重新计算。
有没有减少读取数据库的方法呢?
我们可以结合redis来使用。
这里面有一个策略,以小时/天为更新单位,那么我们key前缀就用年-月-日-天,或者小时为更新单位的话我们的key前缀则是年-月-日-天-时,后缀则是点赞内容的id。
方法:
key:年_月_日_天_时_id_用户id value:点赞用户
这是redis里的数据设计。
流程,当用户点赞的时候,涉及到插叙是否有点赞,以及插入数据。
查询的话先查询redis里有没有,有则直接下一个操作,如果没有则查询mysql数据库。
插入,数据先不插入到mysql/mongodb里,先插入到redis里。
每一个小时去处理redis里前一个小时的点赞数据,点赞总数叠加即可。
这样子,用户的点赞总数是每个小时更新一次,不会实时更新,频繁写入数据。
即使并发添加记录也没关系,现在写入总数的是计算前一个小时的,所以就隔离了。
UI体验
UI,点赞完直接+1,如果取消点赞则减掉。这样避免出现并发时实时更新的一点赞多了好几个出来。 不过我们1小时更新一次,则不会出现这种情况,如果你获取新的数据则点赞无效。所以UI判断到操作成功的结果,直接对UI数据进行+1/-1的处理。
UI,如果用户量很大,这意味着有一部分用户会点赞,然后取消点赞。这种两次提交是浪费资源的。
所以可以在UI退出的时候,去发起最后结果的请求。这样子可以节省一部分资源。
到此,在网站开发过程中,也会和大家分享遇到的问题,以及相关的经验。
重点不在代码,都在思想上。我们人类总能通过各种方法去解决问题的。