redis学习笔记(数据结构部分)

redis

官方文档

https://redis.io/docs/getting-started/


概念

REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的key-value 存储系统,是跨平台的非关系型数据库。
Redis 通常被称为数据结构服务器,因为值(value)可以是字符串(String)、哈希(Hash)、列表(list)、集合(sets)和有序集合(sorted sets)等类型。

redis是否可以当作消息组件使用

消息队列的使用场景

消息队列主要用来暂存生产者生产的消息,供消费者使用。
  1. 解耦

    当多个消费者依赖一个生产者时,每次增加或减少消费者都要修改生产者相关的代码。使用消息队列能够减少对生产者的修改。

  2. 异步

    当多个消费者依赖一个生产者时,每个消费者处理信息的速度不一样,这样会导致生产者的效率过低。使用消息队列大大增加了生产者的效率。

  3. 削峰

    在一个时间段过多的请求出现,会导致消费者无法处理甚至奔溃,使用消息队列能够将请求存储,让消费者按照能力来获取消息处理。

使用消息队列需要解决的问题

  • 技术问题

    1. CPU空转

      问题:当消息队列为空时,消费者频繁拉取消息。

      解决方式:

       1、让消费者休眠一段时间(会产生延时)。
       2、通过阻塞式获取消息,即如果队列为空,消费者在拉取消息时就「阻塞等待」,一旦有新消息过来,就通知我的消费者立即处理新消息。
      
    2. 不可重复消费

      问题:当消息被使用后消息就消失。

      解决方式:

       1、在redis中可以使用Pub/Sub方式或stream解决
      
    3. 消息丢失

      问题:如果发生以下场景,就有可能导致数据丢失:

      • 生产者发送信息失败或无法判断消息是否发送成功:

        解决方式:重复发送消息,消息队列对消息进行处理。

      • 消费者下线或出现故障导致信息获取但未处理完成

        解决方式:当消费者处理完成信息后返回成功信息给消息队列

      • 消息队列宕机

        解决方式:将数据持久化(redis通过RDB和AOF方式持久化)

      • 消息堆积

        解决方式:

          1. 生产者限流:避免消费者处理不及时,导致持续积压
          2. 丢弃消息:中间件丢弃旧消息,只保留固定长度的新消息,其他消息可放入数据库等可保存数据的地方
        
  • 业务问题

    1. 增加了系统的复杂性和维护的消耗
    2. 维护者需要有相关消息队列的部署和运维能力

答案

  能否使用消息队列主要看它是否能解决上述问题(即在使用消息队列需要解决的问题)。
  技术方面:
  1、redis在消息堆积做的不是很好,因为redis使用内存存储数据,它能存储的数据比磁盘小。
  2、redis会产生极少量的消息丢失。在使用aof持久化时突然宕机会导致最新的几条命令丢失,
  业务方面:redis的stream使用较为简单,相较其他消息队列较为快速。

  总结:如果业务场景足够简单,对于数据丢失不敏感,而且消息积压概率比较小的情况下,把 Redis 当作队列是完全可以的。

使用建议及注意事项

Key的使用

  1. 不要太长,太长的可以使用hash值
  2. 不要太短,因为key所占的空间相对其他来说较少,命名最好易读
  3. 命名最好统一,官方建议object-type:id,多词组合的命名可以使用.或-,例如“comment🔢reply.to” 或者 “comment🔢reply-to”

通用操作

  1. exists:

    判断该键是否存在

  2. del:

    删除改键

  3. type:

    判断类型

  4. expire(pexpire):

    设置过时时间

  5. ttl(pttl):

    检查剩余的过时时间


数据结构

Strings

  • 使用情景:

      缓存HTML页面
    
  • 主要操作:

    1. set与get操作:

      赋值与获取(其中set会替换已存在的数据)

    2. incr(incrby)与decr(decrby)操作:

      增加和减少(它们是原子操作)

    3. mset与mget操作:

      同时赋值与获取多个值

Lists

  • 实现:

      redis的list通过链表(Linked Lists)实现
    
  • 使用场景:

    1. 社交平台上用户的最新发布的信息
    2. 使用生产者-消费者模式进行通信的程序
  • 主要操作:

    1. rpush(lpush):

      往链表的头(尾)增加数据

    2. rpop(lpop):

      从链表的头(尾)删除数据

    3. brpop(blpop):

      从链表的头(尾)删除数据,为了解决rpop(lpop)中lists为空所造成的问题

    4. lmove(rmove):

      从链表的头(尾)移除数据并放入另一个列表,可以防止因为一些原因(如网络问题)导致的信息丢失

    5. lrange:

      往链表的头(尾)开始引索数据

    6. ltrim:

      往链表的头(尾)开始引索数据并将其他元素丢弃

Hashes

  • 使用场景:

      存储对象(就是存储多个属性值)
    
  • 主要操作:

    1. hset:

      给该hash对象赋值

    2. hget(hmget/hgetall):

      得到对应的单个(多个/所有)的属性的值

Sets

  • 使用场景:

    1. 粉丝、关注、好友
    2. 标签
    3. 黑名单/白名单
  • 主要操作:

    1. sadd:

      为该集合添加元素

    2. smembers

      得到该集合的所有值

    3. sismember

      判断该元素是否存在该集合中

    4. SUNIONSTORE

      获取多个集合的并集,并将该并集赋给另外一个集合

    5. sinter

      获取多个集合的交集

    6. srandmember

      随机获取该集合的指定个数的值

    7. pop

      随机移除该集合的一个值

    8. scard

      获取该集合的元素总量

Sorted sets

  • 使用场景

    1. 排行榜
    2. 优先级
  • 主要操作

    1. zadd

      为该集合增加元素

    2. zrange(zrevrange)

      按分数(或字符串[注:按字符串需要所有的元素的分数为0])正序(倒序)引索该集合中的元素

    3. zrangebyscore

      获取指定分数范围内的元素

    4. zremrangebyscore(zremrangebylex)

      移除指定分数(字符串)范围内的元素,并返回移除数量

    5. zrank(zrevrank)

      按正序(倒序)获取指定元素的引索

    6. zcount(zlexcount)

      统计指定分数(字符串)范围内的元素数量

Bitmaps

  • 实现:

      bitmap不是一种数据结构,底层是string数据结构
    
  • 使用场景:

    1. 用户签到
    2. 实时分析
  • 主要操作:

    1. setbit

      给相应的位数赋值

    2. getbit

      得到相应的位数的值

    3. bitop

      进行位运算,并将结果付给另一个string类型数据结构

    4. bitcount

      统计指定范围内值为1的总数

    5. bitpos

      获取指定元素的引索

HyperLogLogs

  • 使用场景:

    一些要求的精度不高但数据量极大的数据统计,例如,统计google被多少用户访问的次数

  • 主要操作:

    1. pfadd

      增加元素

    2. pfcount

      返回估计的总量

streams

  • 实现:

    注意:与redis其他的数据结构不同,stream删除最后一个item时不会导致该stream的删除(因为stream拥有consumer_group)

      redis stream通过redis tree实现。
      每个redis由多个item组成,每个item由一个id和一个字典(即多个键值对)组成。
      stream通常与Consumer groups协同工作。
      每个Consumer group有自己的名字,对应的流,最后收到的消息id,与它的消费者(包括它拥有的挂起的messages)
      +----------------------------------------+
      | consumer_group_name: mygroup           |
      | consumer_group_stream: somekey         |
      | last_delivered_id: 1292309234234-92    |
      |                                        |
      | consumers:                             |
      |    "consumer-1" with pending messages  |
      |       1292309234234-4                  |
      |       1292309234232-8                  |
      |    "consumer-42" with pending messages |
      |       ... (and so forth)               |
      +----------------------------------------+
      每个流可以有多个Consumer group,每个Consumer group可以有多个consumer
    
  • 使用场景:

    1、业务场景足够简单,对于数据丢失不敏感,而且消息积压概率比较小的情况下,把 Redis 当作队列是完全可以的。

    2、因为redis stream支持时间序列数据,并且跨平台能力强。可以用于IoT数据采集:Redis作为一个嵌入式的存储系统跑在各个IoT设备上,各个设备使用Redis Stream暂存产生的时序数据,然后再异步的推送到云端。

    3、服务器端负载均衡系统,即当多个功能一致的consumer处理大量的message时,可以根据每个consumer的性能来获取相应的message(即性能好的可以处理更多的messages)

  • 查询模式:

    1. 只读取最新得到的数据

    2. 访问指定时间的数据

    3. 通过consumer groups访问,每个consumer获取stream的一个部分

  • 主要操作:

    • 基本操作

      1. xadd

        往stream中增加item(即多个键值对和一个id),id可以指定或自动生成,通过maxlen可以指定stream的长度(超过长度的(最旧的)item会被去除),当使用maxlen时使用~符号能够更加节省资源(因为redis stream是由redis tree组成的,当删除节点时要求修改后面的节点信息,使用该符号能使出现一个空的节点后再删除,虽然这样会导致保留的数据略大于maxlen)

      2. xlen

        计算一个stream中的item个数(注意,一个item里有一个id和多个键值对)

      3. xrange(xrevrange)

        正序(倒序)访问指定id范围内指定个数的item

      4. xread

        以阻塞(通过block)或非阻塞方式监听/获取指定流(流可以有多个)中,指定个数且id大于所规定的id中的item

      5. xdel

        根据id和stream来删除stream中的某个item

    • consumer groups操作

      1. xgroup

        通过指定stream与groups的名字创建Consumer groups

      2. xreadgroup

        通过指定stream,Consumer group和consumer来获取stream中最新的消息(通过>,会让该message挂起[挂起指该message已给了消费者,但消费者没有确认该消息处理完成])或获取该consumer所挂起的所有message(通过0)

      3. xack

        通过指定stream,Consumer group和需要确认信息处理完成的信息来让该message取消挂起

      4. xpending

        通过指定stream,Consumer group来获取该group的挂起情况(通过指定范围和个数可以获得详细信息,不指定则获得概述信息)
        注:在使用该指令获取信息后可用xrange获取该message的更具体的信息。

      5. xclaim

        通过指定stream,Consumer group,需要传给的consumer,超时时间和指定的message的id,来将长时间没有确定的挂起信息传给其他消费者处理(通过justid可以只获取改变的message的id)

      6. xautoclaim

        通过指定stream,Consumer group,需要传给的consumer,超时时间和需要超过的id(自动检测大于id的挂起的message),来将长时间没有确定的挂起信息传给其他消费者处理(通过justid可以只获取改变的message的id)

      7. xinfo

        获取stream,group还有consumer的相关信息

      8. xtrim

        可以指定stream的长度(超过长度的(最旧的)item会被去除),当使用maxlen时使用~符号能够更加节省资源(因为redis stream是由redis tree组成的,当删除节点时要求修改后面的节点信息,使用该符号能使出现一个空的节点后再删除,虽然这样会导致保留的数据略大于maxlen)

  • 主要通配符

      - +  stream数据结构中最小的和最大的id
      $    该stream中已存在message的最大的id
      >    只有XREADGROUP指令使用,表示只接收从来没有发送过的message
      *    只有xadd指令使用,表示自动选择id为新增加的item
    
  • message被使用的延时

      Processed between 0 and 1 ms -> 74.11%
      Processed between 1 and 2 ms -> 25.80%
      Processed between 2 and 3 ms -> 0.06%
      Processed between 3 and 4 ms -> 0.01%
      Processed between 4 and 5 ms -> 0.02%
      So 99.9% of requests have a latency <= 2 milliseconds, with the outliers that remain still very close to the average.
    

    数据来自官方文档

  • 故障与解决:

    1. 消息损坏或该消息在处理程序发生错误

      可以检查该message被传递的次数,如果次数达到一定的量,则将该message传到其他的消息队列,由其他程序(或人)处理

    2. 消息丢失

      配置redis持久化设置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值