目前在用go写一个项目,这个项目的点赞的接口是我来写的,这个接口用到了redis进行缓存,同时用mysql进行持久化。但是项目过程遇到了一些比较难搞的问题,就是如何优雅地对点赞的数据继续持久化。
获取go-redis相关依赖
go get github.com/go-redis/redis/v8
一开始是一边用redis进行缓存,一边用mysql进行持久化。这样的话想对于直接从mysql进行直接读入读出来说虽然是更优化了,但似乎还是不是最优的方法。
这里先说明,我采用的是在redis里面键是set类型的,一个评论的id对应的是一个键,然后用户的username对应的是一个值。一个评论可以有很多个用户点赞,但是这些用户必须是不一样的,所以这个模型是没问题的。
但是假如用户疯狂点赞和取消点赞,其实还是会导致性能的下降。怎么办呢?显然,我们不应该实时地在mysql里面进行点赞数据的存储与删除,而是可以利用定时任务,在某一个时间段内统一对redis里面的部分点赞的数据进行持久化。这也是很容易想到的。但是,我们的redis里面的数据随着时间的累计很显然会越来越多,内存也会出现不足的情况。所以,我们需要对redis里面的数据进行过期处理,因为对于一些存入redis里面的点赞的数据,可能很久很久都不会再被访问到了。这样的话就可以进行过期处理。
redis里面的过期键删除策略有三种:
1、定时删除。在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。
2、惰性删除。放任键过期不管,但是每次从键空间获取键的时候,会检查键是否过期,如果过期就删除这个键,否则就返回这个键。
3、定期删除。定期删除是综合了前两种的有点,就是设置一个定时任务,定时检查redis里面部分键是否过期,如果过期就删除。
很显然,我选择的是第三种策略。对于同一个评论,我开了两种set,一种是用于read,另一种是用于write,write的是用于定期删除。
-
首先,用户给某一个评论点赞,数据存入键为
comment+comment_id+r的set,为每个read的键设置一个过期的时间,同时将同样的数据存入comment+comment_id+w的set; -
当用户给某一条评论取消点赞的时候,数据首先从read里面进行删除,如果这个过期了,那最好,反正我也要进行删除,如果没有过期,那么就正常地删除这条数据。对于wirte里面的数据,要么就在redis里面,要么就被持久化了。(注意我的用于持久化的那些键值对是没有设置过期时间的,因为我在持久化之后会是删除掉那些数据,所以不会出现内存无限制增长的情况)所以,我只要先判断数据是否在用于持久化的键值对里面,如果在,就说明没有被持久化,直接从redis里面删除即可,否则说明被持久化了,那么我们要做的是从mysql里面进行删除。
以上,就是我在写一个点赞接口的时候遇到的一些问题和我能想到的解决的办法,前前后后花了大概一天的时间在想这个问题,目前,我觉得基本得到了解决,分享出来希望对遇到同样问题的人有一个启发。同时在解决问题的同时也加深了我对于redis的理解。