使用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). // 限制返回30条
Find(&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)
}