当前位置:首页 > PHP教程 > php图像处理 > 列表

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开始计数);

  1. 127.0.0.1:6379> setbit zero 0 1     // 设置第0位为true 
  2. (integer) 0 
  3. 127.0.0.1:6379> getbit zero 0 
  4. (integer) 1 
  5. 127.0.0.1:6379> setbit zero 8 1     // 设置第8位为true 
  6. (integer) 0 
  7. 127.0.0.1:6379> getbit zero 8 
  8. (integer) 1 
  9. 127.0.0.1:6379> getbit zero 7       // 前面会自动补位 
  10. (integer) 0 
  11.  
  12. // 通过python方法获取 h 的二进制 
  13. >>> bin(ord("h")) 
  14. '0b1101000' 
  15. 127.0.0.1:6379> set one h         // 直接存入字符 h = 01101000 
  16. OK 
  17. 127.0.0.1:6379> getbit one 0 
  18. (integer) 0 
  19. 127.0.0.1:6379> getbit one 1 
  20. (integer) 1 
  21. 127.0.0.1:6379> getbit one 2 
  22. (integer) 1 
  23. 127.0.0.1:6379> getbit one 3 
  24. (integer) 0 
  25. 127.0.0.1:6379> getbit one 4 
  26. (integer) 1 
  27. 127.0.0.1:6379> getbit one 5 
  28. (integer) 0 

功能实现

看完上面的基础知识大家就都基本知道怎么实现了, 下面是实战环节

需求

以自然周为周期进行签到;

展示签到周期;

重复签到提示报错;

流程图

PHP利用redis位图实现简单的签到功能

签到周期获取

获取每个自然周的起始和结束时间

  1. // SignStartEndTime 签到起始时间&结束时间. 
  2. func (slf *TaskService) SignStartEndTime() (startTime, endTime int64) { 
  3.     now := time.Now() 
  4.     weekDay := int(now.Weekday())  
  5.   // 如果是周日的话weekDay=0 
  6.     if weekDay == 0 {   
  7.         weekDay = 7 
  8.     } 
  9.     startTime = time.Date(now.Year(), now.Month(), now.Day()-weekDay+1, 0, 0, 0, 0, now.Location()).Unix() 
  10.     endTime = startTime + 86400*7 - 1 
  11.     return 

签到存储key

因为是以自然周为单位, 所以设定key的格式为 user:sign:userId:20221107(周一的日期)

  1. // SignKey 签到key. 
  2. func (slf *TaskService) SignKey(userId string) string { 
  3.     st, _ := slf.SignStartEndTime() 
  4.     return fmt.Sprintf("user:sign:%s:%s", userId, time.Unix(st, 0).Format("20060102")) 

签到

  1. const ( 
  2.     SignTypeNo  = iota // 未签到 
  3.     SignTypeYes        // 已签到 
  4. // Sign 签到. 
  5. func (slf *TaskService) Sign(userId string) (code errcode.ErrCode, err error) { 
  6.     var ( 
  7.         client  = redis.GetClient() 
  8.         key     = slf.SignKey(userId) 
  9.         weekDay = int64(time.Now().Weekday()) 
  10.     ) 
  11.     if weekDay == 0 { 
  12.         weekDay = 7 
  13.     } 
  14.     val, err := client.SetBit(key, weekDay, SignTypeYes).Result() 
  15.     if err != nil { 
  16.         return errcode.ServerErr, fmt.Errorf("sign setBit err: %v", err) 
  17.     } 
  18.     if val == SignTypeYes { 
  19.         return 已签到错误码, fmt.Errorf("sign already, val: %d", val) 
  20.     } 
  21.   // 如果是第一次签到需要加个过期时间 
  22.     signDay := client.BitCount(key, nil).Val() 
  23.     if signDay == 1 { 
  24.         client.Expire(key, time.Hour*24*7) 
  25.     } 
  26.     if err = 签到成功获取的奖励; err != nil { 
  27.         // 如果添加奖励失败, 重置签到状态 
  28.         if err = client.SetBit(key, weekDay, SignTypeNo).Err(); err != nil { 
  29.             log.Errorf("sign reset err: %v", err) 
  30.         } 
  31.         return errcode.ServerErr, fmt.Errorf("sign add reward err: %v", err) 
  32.     } 
  33.     return errcode.Code200, nil 

结语

签到任务看着简单, 但实际上确实也不难, 但是我们在实现的时候要考虑到如何有效的节约和利用资源, 哪种实现方式会更好更优雅一些;

Tags: PHP签到功能 redis位图签到

分享到: