Home » Code » 关于签到与补签功能

关于签到与补签功能

做一个签到功能,每天一次,过后可以补签。访问签到页面时候,若最新一次签到时间小于昨天凌晨0点,则显示0,否则显示最新一个连续签到区间的天数和。若没有补签功能,只需要一个计数器,根据本次签到时间与上次签到时间对比,若不相连,重置为1,若相连,加1。但有了补签,这个计数器就不好更新了。其实我们可以简单点,正常签到和补签,只需要插入一条签到记录,其他啥也不用做。获取连续签到天数的时候,取出全部签到记录,往后循环判断是否有断就可以了。

看到这里可能有人觉得太naive,取出全部签到记录?循环?这些字眼容易让人联想到新手、初级程序员、菜鸟等这些不怎么高大上的词汇。但在这里,它确实是一个简单可行易维护的方案。签到记录,肯定会根据uid进行分表,uid又有索引,一个用户每天都签到,10年也才3650条记录,mysql取这3650条记录(只需要签到时间一个字段)出来再程序中循环一下,一点压力也没有。不要看到“取全部”就怕了,还要考虑实际情况,实际上它就没多少。再说,能撑10年的公司有多少,经常用签到功能的用户有多少,能坚持10年不断签的用户又有多少,这么一筛选下来,其实大部分用户都只需要取几百条就可以得出结果了。因为那特殊的一点能长时间坚持的用户而采用其他复杂不易维护的方案,个人感觉不值得。

考虑极端情况用户坚持了10年不断,姑且认为从mysql取只有一个整型字段的3650条记录有压力,还可以使用redis的有序集合来做一下缓存。一个用户一个集合,成员是签到日期,分数也是,这样可以一次性按顺序取出所有签到记录,取出效率肯定比mysql要快了。以下用代码模拟这两种方案的实现,包括插入一个用户10年连续签到记录、取出并遍历一个用户10年连续签到记录(DatePeriod不取消开始日期会比指定的天数多出一天,因此实际插入了3651条记录),在自己双核i3渣本上的测试数据:

输出结果如下(单位:毫秒):

begin solution redis...
init connection timeuse: 0.6211
insert timeuse: 34.3308
getContinuousDayNumber, number: 3651 timeuse: 34.0650
solution total timeuse: 69.0732
-------------------------------------------------------
begin solution mysql...
init connection timeuse: 1.4279
insert timeuse: 153.9268
getContinuousDayNumber, number: 3651 timeuse: 64.2660
solution total timeuse: 219.6910

经过多次truncate table和flushdb的测试,结论是这样的:对于插入,redis要比mysql快,对于全部取出循环判断之,redis是30ms左右,mysql是60ms左右,2倍左右的差距,二者都是很快的!

因此,对于这个需求,如果由我来做,就是采取这种方案了。能达到上边极端情况连续签到10年的用户是少之又少的,即使存在这样的用户计算出天数也只需要30~60毫秒,完全可以接受,过度优化,杞人忧天,有时候反而会误事。用户访问签到页面,先取天数缓存,若有直接显示,若没有则需要计算,计算是从redis取出全部签到记录往后循环判断是否断签。若reids还没有签到记录,则从mysql全部取出来,插入redis并计算之,然后存入天数缓存。用户进行正常签到或补签,只需要同时往mysql和redis插入记录,并删除天数缓存。流程图如下:

签到与补签流程

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.