使用Redis优化关注列表和消息记录 | 青训营

89 阅读4分钟

使用Redis优化关注和取消关注 | 青训营

Redis从内存中取数据,所以读取速度快,能让程序快速反应

r.Use(func(c *gin.Context) {
        c.Set("rdb",rdb)
        c.Next()
    }) // 注册数据库连接中间件

思路

思路是将要传递的user结构体转换成字符串数据存储在redis里,需要使用的时候再取出来。

token验证和查询用户都可以使用Redis优化数据

使用Redis的存储思路如图:

存储结构: token验证:

key:"token" field:ID value:token

用户查询:

key:"user" field:token value: 用户

关注表

key:用户ID field:关注用户ID value: 关注用户

粉丝列表

key:用户token field:粉丝用户ID value:粉丝用户

用用户ID可以访问用户的token进行验证,用户的token可以取出用户本体

关注列表用用户的ID作为key,被关注的用户ID作为field,value为关注用户的json数据

关注列表用用户的token作为key,粉丝用户ID作为field,value为粉丝用户的json数据

user.go 在登录和注册的时候将用户数据注册进redis的哈希结构中,用 HSetNX(分布式锁)的方式存储,防止重复操作

 //登录成功后,将token缓存到redis
    rdb := c.MustGet("rdb").(*redis.Client)
    idStr := strconv.FormatInt(user.Id, 10) //将用户id转成string型
    err = rdb.HSetNX(context.Background(),"token",idStr, user.Token).Err()//分布式锁,存储token验证
    if err != nil {
    panic(err)
    }
    //将user存入缓存
    NewUser,err := json.Marshal(user) //将user转成byte[]型字符串数据
    if err != nil {
        panic(err)
        }
    err=rdb.HSetNX(context.Background(),"user",user.Token,string(NewUser)).Err()//分布式锁,string(NewUser)将byte转成string型存入Redis
    if err != nil {
    panic(err)
    }

在relation.go中实现关注,取消关注操作

// RelationAction 关注和取消关注操作
func RelationAction(c *gin.Context) {
    
    rdb := c.MustGet("rdb").(*redis.Client)
​
    token := c.Query("token")
    toUserId,err := strconv.ParseInt(c.Query("to_user_id"), 10, 64)
    if err != nil {
        panic(err)
        }
    action_type := c.Query("action_type")
​
    var user User
    //检验令牌
    CheckToken, err := rdb.HGet(context.Background(),"user", token).Result()
    if err != nil {
        panic(err)
        }
    err = json.Unmarshal([]byte(CheckToken),&user)
    if err != nil {
    panic(err)
    }
    //自己不能关注自己
    if toUserId == user.Id{
        c.JSON(http.StatusOK, Response{StatusCode: 1, StatusMsg: "You can't follow yourself"})
        }   
​
    if token == user.Token{
        //执行关注或取消关注操作
        to_user_id := strconv.FormatInt(toUserId, 10)
        var ToToken string 
        ToToken, err = rdb.HGet(context.Background(),"token", to_user_id).Result()//拿到对方用户token
        if err != nil {
            fmt.Println(toUserId)
            panic(err)
            }
            var ToUser User
        CheckToken, err = rdb.HGet(context.Background(),"user", ToToken).Result() //使用token查user
        if err != nil {
            panic(err)
            }
        err = json.Unmarshal([]byte(CheckToken),&ToUser) //得到对方用户数据
        if err != nil {
            panic(err)
            }
        //数据转换
        idStr := strconv.FormatInt(user.Id, 10)
        ToUser_Id:= strconv.FormatInt(ToUser.Id, 10)
        NewToUser,err := json.Marshal(ToUser) //json
        if err != nil {
                panic(err)
        } 
        switch action_type{
        case "1":    //加入关注列表,储存在redis中
            err = rdb.HSetNX(context.Background(),idStr, ToUser_Id, string(NewToUser)).Err()
            if err != nil {
            panic(err)
             }   
        case"2" :  //从关注列表中移除
            err = rdb.HDel(context.Background(),idStr, ToUser_Id).Err()
            if err != nil {
                panic(err)
                 }
        }
        c.JSON(http.StatusOK, Response{StatusCode: 0})
    } else {
        c.JSON(http.StatusOK, Response{StatusCode: 1, StatusMsg: "User doesn't exist"})
    }
}
​

首先query传进来了token,to_user_id,action_type三个参数,其中token是关注者的token,所以从key"token"中取出token 进行对比,action_type是关注和取消关注操作,to_user_id是被关注者的ID,从key:“user”中拿出被关注用户的结构体。

关注和需要被关注的ID先进行对比,自己是不能关注自己的。

然后使用switch来判断是关注还是取消关注。

当是进行关注操作的时候,将被关注者存入到关注者的关注表中

然后用户关注列表和粉丝列表进行类似的取值操作就完成了

而Redis实现朋友列表功能只需要返回关注和被关注列表就行了

用户关注列表更新:

//json字符串转成user结构体传入userlist
    if len(data) != 0{  
        for _, val := range data {
            var user User
            err := json.Unmarshal([]byte(val) , &user)
            if err != nil{
                panic(err)
            }
            userlist = append(userlist, user)
          }
    } 
   
    c.JSON(http.StatusOK, UserListResponse{
        Response: Response{
            StatusCode: 0,
        },
        UserList: userlist,
    })
​

少用的一些方法

切片数组增添元素操作

userlist = append(userlist, user)

将整形转成string类型

idStr := strconv.FormatInt(user.Id, 10)

user.Id从整数转换为十进制表示的字符串。参数10表示使用十进制进行转换

传递空上下文context

context.Background()

结构体转byte[]类型json,byte[]转string

NewToUser,err := json.Marshal(ToUser) //结构体转json
Newstring := string(NewToUser) //byte[]转string

string转byte[]类型json,byte转结构体

err := json.Unmarshal([]byte(val) , &user) //string类型的val转成[]byte,再转成结构体user

Mysql强类型查询

db.Where(User{Token: token}).Select("id").First(&id) 

如果表结构体中有自定义的类型记得要预加载,否则数据库不会读取,Preload("Author"),Author是自定义类型的变量名

result := db.
        Where("upload_time < ?", latest_time).Preload("Author").// 投稿时间过滤
        Order("upload_time desc").             // 按照投稿时间倒序
        Limit(30).                             // 限制返回30Find(&videos)

int转string

User_ID := strconv.FormatInt(user.Id, 10)

string转int

toUserId,err := strconv.ParseInt(to_user_id, 10, 64) //10进制,64字节

Redis一些知识

Set是设置,Get是查询,GetSet设置之后查询旧的,NX分布式锁

string类型 key value

hash类型 key field value

Redis实现聊天消息功能,储存思路如图:

因为下面的储存结构中key有ID存在,所以需要存储到新的数据库中,否则可能会与上面的数据产生冲突:

储存消息:

key:chatKey field:MsgID value:messge列表

储存消息顺序ID:

key:发送消息用户ID field: 接收消息用户ID value:messageId

Redis切换存储数据库

Redis一共有划分十六个编号的存储数据库,分别是0到15号。

命令行切换命令为:

 Select 1 

选择数据库第二个数据库,数据库编号为1

在go中可以使用do函数进行使用Redis命令来切换数据库:

    _, err = rdb.Do(context.Background(), "Select", 0).Result() //切换到第一个数据库存储 
    if err != nil {
        panic(err)
    }