redis 五种数据结构使用场景举例

一.string 字符串数据结构使用场景

  1.单值缓存

set key value
get key

  2. 对象缓存

(1) set user:1  value(json对象)
(2) mset user:1:name  zhangsan user:1:balacnce  1888
    mget user:1:name

  第一种方法用于读频繁的,第二种方法用于修改比较频繁的,只需要修改某个字段,而不是整个一条记录对象

  3. 分布式锁

setnx product:10001 true
setnx product:10001 false

   返回为1的结果表示执行成功,还可能设置锁超时(10秒)  setnx product:10001 true  ex 10 nx

  4. 计数器

incr article:readcount :{文章id}
get article:readcount :{文章id}

    比如用于文章阅读记数。

  5. web集群session共享

    比如使用asp.net core session

  6. 分布式系统全局序列号

   incr orderid

    比如:分库分表中的主键id, 用自增长id不好解决。用redis自增长来维护主键id。但在并发量高时,每次都与redis交互,redis的资源也有限。 可以先批量生成比如(1~1000)放入内存,在程序中累加。

   incrby orderid 1000

      7.api访问限流  

    目的:会员id:10001_action,请求api接口,一分钟不允许超过30次,下面是伪代码

//不存在key,设置key
if(!exists(10001))
{
     //value为访问次数, 60为过期秒数
     setnx(10001,1,60); 
}
else
{
     if(get(10001)>30)
         return "一分钟不允许超过30次";
     //添加访问次数
     incr(10001);
}

    

 

 

二. hash 哈希数据结构应用场景

  1.对象缓存 格式(hset key field value) 

    下面是一个user表,对应的存储如下所示:

  

  hmset  user   1:name  zhuge   1:balance 1888
  hmget  user   1:name  1:balance

   上面这个存在问题,由于redis内部使用单线程操作,将一个user表的数据全部存入一个key中,这样应用程序端多线程操作同一个key时会出现阻塞。

       解决办法使用数据分段:例如对user表创建10个hash,   每个hash的key,分别是 user01,user02.. user10。 存储时,对user表id取模,存入到不同的hash  key中.

  2.电商购物车

    以用户id为key  , 商品id为field, 商品数量为value, 如下所示:

 

     购物车操作:

      1) 添加商品  hset cart:1001  10088 1

      2) 增加数量 hincrby cart:1001  10088 1

      3) 商品总数 hlen  cart:1001

      4) 删除商品 hdel cart:1001 10088

      5) 获取购物车所有商品  hgetall cart:1001

  3. hash的优点

    1)同类数据归类整合储存,方便数据管理

    2) 相比string操作消耗内存与cpu更小

    3) 相比string存储更节省空间

  4. hash的缺点

    1)过期功能不能使用在field上,只能用在key上

    2) redsi集群架构下不适合大规模使用。因为集群采用的是分片。而分片存储是对key取模定向到不同的数据节点。同一个key只会使用数据全部集中在一个节点。解决:考虑数据分段hash key      

三. list 列表数据结构应用场景

  格式: lpush key value[value...]     从左边(前)存放

      rpush key value[value...]     从右边(后)存放

      lpop key    从左边取

      rpop key    从右边取

  常用的数据结构

    stack 栈(先进后出)  lpush     +  lpop 

    query 队列(先进先出) lpush + rpop

    blocking mq(阻塞队列) lpush + brpop

  1. 微博消息和微信公众号消息

    比如:“张三(id为10)”关注了二个公众号, mactalk, 备胎说车,当发文时,向关注的粉丝们发送消息。

    1.mactalk发文,消息id为10018

      lpush msg:10:10018

    2.备胎说车发文,消息id为10086

     lpush msg:10  10086

    3. 张三查看最新

     lpush msg:10   0    5   

     取出索引5条

 

 

              注意:当关注的粉丝数在万以下可以考虑上面的方式,因为redis  单机的QPS能达到10w,插入w条数据也是很快。当某些大V的粉丝数达到100w或1000w这种方式就不行。

  2.lpush + rpop 队列(先进先出)

    它一般应用在异步消息队列。平时我们习惯于使用rabbitmq或kafka作为消息队列中间件,在应用程序之间增加异步消息传递功能,但使用过rabbitmq的知道使用起来比较复杂,发消息之前要创建Exchange,再创建Queue,还要将Queue与Exchange通过某种规则 绑定起来,发消息的时候要指定routing-key,还要控制头部信息。消费者在消费之前也要进行上面一系列的烦琐过程。

    有的redis,可以解脱出来,对于那些只有一组消费者的消息队列,使用redis可以非常轻松搞定,需要注意的是,redis的消息队列不是专业的消息队列,它没有非常多的高级特性(如消息优先级),没有ack保证,如果对消息的可靠性有着极高要求,那么它就不适合使用。

    客户端在rpop操作来获取消息,然后进行处理,如此循环,如果队列空了,需要让客户端暂停1秒再取,不然redis的qps会被拉高。如果有多个客户端,可以考虑使用brpop替代rpop。 前缀b代表blocking也就是阻塞读,阻塞读在队列没有数据的时候,会进入休眠状态,一是数据到来,则立即醒过来,消息的延迟几乎为零。

四. set 集合数据结构应用场景

  格式  sadd key member [member ...] 

     srem key member[member..] 

  1. 微信抽奖小程序

    1)点击参与抽奖加入集合

      sadd key {userID}

    2)查看参与抽奖所有用户

      smembers key

    3)抽取count名中奖者

      srandmember key  [count]/sopop key [count] 

 

   比如:上面业务抽奖获得实点书籍 共2条,有6个用户参考了该活动

sadd act:101  1888
sadd act:101  1889
sadd act:101  1890
sadd act:101  1891
sadd act:101  1892
sadd act:101  1893

  通过以下命令可以随机获取二个中奖的用户,每次执行结果都不一样。结果不会从集合中删除

 srandmember   act:101  2       //atc:101是key 

  下面命令是获得抽奖的用户,不能再次抽奖, 结果会从集合中删除

  spop act:101  3  //取三个用户

  2. 微信点赞,收藏,标签

   1)点赞

    sadd  like:{消息id} {用户id}

    2)取消点赞

    srem like:{消息id} {用户id}

   3) 检查用户是否点过赞

    sismember  like:{消息id} {用户id}

   4) 获取点赞的用户列表

    smembers  like:{消息id} 

   5) 获取点赞用户数

    scrad like like:{消息id} 

 

 

  3.集合操作

 

     sinter set1  set2  set3  -->{ c}     //交集

     suntion set1 set2  set3 -->{a,b,c,d,e}   //并集

     sdiff set1 set2 set3 -->{a}    //差集

    集合操作实现微博微信关注 模型

     1)诸葛老师关注的人:

       zhugeset -->{yangguo ,sima,luban} 

    2) 杨过老师关注的人

      yanguo set-->{zhuge,sima,luban,guojia}

    3) 我和杨过老师共同关注:

      sinter zhugeset yugguoset  -->{sima,luban}

    4)我关注的人也关注他(杨过老师)

      sismember simaset yangguo

      sismember lubanset yangguo

    5) 我可能认识的人

      sdiff yangguoset zhugeset ->(zhuge,guojia)

   4.集合操作实现电商商品筛选

  sadd brand:huawei p30
  sadd brand:xiaomi mi-6x
  sadd brand:iphone iphone8
  sadd os:android p30  mi-6x
  sadd cpu:brand:intel  p30 mi-6X
  sadd ram:8G p30  mi-6x  iphone8

  sinter os:android  cpu:brand:intel  ram:8G -->{ p30, mi-6X}

 

   

五. Zset 有序集合数据结构应用场景

  1.延迟队列实现

    延迟队列可以通过redis的zset(有序集合)来实现,将消息序列化成一个字符串作为zset的value, 消息的到期处理时间作为score,然后用多个线程轮询zset获取到期的任务进行处理,因为多个线程是为了保障可用性,万一挂了一个线程还有其他线程可以继续处理,因为有多个线程,所以需要考虑并发争抢任务,确保任务不会被多次执行。

    伪代码如下所示:

----------生产端---------------
var value=jsonstr;
 
//s:2108191942   延时5分钟
var s = DateTime.Now.AddMinutes(5).ToString("yyMMddHHmm");
 
redis.ZADD("delay-queue",s,value);

----------消费端---------
while(true)
{
         var s = DateTime.Now.ToString("yyMMddHHmm");
         var values=   redis.zrangebyscore("delay-queue",0,s,0,1);
         if(value=="")
          continue;
         var value=  values[0];
         //从消息队列中移除该消息
         var success=redis.zrem("delay-queue",value)
        //最后value反序列化出来,进行业务处理 
        .....
}
            

    redis的zrem方法是多线程多进程争抢任务的关键,它的返回值决定了当前实例有没有抢到任务,因为loop方法可能会被多个线程,多个进程调用,要通过zrem来决定唯一的属主。

    参考资料 : .net core中使用redis 实现延迟队列 

   

  2.粉丝关注时间排序和学生成绩名次排序

    zset可以用来存储粉丝列表,value值是粉丝的用户ID,score是关注时间,我们可以对粉丝列表按关注时间进行排序。

    同样也可以用来存储学生的成绩,value值是学生的ID,score是学生的成绩,我们对成绩按分数进行排序就可以得到他的名次。

   3. zset限流

    参考资料 https://blog.csdn.net/XIANZHIXIANZHIXIAN/article/details/107744599

 

 

posted on 2021-06-16 21:02  花阴偷移  阅读(351)  评论(0编辑  收藏  举报

导航