【应用开发】Python + Redis 实现用户签到统计

签到是产品的一个功能模块,可以辅助产品的后期运营,增加用户粘性,防止用户流失,如何做好签到是产品应该关注的问题。本文将讲解一种基于Redis位运算的签到功能后端实现方式。

需求分析

签到功能往往涉及以下几个子功能:

  • 用户登陆后签到

  • 后端返回用户某一天或者一段时间的签到情况

  • 获得某段时间的签到天数

  • 获得用户连续签到的最大天数

数据库选择

数据库选择往往有两种:Mysql 和 Redis

Mysql 的实现同学们可以参考以下链接:

https://blog.csdn.net/wxs55555/article/details/72674856

但是Mysql实现方式对于用户量较大的应用来说有很大弊端:引用这位博主的实验解释

https://mrhelloworld.com/redis-sign/#mysql

根据查询结果我们做一个简单的计算:

  • 1 个用户签到一天会产生 0.02MB 数据

  • 每个月都按 30 天来计算的话,1 个用户连续签到一个月会产生 0.60MB 数据

  • 1 个用户连续签到一年会产生 7.20MB 数据

  • 1000W 签到狂魔连续签到一年会产生 7200W MB 数据(7200W MB ÷ 1024 ÷ 1024 ≈ 68.66TB

根据结果大家自行查询各大云厂商的数据库存储空间,对下面这个结果你还满意吗🤣?P.S. 我都没怎么选配置,只是选了 6000GB(大约 5.86TB)

选用Redis实现能够很好地解决这个问题

Redis 与 BitMap位图

对于用户签到数据,如果每条数据都用K/V的方式存储,当用户量大的时候内存开销是非常大的。而位图(BitMap)是由一组bit位组成的,每个bit位对应0和1两个状态,虽然内部还是采用String类型存储,但Redis提供了一些指令用于直接操作位图,可以把它看作是一个bit数组,数组的下标就是偏移量。它的优点是内存开销小、效率高且操作简单,很适合用于签到这类场景。

Redis提供了以下几个指令用于操作位图:

考虑到每月初需要重置连续签到次数,最简单的方式是按用户每月存一条签到数据(也可以每年存一条数据)。Key的格式为uid:yyyyMM,Value则采用长度为4个字节(32位)的位图(最大月份只有31天)。位图的每一位代表一天的签到,1表示已签,0表示未签。

Python 实现

import redis
from datetime import datetime
import calendar

class Author:
 def __init__(self, id, name):
 self.id = id
 self.name = name

class AuthorSignDemo:
 # 创建Redis连接
 pool = redis.ConnectionPool(host='localhost', port=6379, decode_responses=True)
 r = redis.Redis(connection_pool=pool)

 def __init__(self, author):
 self.author = author

 # 某天签到
 def do_sign(self, offset=None):
 key = self.author.id + ':' + str(datetime.now().year) + str(datetime.now().month)
 offset = datetime.now().day-1 if offset is None else offset - 1
 self.r.setbit(key, offset, 1)

 # 查询某天签到情况
 def check_sign(self, day):
 offset = day-1
 key = self.author.id + ':' + str(datetime.now().year) + str(datetime.now().month)
 return True if self.r.getbit(key, offset)==1 else False

 # 签到列表
 def sign_list(self):
 mdate = datetime.now().strftime('%Y-%m')
 for i in range(calendar.monthrange(datetime.now().year, datetime.now().month)[1]):
 date = mdate + '-' + str(i+1)
 print(date + '\t' + ('√' if self.check_sign(i+1) else '-'))

 # 签到天数统计
 def get_sign_count(self):
 key = self.author.id + ':' + str(datetime.now().year) + str(datetime.now().month)
 return self.r.bitcount(key)

 # 连续签到天数
 def get_continuous_sign_count(self):
 key = author.id + ':' + str(datetime.now().year) + str(datetime.now().month)
 continues_count = 0
 res = 0
 for i in range(calendar.monthrange(datetime.now().year, datetime.now().month)[1]):
 if(self.check_sign(i+1)):
 continues_count += 1
 if(continues_count>res):
 res = continues_count
 else:
 continues_count = 0
 return res

# 测试
if __name__ == '__main__':
 author = Author('10000', '张三')
 zs = AuthorSignDemo(author)
 print('用户ID:' + author.id)
 print('用户姓名:' + author.name)

 zs.do_sign(1)
 zs.do_sign(2)
 zs.do_sign(3)
 zs.do_sign(4)

 zs.do_sign(18)
 zs.do_sign(19)
 zs.do_sign(20)
 zs.do_sign(21)
 zs.do_sign()
 print('当月签到次数:' + str(zs.get_sign_count()))
 print('连续签到天数:' + str(zs.get_continuous_sign_count()))
 # print(zs.check_sign(1))
 zs.sign_list()

运行结果如下:

用户ID:10000
用户姓名:张三
当月签到次数:9
连续签到天数:5
2021-11-1  √
2021-11-2  √
2021-11-3  √
2021-11-4  √
2021-11-5  -
2021-11-6  -
2021-11-7  -
2021-11-8  -
2021-11-9  -
2021-11-10  -
2021-11-11  -
2021-11-12  -
2021-11-13  -
2021-11-14  -
2021-11-15  -
2021-11-16  -
2021-11-17  -
2021-11-18  √
2021-11-19  √
2021-11-20  √
2021-11-21  √
2021-11-22  √
2021-11-23  -
2021-11-24  -
2021-11-25  -
2021-11-26  -
2021-11-27  -
2021-11-28  -
2021-11-29  -
2021-11-30  -
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容