PHP利用redis位图实现简单的签到功能
发布:smiling 来源: PHP粉丝网 添加日期:2023-10-06 16:52:52 浏览: 评论:0
在日常开发中, 我们会遇到需要存储大量 bool类型数据的需求, 比如用户签到和用户登陆的记录等, 本文将为大家介绍如何利用redis位图轻松实现签到功能,感兴趣的可以了解一下。
不会吧不会吧, 都2202年了还有人不会写签到? redis位图实现签到功能简单方便, 走过路过可不要错过呦!
基础知识
位图源头
在日常开发中, 我们会遇到需要存储大量 bool类型数据的需求, 比如用户签到和用户登陆的记录等, 这个时候用mysql存储来说比较占用资源, 所以为了解决这个问题, redis提供了位图数据结构(就是 位数组), 每个 bool值只占用1个位, 8个位组成一个字节, 这样存储空间的节约率不用我多说吧;
Mysql占用对比
mysql 存一个thinyint需要占用1个字节(bool类型默认为thinyint(1)), 而且你还需要存一个主键Id(你不存也会自动隐性的帮你存一列), int的话需要占用 4个字节, 不算其他光是这两个字段存储你就需要5个字节, 而在位图里面5个字节都够存40条记录了...
使用
需要先知道以下几点:
位图的内容实际上也就是字符串, 只不过是更改的个位的内容, 所以分为零存零取和整存零取;
位图的位数是会自动补位的, 比如你设置一个空键第8位为1, 则会自动补充前8位为0 (数组从0开始计数);
- 127.0.0.1:6379> setbit zero 0 1 // 设置第0位为true
 - (integer) 0
 - 127.0.0.1:6379> getbit zero 0
 - (integer) 1
 - 127.0.0.1:6379> setbit zero 8 1 // 设置第8位为true
 - (integer) 0
 - 127.0.0.1:6379> getbit zero 8
 - (integer) 1
 - 127.0.0.1:6379> getbit zero 7 // 前面会自动补位
 - (integer) 0
 - // 通过python方法获取 h 的二进制
 - >>> bin(ord("h"))
 - '0b1101000'
 - 127.0.0.1:6379> set one h // 直接存入字符 h = 01101000
 - OK
 - 127.0.0.1:6379> getbit one 0
 - (integer) 0
 - 127.0.0.1:6379> getbit one 1
 - (integer) 1
 - 127.0.0.1:6379> getbit one 2
 - (integer) 1
 - 127.0.0.1:6379> getbit one 3
 - (integer) 0
 - 127.0.0.1:6379> getbit one 4
 - (integer) 1
 - 127.0.0.1:6379> getbit one 5
 - (integer) 0
 
功能实现
看完上面的基础知识大家就都基本知道怎么实现了, 下面是实战环节
需求
以自然周为周期进行签到;
展示签到周期;
重复签到提示报错;
流程图

签到周期获取
获取每个自然周的起始和结束时间
- // SignStartEndTime 签到起始时间&结束时间.
 - func (slf *TaskService) SignStartEndTime() (startTime, endTime int64) {
 - now := time.Now()
 - weekDay := int(now.Weekday())
 - // 如果是周日的话weekDay=0
 - if weekDay == 0 {
 - weekDay = 7
 - }
 - startTime = time.Date(now.Year(), now.Month(), now.Day()-weekDay+1, 0, 0, 0, 0, now.Location()).Unix()
 - endTime = startTime + 86400*7 - 1
 - return
 - }
 
签到存储key
因为是以自然周为单位, 所以设定key的格式为 user:sign:userId:20221107(周一的日期)
- // SignKey 签到key.
 - func (slf *TaskService) SignKey(userId string) string {
 - st, _ := slf.SignStartEndTime()
 - return fmt.Sprintf("user:sign:%s:%s", userId, time.Unix(st, 0).Format("20060102"))
 - }
 
签到
- const (
 - SignTypeNo = iota // 未签到
 - SignTypeYes // 已签到
 - )
 - // Sign 签到.
 - func (slf *TaskService) Sign(userId string) (code errcode.ErrCode, err error) {
 - var (
 - client = redis.GetClient()
 - key = slf.SignKey(userId)
 - weekDay = int64(time.Now().Weekday())
 - )
 - if weekDay == 0 {
 - weekDay = 7
 - }
 - val, err := client.SetBit(key, weekDay, SignTypeYes).Result()
 - if err != nil {
 - return errcode.ServerErr, fmt.Errorf("sign setBit err: %v", err)
 - }
 - if val == SignTypeYes {
 - return 已签到错误码, fmt.Errorf("sign already, val: %d", val)
 - }
 - // 如果是第一次签到需要加个过期时间
 - signDay := client.BitCount(key, nil).Val()
 - if signDay == 1 {
 - client.Expire(key, time.Hour*24*7)
 - }
 - if err = 签到成功获取的奖励; err != nil {
 - // 如果添加奖励失败, 重置签到状态
 - if err = client.SetBit(key, weekDay, SignTypeNo).Err(); err != nil {
 - log.Errorf("sign reset err: %v", err)
 - }
 - return errcode.ServerErr, fmt.Errorf("sign add reward err: %v", err)
 - }
 - return errcode.Code200, nil
 - }
 
结语
签到任务看着简单, 但实际上确实也不难, 但是我们在实现的时候要考虑到如何有效的节约和利用资源, 哪种实现方式会更好更优雅一些;
Tags: PHP签到功能 redis位图签到
- 上一篇:详解php如何合并身份证正反面图片为一张图片
 - 下一篇:最后一页
 
推荐文章
热门文章
最新评论文章
- 写给考虑创业的年轻程序员(10)
 - PHP新手上路(一)(7)
 - 惹恼程序员的十件事(5)
 - PHP邮件发送例子,已测试成功(5)
 - 致初学者:PHP比ASP优秀的七个理由(4)
 - PHP会被淘汰吗?(4)
 - PHP新手上路(四)(4)
 - 如何去学习PHP?(2)
 - 简单入门级php分页代码(2)
 - php中邮箱email 电话等格式的验证(2)
 
