Redis各数据类型应用场景

84 阅读6分钟

string

普通缓存

  • 整个JSON字符串存储,代码里面unmarshall解析
127.0.0.1:6379> set mazi '{"age":12,"addr":"anhui"}' ex 10
OK
127.0.0.1:6379> get mazi
"{\"age\":12,\"addr\":\"anhui\"}"
  • key隔离,val是对应的值 比如某个用户id对应的等级
keyval
level:$uid1
127.0.0.1:6379> mset zhangsan 18 lisi 19 
OK
127.0.0.1:6379> mget zhangsan lisi
1) "18"
2) "19"

计数器

因为Redis是单线程操作,所以执行命令是原子性的,适合计数场景,比如计算访问次数、点赞、转发、库存数量等等

127.0.0.1:6379> mset zhangsan 18 lisi 19
OK
127.0.0.1:6379> mget zhangsan lisi
1) "18"
2) "19"
127.0.0.1:6379>
127.0.0.1:6379> incr zhangsan
(integer) 19
127.0.0.1:6379> incr wanger //不存在的不报错 自动创建 从0开始
(integer) 1
127.0.0.1:6379> incrby lisi 10
(integer) 29

分布式锁

setnx命令可以做分布式锁使用
加锁

func (d *Idempotent) getNxLock(ctx context.Context, key string, value int) (res bool, err error) {
   //过期时间固定为5秒
   res, err = d.redis.SetNX(ctx, key, value, time.Second*lockExpire).Result()
   //如果设置成功返回true 没成功返回false
   if err != nil {
      log.Errorc(ctx, "[SetLock] d.xRedis.SetNX failed ,err(%+v)", err)
      return
   }
   return
}
func (s *Idempotent) getRedisLock(ctx context.Context) (res bool, lockVal int, err error) {
   key := s.getLockKey()
   rand.Seed(time.Now().Unix())
   lockVal = rand.Intn(100) + 1
   res, err = s.getNxLock(ctx, key, lockVal)
   if err != nil {
      log.Errorc(ctx, "[getRedisLock] failed ,err(%+v)", err)
      return
   }
   if !res {
      //加报警日志
      log.Warnc(ctx, "[getRedisLock] lock been taken key(%s) lockVal(%d)", key, lockVal)
   }
   return
}

解锁
在方法退出之前的defer 中添加解锁操作

//操作完成解锁
func (s *Idempotent) Unlock(ctx context.Context) {
   //说明没有获取到锁
   if s.lockVal == 0 {
      return
   }
   key := s.getLockKey()
   err := s.unlock(ctx, key, s.lockVal)
   if err != nil {
      log.Errorc(ctx, "[Unlock] s.dao.Unlock failed ,err(%+v)", err)
      return
   }
}
func (d *Idempotent) unlock(ctx context.Context, key string, value int) (err error) {
   res, err := d.redis.Get(ctx, key).Int()
   if err != nil && err != redis.ErrNil {
      log.Errorc(ctx, "[Unlock] d.xRedis.Get failed ,key(%s) err(%+v)", key, err)
      return
   }
   //说明锁已经没有了
   if err == redis.ErrNil {
      err = nil
      return
   }
   //说明目前锁已经是别人的了
   if res != value {
      log.Warnc(ctx, "[Unlock] lock already been token key(%s) old(%d) now(%d)", key, value, res)
      //不做错误处理,直接返回
      return
   }
   //释放锁
   cmd, err := d.redis.Del(ctx, key).Result()
   if err != nil {
      log.Errorc(ctx, "[Unlock] del failed ,err(%+v) key(%s) return(%d)", err, key, cmd)
      return
   }
   return
}

list

消息队列

lpush + rpop 组合可以实现前进先出的消息队列

127.0.0.1:6379> lpush myhobby sing sport read
(integer) 3
127.0.0.1:6379> rpop myhobby
"sing"

想要主动及时读取消息,可以使用阻塞式读取,brpop key timeout 命令表示阻塞timeout时间等待消息,时间过了,命令结束,不再等待

127.0.0.1:6379> lpush myhobby sing sport read
(integer) 3
127.0.0.1:6379> rpop myhobby
"sing"
127.0.0.1:6379> brpop myhobby 5 //设置阻塞5秒,因为list里面目前有元素所以立即返回
1) "myhobby"
2) "sport"
127.0.0.1:6379> brpop myhobby 50 //继续立即返回
1) "myhobby"
2) "read"
127.0.0.1:6379> brpop myhobby 50 //等待50秒没有元素,直接返回
(nil)

获取一段范围内的val

127.0.0.1:6379> lpush myhobby 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange myhobby 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lrange myhobby 0 2
1) "5"
2) "4"
3) "3"

删除某个元素
lrem key count val

  • count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
  • count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
  • count = 0 : 移除表中所有与 VALUE 相等的值
127.0.0.1:6379> lpush myhobby 5
(integer) 6
127.0.0.1:6379> lrange myhobby 0 -1
1) "5"
2) "5"
3) "4"
4) "3"
5) "2"
6) "1"
127.0.0.1:6379> lrem myhobby 2 5 //从表头开始删除2个val为5的元素
(integer) 2
127.0.0.1:6379> lrange myhobby 0 -1
1) "4"
2) "3"
3) "2"
4) "1"

修改某个元素
lset key index val 修改索引index位置出的元素

127.0.0.1:6379> lrange myhobby 0 -1
1) "4"
2) "3"
3) "2"
4) "1"
127.0.0.1:6379> lset myhobby 0 7 //修改位置0 的值为7
OK
127.0.0.1:6379> lrange 0 -1
(error) ERR wrong number of arguments for 'lrange' command
127.0.0.1:6379> lrange myhobby 0 -1
1) "7"
2) "3"
3) "2"
4) "1"

hash

对象有多种属性

一个有多种属性的对象,filed 对应属性

127.0.0.1:6379> hmset profile name lisi age 28 sexuality female
OK
127.0.0.1:6379> hgetall profile
1) "name"
2) "lisi"
3) "age"
4) "28"
5) "sexuality"
6) "female"
127.0.0.1:6379> hdel profile age name
(integer) 2
127.0.0.1:6379> hset profile name zhangsan age 36
(integer) 2
127.0.0.1:6379> hmget profile name age
1) "zhangsan"
2) "36"
127.0.0.1:6379> hlen profile
(integer) 3
127.0.0.1:6379> hincrby profile age 4
(integer) 40
127.0.0.1:6379> hincrby profile age -4
(integer) 36

购物车

keyfieldval
用户ID收藏商品id商品数量

image.png 图片地址 xiaolincoding.com/redis/data_…

127.0.0.1:6379> hset cart:21700655 100 1 //添加商品id为100 的商品
(integer) 1
127.0.0.1:6379> hincrby cart:21700655 100 2 //添加数量
(integer) 3
127.0.0.1:6379> hlen cart:21700655 //获取收藏商品总数
(integer) 1
127.0.0.1:6379> hgetall cart:21700655 //获取所有购物车商品
1) "100"
2) "3"
127.0.0.1:6379> hdel cart:21700655 100   //删除id为100 的商品
(integer) 1

set

特点是 去重,无序,支持交并差

点赞

对一篇文档每个人只能点赞一次

127.0.0.1:6379> sadd article:1 zhangsan lisi wanger  //张三 李四 王二 点赞了文章
(integer) 3
127.0.0.1:6379> smembers article:1 //查看所有点赞文章的用户
1) "wanger"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> srem article:1 zhangsan //张三取消了点赞
(integer) 1
127.0.0.1:6379> scard article:1 //文档点赞用户数
(integer) 2
127.0.0.1:6379> sismember article:1 zhangsan //张三是否点赞了文章
(integer) 0
127.0.0.1:6379> sismember article:1 lisi
(integer) 1

共同关注

支持交集,所以可以查找共用拥有元素

127.0.0.1:6379> sadd uid:1 zhangsan lisi //用户1关注了 张三 李四
(integer) 2
127.0.0.1:6379> sadd uid:2 lisi wanger //用户2 关注了 李四 王二
(integer) 2
127.0.0.1:6379> sinter uid:1 uid:2 //两个人共同关注
1) "lisi"
127.0.0.1:6379> sdiff uid:1 uid:2 //找到用户1 关注的 推荐给用户2
1) "zhangsan"

抽奖活动

如果一个人只能有一次中奖机会,中完即删

127.0.0.1:6379> sadd pool zhangsan lisi wanger //员工添加到中奖池
(integer) 3
127.0.0.1:6379> srandmember pool 2 //随机抽两个一等奖,可以重复中奖
1) "wanger"
2) "zhangsan"
127.0.0.1:6379> smembers pool //全部都在
1) "wanger"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> spop pool 2 // 随机两名,不能重复,直接剔除
1) "wanger"
2) "lisi"
127.0.0.1:6379> smembers pool
1) "zhangsan"

zset

特点 比较set 多了根据score值排序

排行榜

127.0.0.1:6379> zadd rank 10 uid:1 20 uid:20 30 uid:30 30 uid:40 //添加成员
(integer) 4
127.0.0.1:6379> zrange rank 0 -1 //获取所有成员
1) "uid:1"
2) "uid:20"
3) "uid:30"
4) "uid:40"
127.0.0.1:6379> zscore rank uid:1 //查询某个成员的分数
"10"
127.0.0.1:6379> zcard rank //获取排行榜成员数
(integer) 4
127.0.0.1:6379> zincrby rank 2 uid:1 //排行榜中成员1 分值增加2
"12"
127.0.0.1:6379> zrem rank uid:3 //删除成员3
(integer) 0
127.0.0.1:6379> zrange rank 0 1 withscores //获取排行榜最后两名用户 附加分值返回
1) "uid:1"
2) "12"
3) "uid:20"
4) "20"
127.0.0.1:6379> zrevrange rank 0 -1 withscores //倒序返回排行榜信息,一般应用按照分高到低展示排序 用这个命令
1) "uid:40"
2) "30"
3) "uid:30"
4) "30"
5) "uid:20"
6) "20"
7) "uid:1"
8) "12"
127.0.0.1:6379> zrangebyscore rank 30 100 //找到分值30-100 的所有用户
1) "uid:30"
2) "uid:40"

命令大全

类型长度
stringset
mset
incr
incrby
delsetget/
listlpushlremlset
lrange 查范围
lindex 查某个索引
rpop 弹出元素
llen
hashhset
hmset
hincrby 修改field值
hdelhset
hincrby 修改field值
hget
hgetall
hlen
setsaddsrem/srandmember 随机返回
sismember 是否是成员
spop 随机弹出
sinter 交集
sdiff 差集
scard
zsetzadd
zincrby 修改元素分数
zrem 删除元素
zremrangebyscore 删除分数区间元素
zremrangebyrank 删除排名区间元素
zadd 修改分数
zincrby 修改元素分数
zrank 某个成员排名
zrange 正序范围排名
zrevrange 倒序排名
zrangebyscore 分数区间内成员
zcard