前言

不会吧不会吧, 都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
}

结语

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

到此这篇关于PHP利用redis位图实现简单的签到功能的文章就介绍到这了,更多相关redis签到内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
  • Redis中统计各种数据大小的方法
  • 利用Redis统计网站在线活跃用户的方法
  • PHP使用redis位图bitMap 实现签到功能
  • java redis 实现简单的用户签到功能
  • SpringBoot使用Redis的zset统计在线用户信息
  • 基于Redis位图实现用户签到功能
  • Redis基于Bitmap实现用户签到功能
  • SpringBoot整合Redis实现访问量统计的示例代码
  • 微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计功能
  • Redis如何统计用户访问量
  • SpringBoot+Redis BitMap实现签到与统计的项目实践

点赞(947) 打赏

评论列表 共有 0 条评论

暂无评论

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部