Redis中常见的基础和高级数据结构

Redis

数据类型

  • eg:大写代表属于redis的关键字,小写代表可填值
String
  • 定义:存储字节序列(二进制安全的字符串),包括文本、序列化对象和二进制数组,并允许实现计数器和bit操作。作为Redis中其他数据类型的存储单元,如:ListSetHashes
  • 命令:命令 | 文档 — Commands | Docs
    • SET key value:设置键值对
      • 命令参数:
        • nx:如果键已存在则失败,可以实现简易的不可重入的分布式锁
        • xx:当键已存在时才成功
        • ex:设置过期时间
    • GET key value:获取value值
    • GETSET key value:设置键值对并获取旧值
    • MSET key1 value1 key2 value2 ...:设置多个键值对
    • MGET key1 key2 key3:获取多个value值
    • INCR key:value自增1,具有原子性
List
  • 定义:存储元素的链表,实现栈和队列,以便快速向一个非常长的列表中添加访问,但不注重中部元素的访问。
  • 命令:命令 | 文档 — Commands | Docs
    • LPUSH key1 member1 key2 member2 ... :将元素添加到列表头部
    • RPUSH key1 member1 key2 member2 ... :将元素添加到列表尾部
    • LPOP key:从列表头部移除并返回一个元素
    • RPOP key:从列表尾部移除并返回一个元素
    • LLEN key:返回列表的长度
    • LREM key count element:删除前countmember等于element的元素
      • count > 0:从头部到尾部开始移除
      • count < 0:从尾部到头部开始移除
      • count = 0:移除所有member等于element的元素
    • LMOVE source destination source-side destination-side :原子地将元素从一个列表的一端(LEFT | RIGHT)移动到另一个列表的一端(LEFT | RIGHT
      • 应用:RPOP命令可以实现生产者-消费者模型,但无法确保可靠性,可以在消费者收到消息时选择LMOVE命令将消息转移到另一个List中,当消费者处理完消息再删除该List中的消息
    • LRANGE key start stop:从列表中提取指定范围的元素列表
      • stop为-1时表示最后一个元素的位置
    • LTRIM key start stop:将列表缩减为指定的元素范围
    • BRPOP/BLPOP key1 key2 ... timeout :跟LPOP/RPOP相比,当列表为空时可以阻塞,只有当列表中添加了新元素,或者达到用户指定的超时时间时,才会返回给调用者。
      • timeout为0时表示永久等待
      • 等待多个列表,会选择第一个有元素的列表进行操作
      • 返回值是一个包含两个元素的数组(键值对)
      • 如果超时,返回null。
    • BLMOVE source destination source-side destination-side timeout:跟LMOVE相比,当 source 为空时,Redis 将阻塞连接,直到另一个客户端向其推送元素或直到 timeout
  • 使用场景:
    • 记录用户在社交网络上发布的最新更新
    • 生产者-消费者的通信模型
Set
  • 定义:存储无序且唯一的元素的集合,可以执行集合操作(交并补)
  • 命令:命令 | 文档 — Commands | Docs
    • SADD key member:添加新元素
    • SREM key member1 member2 ...:移除指定的元素
    • SISMEMBER key member:判断member是否属于集合
    • SINTER key1 key2 ...:返回多个集合的交集
    • SUNION key1 key2 ...:返回多个集合的并集
    • SCARD key:返回集合大小
    • SSCAN key cursor [matchPattern] count:根据matchPattern扫描出匹配的count个元素
Hashes
  • 定义:存储键值对的集合
  • 命令:命令 | 文档 — Commands | Docs
    • HSET key field value:设置一个字段-值对
    • HGET key field:获取一个字段值
    • HMSET key1 field1 key2 field2:设置多个字段-值对
    • HMGET key1 field1 key2 field2:获取多个字段-值对
    • HINCRBY key field number:将给定字段的值增加提供的整数。
Sorted Set
  • 定义:相比Set,每个元素都关联一个score(分数),并依据score进行排序,如果score相同则按照字典序排序
  • 命令:命令 | 文档 — Commands | Docs
    • ZADD key score member:设置键值对及其score
    • ZREM key member:移除元素
    • ZINCRBY key number member:给指定元素加分
    • ZREMRANGEBYSCORE key start stop:移除并返回指定分数范围的元素列表
    • ZRANGE key start stop:提取指定排名范围的元素列表(有序)
      • 命令参数:
        • WITHSCORES:不仅返回值还有socre
    • ZRANGEBYSCORE key start stop:提取指定分数范围的元素列表
      • 命令参数:
        • WITHSCORES:不仅返回值还有socre
    • ZRANK key number:返回指定元素的排名(从低到高)
    • ZREVRANK key number:返回指定元素的排名(从高到低)
  • 使用场景:
    • 排行榜:数据量不大,要求有序,实时性要求较高
Streams(流)
  • 定义:类似一个仅追加的日志,存储条目(包含多个键值对,并关联一个具有自增性的id)的列表

  • 命令:命令 | 文档 — Commands | Docs

    • XADD key id field1 value1 field2 value2 ... [MAXLEN ~ count]:添加一个条目(包含多个字段-值对)并为其分配一个id

      • 如果id为"*"将自动生成,构成:时间戳-序列号

      • 序列号用于区分在同一毫秒内生成的 id

      • id必须保证自增

      • MAXLEN:可选参数,指定流的最多条目数,当条目数超过该值时,会移除最旧的条目。

        ~也是可选,表示流保留count个条目,但不保证条目数一定不会超过count个,用来减缓插入高峰时因移除操作导致的长时间阻塞

    • XREAD [COUNT count] [BLOCK ms] STREAMS key1 key2 ... id1 id2 ... :从指定位置(每个key对应一个起始id)开始,读取一个或多个条目,可指定阻塞多少ms(BLOCK ms

      • ms为0时,表示永不超时,当监听到其中一个流的条目时解除阻塞
      • id为"$"时,表示流中已存储的最大条目Id
      • 结合以上两点可只接收到从开始监听时起的新条目
    • XRANGE key startId endId [COUNT count]:读取指定范围内(startIdendId)的一个或多个条目

      • 如果id为-+ :分别表示流中条目可能的最小id 和最大id
      • startId可以加前缀"(",表示不包括startId
      • endId可以加后缀")",表示不包括endId
    • XREVRANGE key endId startId [COUNT count]:与XRANGE的方向相反

    • XLEN key:返回流的长度

    • XDEL key id1 id2 ...:删除流中的某些条目

  • 消费者组:

    • 定义:XREAD命令会将新条目分发给每一个监听的消费者,现在我们有一个新的需求,希望不同的条目分发给不同的消费者,所以建立一个消费者组作为路由负责转发条目到消费者组的其中一个消费者,保证消费者组中的消费者各自接收不同的条目

    • 消费者组的状态信息:

      +-----------------------------------------------+
      | consumer_group_name: mygroup                  |
      | consumer_group_stream: somekey		        |
        # 消费者组最后一次从流中接收并传递给某个消费者的条目id		 
      | last_delivered_id: 1292309234234-92           |
      |                                               |
      | consumers:      						        |
           # 已接收但还未确认为已处理的条目
      |    "consumer-1" with pending messages         |
      |       1292309234234-4                         |
      |       1292309234232-8                         |
      |    "consumer-42" with pending messages        |
      |       ... (and so forth)                      |
      +-----------------------------------------------+
      
    • 命令:

      • 创建消费者组:XGROUP cREATE key gruop id [MKSTREAM]
        • group:消费者组的名称
        • id:消费从该id**之后(不包括该id)**的条目
        • MKSTREAM:可选项,如果流不存在则为其自动创建
      • 从消费者组的未确认条目列表(当条目被传递给某个消费者时,它就是未确认的)中删除一个或多个条目:XACK key group id1 id2 ...
      • 通过消费者组读取条目并将其转发给其中一个消费者:XREADGROUP GRUOP group consumer [COUNT count] [BLOCK ms] STREAMS key1 key2 ... id1 id2 ...
        • consumer:在当前消费者组中创建的消费者的名称
        • key:这里的key必须是消费者组中监听的流
        • id:
          • 普通id:返回从该id之后的当前消费者组中的未确认条目列表
          • “>”:读取当前消费者组还未接收的条目,并更新其last_delivery_id
        • XREADGROUP 是一个写命令,因为它虽然是从流中读取,但会修改消费者组的状态,所以它只能被调用在主节点上
      • XPENDING key group startId endId count :返回消费者组对流中待确认条目的信息(待确认条目的总数、待确认条目中的最低和最高id,消费者列表及其待确认条目总量),consumer表示仅显示该消费者的待确认条目
    • 使用思路:先消费历史记录(未确认条目的列表)。因为消费者之前可能已经崩溃,所以在重启的情况下,先重新读取那些已经接收但未确认的消息。一旦历史记录被消费完毕,待处理条目的列表为空,此时消费者组中的消费者可以使用特殊id > 来监听流。

    • 从永久性故障中恢复:有些消费者可能出现永久故障的情况,此时,Redis的消费者提供一种认领机制,允许其他消费者认领该故障消费者中待确认的条目列表。

      • XCLAIM key group consumer min-idle-time id1 id2 ... JUSTID:将指定的条目(id)分配给指定的消费者,可指定可接收条目的最小空闲时间(ms)。

        • XLAIM会重置其空闲时间,这避免了同一时间下条目的重复分配s情况。

        • JUSTID表示仅返回成功认领的条目id

      • XAUTOCLAIM key group consumer min-idle-time startId [COUNT count] JUSTID:扫描消费者组(group)中超过指定的空闲时间的未确认条目列表,并将其传递给消费者(consumer)。会返回下一次 XAUTOCLAIM 调用的 startId 参数和已认领的消息。

    • 投递计数器:每个条目都会记录一个投递次数,每次通过XLAIMXREADGROUP成功传递消息时,投递次数都会加一。

      • 作用:用来解决消费者持续无法处理的消息,此时可以将其投递到另一专门用来处理这些死信消息的流中。
    • 流的可观察性:除了之前的XPENGDING命令,还有以下几个命令来更具体的观察流的信息:

      • XINFO STREAM key:展示流的详细信息及其消费者组的个数
      • XINFO GROUPS key:展示流的消费者组的状态
      • XINFO CONSUMERS key group:展示流的某个消费者组的消费者状态
  • 特殊ID:不同的API有自己可用的特殊ID

    • “-“和”+”:用于XRANGE,表示流中条目可能的最小id 和最大id(无限小和无限大)
    • “$”:用于XREADXGROUP CREATE,表示当前流中已存在条目的最大id
    • “>”:用于XREADGROUP,使其读取当前消费者组还未接收的条目
    • “*”:用于XADD,表示添加时自动且递增地生成条目的ID
  • Redis的阻塞模型原理:

    • 当客户端执行阻塞操作时,Redis 会把这个客户端的信息记录在一个哈希表中。这个哈希表的键是“要获取的key”,值是“等待该 key 的客户端列表”
    • 当调用 XADD 向某个 Stream 写入数据时,会调用内部函数 signalKeyAsReady()。这个函数不会立即唤醒阻塞客户端,而是把这个key放入一个"就绪 key 列表"中
    • 等到 事件循环即将结束时 再统一处理这些就绪key,对每个key找到所有等待这个key的阻塞客户端,如果有满足条件的新数据(比如 Stream 中的消息 ID 大于客户端请求的 ID),就把这些数据写入客户端的输出缓冲区
  • 使用场景:追踪、监控,日志

Geospatial indexes(地理空间索引)
  • 定义:存储坐标并进行搜索,适用于在给定半径或边界框内查找附近的点

  • 命令:命令 | 文档 — Commands | Docs

    • GEOADD key longitude(经度) latitude(纬度) member:添加元素并指定其位置(经纬度

    • GEOSEARCH key <FROMMEMBER member | FROMLONLAT longitude latitude>  <BYRADIUS radius <M | KM | FT | MI> | BYBOX width height <M | KM |
        FT | MI>> 
      

      查找给定位置(某个元素的位置 或 指定经纬度)的某个范围(多少半径 或 多宽多高)内的元素

Bitmaps(位图)
  • 定义:面向bit位操作的String类型数据,初始情况下,任何未显示设置的bit位值为0

  • 命令:命令 | 文档 — Commands | Docs

    • SETBIT key offset value:将指定的offset的位设置为0或1
    • GETBIT key offset:获取指定的offset的位的值
    • BITOP key1 key2 ...:在不同的String数据之间进行按位操作(与 | 或 | 异或 | 非)
    • BITCOUNT key start end :返回设置为1的位数
    • BITPOS key bit start end :返回第一个位的值为bitoffset
  • 使用场景:

    • 对于一个具有紧密连续性的数据集合(如:1,2,3,5…),判断每个元素是否具有某种功能或特性时,可以有效地降低存储占用,如用户签到功能
    • 需要进行频繁的集合操作的业务,如标签分类功能
  • 使用策略:

    • 数据分片:为了避免处理巨大的键,可以将位图拆分到不同的键中,一个简单的策略是每个键存储 M 个位,并通过 bit-number/M 获取键名,通过 bit-number MOD M 获取键内的第 N 个位来访问。这里的bit-number指数据标识编号,比如数据库中的用户表的用户id
Bitfields
  • 定义:允许设置、递增或获取任意位长下的整数值

  • 命令:BITFIELD | 文档 — BITFIELD | Docs

    • BITFIELD key <GET encoding offset | SET encoding offset value | INCRBY encoding offset increment > <GET encoding offset | SET encoding offset value | INCRBY encoding offset increment > ...
      

      GET:从指定的offset开始读取一定长度的二进制序列,并根据encoding将其解释为一个有符号或无符号的整数

      SET :根据encoding将其转化为二进制序列,然后将其从指定的offset开始按位写入

      INCRBY:根据encoding将指定的increment作为整数加到从offset读取后的值上,结果按 encoding转成二进制序列,从 offset开始按位写回字符串

      GET 在当前字符串长度之外寻址,会导致缺失的部分用0填充。SETINCRBY在当前字符串长度之外寻址,会将字符串扩展到所需的最小长度

      可以在一条命令上进行多种操作(SETGETINCRBY

      encoding

      • 有符号位整数:前缀i + 数字 (i8:8位有符号位整数)

      • 无符号位整数:前缀u + 数字 (u8:8位无符号位整数)

JSON
  • 定义:将String解析为JSON格式进行处理。

  • 优势(与直接将JSON数据保存在Redis的String相比)

    • 使用Redis的JSON可以在不通过网络传输整个对象的情况下访问和修改嵌套值
    • 不需要重新序列化整个对象
    • 可以使用Redis查询引擎的索引和查询功能

    eg:Redis 8.0版本以下需要安装RedisJson模块才能使用,目前已不再维护,介意直接使用Redis 8.0及以上的版本

  • 命令:命令 | 文档 — Commands | Docs

    • JSON.SET key path value:设置一个JSON值及其路径
    • JSON.STRLEN key path:返回字符串的长度
    • JSON.STRAPPEND key path value: 追加一个字符串到末尾
    • JSON.GET key path value:获取JSON值
    • JSON.DEL key path:删除JSON,path默认为$
    • 数组:
      • JSON.ARRAPPEND key path value1 value2 ...:向JSON数组的末尾追加元素
      • JSON.ARRINSERT key path index value1 value2 ...:向JSON数组的第index个位置插入元素
      • JSON.ARRTRIM key path start stop:只保留startstop索引范围内的元素
      • JSON.ARRPOP key path index:根据指定的索引移除并返回一个元素
    • 对象:
      • JSON.OBJLEN key $:返回JSON对象的键值数
      • JSON.OBJKEYS key $:返回JSON对象的key列表
  • Path:Path | Docs

    • 定义:JSON中,Path能将JSON进行分层,如:{ "user": { "name": "艾露" } },根为"user",第二层(嵌套)为"name"。Path的目的在于快速查找提取JSON结构中的特定元素,而非修改。

    • 关键字:

      语法元素 示例表达式 返回结果(解释)
      $ $ 整个JSON根对象
      .[] $.store.book 查找根对象下的book对象
      .. $..price 查找所有 price,无论嵌套在哪层
      * $.store.* 返回 store 中所有子元素:bookbicycle
      [](下标) $.store.book[1] 获取第 2 本书
      [,](并集) $.store.book[0,2] 获取第 1 本和第 3 本书
      [start:end:step] $.store.book[::2] 每隔一本取一本(第 1 和第 3 本)
      ?()(过滤) $.store.book[?(@.price < 12)] 获取价格低于 12 的书
      ()(脚本表达式) $.store.book[?((@.price + 2) > 10)] 价格+2后大于10的书
      @(当前元素) $.store.book[?(@.author == "Author 2")] 获取作者是 Author 2 的书

      可使用以下命令创造JSON数据测试其效果(根据Redis或RedisJSON版本,可能不支持最后三种关键字):

      JSON.SET store $ '{ "store": { "book": [ { "title": "Book A", "price": 10, "author": "Author 1" }, { "title": "Book B", "price": 15, "author": "Author 2" }, { "title": "Book C", "price": 8, "author": "Author 3" } ], "bicycle": { "color": "red", "price": 19.95 } } }'
      
  • 格式化JSON的输出:

    $ redis-cli --raw
    > JSON.GET obj INDENT "\t" NEWLINE "\n" SPACE " " $
    [
    	{
    		"name": "Leonard Cohen",
    		"lastSeen": 1478476800,
    		"loggedOut": true
    	}
    ]
    
HyperLogLog
  • 定义:概率性数据结构,用于估算一个数据集中「不重复元素的总量」。在损失一定精确度的情况下减少内存消耗(不需要使用与计数项目数量成比例的内存量,而是恒定的内存量)

  • 命令:命令 | 文档 — Commands | Docs

    • PFADD key element1 element2 ...:向HyperLogLog添加元素
    • PFCOUNT key1 key2 ...:返回所有HyperLogLog中不重复元素总量的近似值
    • PFMERGE destkey sourcekey1 sourcekey2 ...:将所有和HyperLogLog进行合并
  • 使用场景:

    • 统计某网页一天的独立访客数(UV),即使同一用户访问多次也只计一次
    • 统计某广告的曝光用户数
Bloom filter(布隆过滤器)
  • 定义:概率性数据结构,用于快速检查集合中某个元素是否存在,注重减少内存消耗即使用固定大小的内存空间。因为哈希冲突,所以可能误判(计算出的下标位置会出现重复,导致不存在的元素被错误地判定为存在)
  • 原理:基于**bitmap(位图)实现,对于一个给定的 key,通过多个不同的 hash 函数 计算得到多个 hash 值,每个 hash 值对 m(bitmap 的大小) 进行 取模运算,得到对应的 bitmap 下标位置,并将这些位置的 bit 值设置为 1。读取时,只需要判断其对应的多个下标位置的元素值是否都为1**即可
  • 命令:命令 | 文档 — Commands | Docs
    • BF.BESERVE key error_rate capacity [EXPANSION expansion] NONSCALINGBloom filter的预配置

      • error_rate:错误率(如0.001,平均每1000个中有一个错误)

      • capacity:预期容量,设置过小会导致过滤器"填满",需要创建子过滤器,此时查询延迟会增加(需逐层检查)。设置过大会浪费内存。

      • EXPANSION

        • Bloom filter添加元素不会因为"填满"而失败,但错误率会开始增长。为了将错误率保持在预配置的水平,Bloom filter会自动扩展,即创建一个额外的子过滤器,并为其添加更多的哈希函数。
        • 新子过滤器的大小是最后一个子过滤器大小的 EXPANSION 倍(默认值是 2)。建议EXPANSION为2或更大值以减少子过滤器的数量。
      • NONSCALING:保证不会扩展出新的子过滤器

    • BF.ADD key value:向Bloom filter添加一个元素

    • BF.EXISTS key value:检查Bloom filter中某个元素是否存在

你可能感兴趣的:(Redis中常见的基础和高级数据结构)