大厂程序员使用Redis案例之连续签到

Redis简介

Redis(全称为Remote Dictionary Server)是一个开源的高性能键值对存储系统,具有快速、灵活和可扩展的特性。它是一个基于内存的数据结构存储系统,可以用作数据库、缓存和消息代理。

Redis 支持多种数据结构,包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。这些数据结构提供了丰富的操作命令,使得开发者可以方便地处理各种数据需求。此外,Redis 还提供了两种持久化方式,即快照(Snapshotting)和日志追加(Append-only file,AOF)。快照方式将 Redis 内存数据以二进制格式写入磁盘,而 AOF 则通过追加记录 Redis 的操作命令来实现持久化。

Redis 还支持发布/订阅模式,可以用作消息代理。发布者将消息发送到指定的频道,订阅者则可以接收和处理这些消息。这种模式在构建实时通信、事件驱动系统和消息队列等场景中非常有用。此外,Redis 还可以通过主从复制和分片来实现数据的分布式存储和高可用性。主从复制可以将数据复制到多个从节点,实现读写分离和数据备份。而分片则可以将数据分布在多个 Redis 节点上,实现横向扩展和负载均衡。

案例详解

接下来进入正题,介绍大厂程序员是怎么使用Redis的,第一个经典案例是连续签到。

对于连续签到这个问题想必大家应该都不陌生,逻辑大概是用户每日有一次签到的机会,如果断签,连续签到计数将归0。连续签到的定义:每天必须在23:59:59前签到,也就是说键的过期时间需要设置为后天的0点。下面是用Golang+Redis实现的完整代码:

package main

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/redis/go-redis/v9"
)

var (
	RedisClient *redis.Client
	ctx         = context.Background()
)

const continuesCheckKey = "uid_%d" //存储到redis的key

func init() {
	rdb := redis.NewClient(&redis.Options{
		Addr: "127.0.0.1:6379",
	})
	RedisClient = rdb
}

func main() {

	//通过一些业务逻辑获取uid,这里为了方便演示,直接写死
	uid := 5201314
	Checkin(ctx, uid)
}

// Checkin 连续签到天数
func Checkin(ctx context.Context, uid int) {
	key := fmt.Sprintf(continuesCheckKey, uid)

	//用户在某一天内不能重复签到
	if exists, _ := RedisClient.Exists(ctx, key).Result(); exists == 1 {
		if ttl := RedisClient.TTL(ctx, key).Val(); ttl >= time.Duration(24)*time.Hour {
			fmt.Printf("用户[%d]今天已经签到过了\n", uid)
			return
		}
	}

	// 1. 连续签到数+1
	err := RedisClient.Incr(ctx, key).Err()
	if err != nil {
		fmt.Printf("用户[%d]连续签到失败", uid)
		return
	} else {
		expAt := beginningOfDay().Add(48 * time.Hour)
		// 2. 设置签到记录在后天的0点到期
		if err := RedisClient.ExpireAt(ctx, key, expAt).Err(); err != nil {
			panic(err)
		} else {
			// 3. 打印用户续签后的连续签到天数
			day, err := getUserCheckInDays(ctx, uid)
			if err != nil {
				panic(err)
			}
			fmt.Printf("用户[%d]连续签到:%d(天), 过期时间:%s", uid, day, expAt.Format("2006-01-02 15:04:05"))
		}
	}
}

// getUserCheckInDays 获取用户连续签到天数
func getUserCheckInDays(ctx context.Context, userID int) (int, error) {
	key := fmt.Sprintf(continuesCheckKey, userID)
	days, err := RedisClient.Get(ctx, key).Result()
	if err != nil {
		return 0, err
	} else {
		return strconv.Atoi(days)
	}
}

// beginningOfDay 获取今天0点时间
func beginningOfDay() time.Time {
	now := time.Now()
	y, m, d := now.Date()
	return time.Date(y, m, d, 0, 0, 0, 0, time.Local)
}

解释分析:

  1. 导入包:
    • context:提供了上下文用于处理请求的截止时间、取消信号等。
    • fmt:用于格式化输出。
    • strconv:用于字符串和基本数据类型之间的转换。
    • time:用于时间相关操作。
    • github.com/redis/go-redis/v9:这是一个 Go 语言的 Redis 客户端库。
  2. 全局变量和常量:
    • RedisClient:用于与 Redis 数据库进行交互的客户端。
    • ctx:一个全局的上下文变量。
    • continuesCheckKey:一个常量字符串模板,用于构建存储在 Redis 中的用户签到数据的键名。
  3. init() 函数:
    • 初始化 Redis 客户端连接,连接到本地 Redis 服务器。
  4. main() 函数:
    • 为了演示,直接写定了一个用户ID(5201314),然后调用了 Checkin() 函数。
  5. Checkin() 函数:
    • 接受上下文和用户ID作为参数。
    • 构建用户签到的键名 key
    • 检查用户今天是否已经签到,如果已签到则判断是否距离上次签到超过24小时,如果是则表示今天已经签到,不再处理。
    • 如果用户未签到,计算签到记录过期时间 expAt,为今天的零点后48小时。使用 Incr() 命令递增用户的连续签到数,并设置过期时间为 expAt
    • 调用 getUserCheckInDays() 函数获取用户的连续签到天数,并格式化输出。
  6. getUserCheckInDays() 函数:
    • 根据用户ID构建签到数据键名 key
    • 使用 Get() 命令获取用户的连续签到天数(以字符串形式存储)。
    • 使用 strconv.Atoi() 将获取到的字符串转换为整数返回。
  7. beginningOfDay() 函数:
    • 返回当前日期的零点时间。

你可能感兴趣的:(Go,#,Redis,数据库,redis,数据库,golang)