Redis的在此次大项目中的使用 | 青训营

172 阅读4分钟

今天来简单写一点我在做大项目是如何使用redis的

需求:

  1. 点赞、取消赞操作十分频繁导致视频获赞总数获取、修改频繁
  2. 视频id参数校验,判断是否存在此视频(前端防君子不防帅哥)

个人觉得这里应该往redis存入点赞关系,以满足判断是否点赞和未点赞需求,由于笔者项目经验有限,也是第一次使用redis,有很多理解不对和写的不好的地方

实现步骤

  1. 首先肯定是创建redis客户端
  2. 定义存入redis的数据格式并从mysql数据库中获取数据
  3. 封装从redis获取数据的函数
  4. 使用go语言的定时任务定时持久化redis中数据到mysql中

代码实现

  1. 初始化连接和创建redis客户端(以及mysql)
    package global  
    
    import (    
    )  
    
    var (  
        DB *gorm.DB  
        RS *redis.Client  
    )  
    func init() {
        //mysql的连接  网上很多
        //redis的连接  网上很多
    
  2. 定义存入redis的数据格式并从mysql数据库中获取数据(初始化redis)
    var videos []Video
    global.DB.Find(&videos)
    
    // 遍历数据库中的视频数据
    for _, video := range videos {
        key := VideoRedisPrefix + fmt.Sprintf("%d", video.VideoID)
    
        // 准备要存储在 Redis 中的数据
        data := map[string]interface{}{
            "favorite":  strconv.FormatInt(video.FavoriteCount, 10), // 将点赞数转为字符串
            "comment":   strconv.FormatInt(video.CommentCount, 10),  // 将评论数转为字符串
            "liked":     "false",                                    // 初始化点赞状态为 false
            "commented": "false",                                    // 初始化评论状态为 false
        }
        // 使用 Hash Map (HMSet) 存储数据到 Redis
        client.HMSet(key, data)
    }
    
  • 解析:
  1. 查询数据库中的视频数据并存储到 videos 切片中。
  2. 遍历视频数据,为每个视频生成一个唯一的 Redis 键。
  3. 为每个视频生成要存储在 Redis 中的数据,包括点赞数、评论数,以及初始点赞和评论状态。
  4. 使用 Redis 客户端的 HMSet 方法将生成的数据存储到 Redis 中的 Hash Map 数据结构中
  5. 封装从redis获取数据的函数
    // LikeVideo 方法2:点赞操作
func LikeVideo(videoID int64) error {
	key := VideoRedisPrefix + fmt.Sprintf("%d", videoID)

	// 更新点赞状态
	_, err := client.HSet(key, "liked", "true").Result()
	if err != nil {
		if err != nil {
			//如果redis没有则从数据获取并初始化这条视频
			_, _, err = GetVideoFavoriteAndCommentCount(videoID)
			if err != nil {
				return err
			}
		}
	}

	// 增加点赞数
	newCount, err := client.HIncrBy(key, "favorite", 1).Result()
	if err != nil {
		return err
	}

	fmt.Printf("Liked video %d. New favorite count: %d\n", videoID, newCount)
	return nil
}

// UnlikeVideo 方法3:取消点赞操作
func UnlikeVideo(videoID int64) error {
	key := VideoRedisPrefix + fmt.Sprintf("%d", videoID)

	// 更新点赞状态
	_, err := client.HSet(key, "liked", "true").Result()
	if err != nil {
		return err
	}

	// 减少点赞数
	newCount, err := client.HIncrBy(key, "favorite", -1).Result()
	if err != nil {
		return err
	}

	fmt.Printf("Unliked video %d. New favorite count: %d\n", videoID, newCount)
	return nil
}

// AddComment 方法4:评论数+1操作
func AddComment(videoID int64) error {
	key := VideoRedisPrefix + fmt.Sprintf("%d", videoID)

	// 更新评论状态
	_, err := client.HSet(key, "commented", "true").Result()
	if err != nil {
		return err
	}
	// 增加评论数
	newCount, err := client.HIncrBy(key, "comment", 1).Result()
	if err != nil {
		return err
	}

	fmt.Printf("Added comment to video %d. New comment count: %d\n", videoID, newCount)
	return nil
}

func DelComment(videoID int64) error {
	key := VideoRedisPrefix + fmt.Sprintf("%d", videoID)

	// 更新评论状态
	_, err := client.HSet(key, "commented", "true").Result()
	if err != nil {
		return err
	}

	// 减少评论数
	newCount, err := client.HIncrBy(key, "comment", -1).Result()
	if err != nil {
		return err
	}

	zap.S().Error("Unliked video %d. New favorite count: %d\n", videoID, newCount)
	return nil
}
  1. 使用go语言的定时任务定时持久化redis中数据到mysql中
func main() {
	// 连接 Redis 客户端

	//初始化操作:查询数据库并将数据存入 Redis
	util.InitRedisData()
	// 定时任务:每隔5秒将 Redis 数据写入 MySQL 数据库
	fmt.Println("redis初始化成功,定时任务开始执行")
	ticker := time.NewTicker(50 * time.Second)
	defer ticker.Stop()
	for range ticker.C {
		fmt.Println("Writing Redis data to MySQL...")
		WriteRedisVideoToMySQL()
		fmt.Println("Done.")
	}
}

// WriteRedisVideoToMySQL 将 Redis 数据写入 MySQL 数据库
func WriteRedisVideoToMySQL() {
	keys, err := client.Keys(VideoRedisPrefix + "*").Result()
	if err != nil {
		fmt.Println("Error getting keys from Redis:", err)
		return
	}

	for _, key := range keys {
		videoIDStr := key[len(VideoRedisPrefix):]
		videoID, err := strconv.ParseInt(videoIDStr, 10, 64)
		if err != nil {
			fmt.Println("Error converting videoID to int:", err)
			continue
		}

		data, err := client.HGetAll(key).Result()
		if err != nil {
			fmt.Println("Error getting data from Redis:", err)
			continue
		}

		liked := data["liked"]
		commented := data["commented"]

		// 仅在点赞数或评论数有变化时更新 MySQL 数据库
		if liked == "true" || commented == "true" {
			// 从 Redis 数据中获取点赞数和评论数,转为 int64 类型
			favoriteCount, _ := strconv.ParseInt(data["favorite"], 10, 64)
			commentCount, _ := strconv.ParseInt(data["comment"], 10, 64)
			fmt.Println(favoriteCount, commentCount, videoID)
			updateData := map[string]interface{}{
				"favorite_count": favoriteCount,
				"comment_count":  commentCount,
			}
			global.DB.Model(&Video{}).Where("video_id = ?", videoID).Updates(updateData)
			fmt.Println("更新数据库")
			//将状态设置为false
			if _, err = client.HSet(key, "liked", "false").Result(); err != nil {
				zap.S().Infof("设置点赞状态异常")
			}
			if _, err := client.HSet(key, "commented", "false").Result(); err != nil {
				zap.S().Infof("设置评论状态异常")
			}
		} 
	}
}