Redis②数据类型、发布/订阅


1、基本命令

Redis 命令中心:讲解 Redis 数据类型的常用命令。

1.1、key

  • 查询
    • keys *:当前库的所有 key。
    • exists key:判断指定 key 是否存在。
    • type key:指定 key 的类型。
  • 删除
    • del key:删除指定 key 的数据。
    • unlink key:将 key 从 keyspace 元数据中删除,非阻塞删除(异步)。
  • 过期时间
    • expire key:设置过期时间(秒)。
    • ttl key:查看存活时间(time to live,秒),-1 永不过期,-2 已过期。

1.2、数据库

Redis 默认有 16 个数据库,默认使用 0 号库。

  • select:切换数据库。
  • dbsize:查看当前库的 key 数量。
  • flushdb:清空当前库。
  • flushall:清空所有库。

2、数据类型

  • key-value:Key 是字符串,Value 可以是不同的数据类型。
  • 数据类型的操作具有原子性(由于单线程)。

1.1、String(字符串)

Redis 最基本的数据类型

  • 二进制安全,可包含任何数据(如序列化对象)
  • String 类型的 Value,最大值是 512M

1.1.1、数据结构

简单动态字符串(Simple Dynamic String,SDS)

  • 类似 Java 的 ArrayList

  • 内存分配:预分配冗余空间(capacity),减少内存的频繁分配。

  • 扩容:字符串长度(len)

    • len < 1M 则加倍,len > 1M 则扩容 1M。

    • 最大长度是 512M

      image-20220414004131113

1.1.2、常用命令

① 存取

  • set:添加键值对。

  • setex:添加键值对,并设置过期时间(秒,expire)。

  • setnx:添加键值对,前提是指定 Key 不存在(not exists)。

  • get:查询指定 Key 的 Value 值。

  • mset:添加多个键值对。

  • msetnx:添加多个键值对,前提是指定的所有 Key 都不存在。

  • mget:查询指定的多个 Key 的 Value 值。

    set 
    setex <过期时间>
    setnx 
    get 
    # multi
    mset 
    msetnx 
    mget 
    

② 范围

类似子串(subString),索引从 0 开始。

  • getrange:指定 Key,获取指定下标范围的值(左右包含)。

  • setrange:指定 Key,覆盖指定起始下标之后的值。

    getrange <起始索引><结束索引>
    setrange <起始索引>
    

③ 数字增减

只能操作数字值,原子性。

  • incr:自增 1,若为空则赋值 1。

  • decr:自减 1,若空则赋值 -1。

  • incrby:自增指定步长。

  • decrby:自减指定步长。

    incr/decr 
    incrby/decrby <步长>
    

④ 其它

  • 替换:查询指定 Key 值,并将 Value 设为指定值。

  • 追加:将指定 Value 值,追加到指定 Key 的 Value 值之后。

  • 长度

    getset 
    append 
    strlen 
    

1.1.3、应用场景

  • 验证码、token(expire)
  • 点赞或访问计数(incr / decr)

1.2、List(列表)

单键多值:简单的字符串列表

有序、有下标、可重复

底层是双向链表

  • 直接操作 List 的头部(左)和尾部(右)元素,性能高。
  • 通过下标操作中间元素,性能较低。

1.2.1、数据结构

快速链表(quickList)

  • 元素较少时,使用压缩列表存储(ziplist,一块连续的内存)

  • 元素较多时,使用快速链表(链表和 ziplist 的结合)

    • ziplist 不利于增删操作。

    • 普通链表的指针需要额外占用空间,浪费内存。

    • 结合使用,满足增删的性能,减少内存冗余。

      image-20220414005707377

1.2.2、常用命令

① 存取

  • 两侧

    • lpush/rpush:向指定 Key 的左/右侧,添加指定一个或多个值。

    • lpop/rpop:从指定 Key 的左/右侧取出一个值。

    • rpoplpush:从指定 Key 的右侧取出值,添加到另一个 Key 的左侧。

      lpush/rpush 
      lpop/rpop 
      rpoplpush 
      
  • 下标:从左往右

    • lindex:获取指定 Key,指定下标对应的值。

    • linsert before/after:在指定 Key 的指定 Value 前/后,添加指定指定值。

    • lset:替换指定 Key 的指定下标的 Value 值。

    • lrem:指定 Key,删除左侧开始的 n 个 Value。

      lindex 
      linsert  before/after 
      lrem 
      lset
      

② 范围、长度

  • lrange:指定 Key,获取指定下标范围的值(左右包含)。

    • 下标从 0 开始,负数表示倒数。
    • 0 -1 表示所有 Value。
  • llen:获取指定 Key 的列表长度。

    lrange 
    llen 
    

1.2.3、应用

  • 发布、订阅
  • 消息队列

1.3、Hash(哈希)

键值对集合

  • hash 是一个 String 类型的 field 和 value 的映射表。
  • 适合存储对象,类似 Java 的 Map

1.3.1、数据结构

两种数据结构:ziplist(压缩列表)、hashtable(哈希表)

  • field-value 长度短且个数少,使用 ziplist。
  • 否则使用 hashtable。

1.3.2、常用命令

① 查询

  • hkeys:列出该 hash 集合的所有 field。

  • hvals:列出该 hash 集合的所有 value。

  • hexists:指定 Key 中,判断指定 field 是否存在。

    hkeys 
    hvals 
    hexists 
    

② 存取

  • hset:指定 Key,给指定 field 赋值。

  • hget:从指定 Key 中取出指定 field 的 value 值。

  • hsetnx:指定 Key,给指定 field 赋值。(前提是 field 不存在)

  • hmset: 指定 Key,批量设置多对 field-value 值。

    hset 
    hget 
    hsetnx 
    hmset .
    

③ 递增

指定 Key,将 field 的数值递增指定步长。

hincrby 

1.3.3、应用

存储对象:Key 为对象名,Value 为 Hash。

image-20220414122326057

为何不用 String 存储?

image-20220414140704365

  1. String-String:Key 为对象名,Value 为序列化的对象。

    • 存储:序列化整个对象并存储。

    • 问题:修改对象需要先反序列化,修改后再次序列化(开销大

      image-20220414122255083

  2. String-String:Key 为对象名+属性名,Value 为属性值。

    • 存储:分别将属性值存储在不同 Key 下。

    • 问题:存储需要使用多个 Key,获取对象时需查询多个 Key(数据冗余

      image-20220414122301137

1.4、Set(集合)

单键多值、排序

无序、无下标、不可重复

  • 底层是 value 为 null 的 hash 表。
  • 增删查的时间复杂度 O(1)

1.4.1、数据结构

dict 字典(由哈希表实现)

  • Java 的 HashSet:底层 HashMap,所有 value 指向同一个对象。

  • Redis 的 set:底层 hash,field-value 中的 value 为 null。

    image-20220414125321893

1.4.2、常用命令

① 查询

  • smembers:查询所有值。

  • srandmember:随机查出 n 个值。

  • sismember:判断是否存在。

  • scard:元素个数(称为基数,cardinality)。

  • spop:随机取出一个值(删除)

    # 仅查询
    smembers 
    srandmember 
    sismember 
    scard
    # 取出
    spop 
    

② 增删

  • sadd:将一个或多个值添加到集合 Key 中,已存在的值被忽略。

  • srem:删除集合中指定元素。

  • smove:将元素移动到另一个集合。

    sadd 
    srem 
    smove   value
    

③ 集合论

  • sinter:交集。

  • sunion:并集。

  • sdiff:差集。

    sinter  
    sunion  
    # 包含k1中的,不包含k2中的
    sdiff  
    

1.4.3、应用

去重

1.5、Zset(有序集合)

SortedSet(单键多值、排序

无序、无下标、不可重复

  • 相比 Set,Zset 的成员都关联评分(score),用于升序排序
  • 成员唯一,评分可重复。
  • 可通过 score 的范围来获取元素。
  • 访问 Zset 的中间元素效率比 Set 高。

1.5.1、数据结构

类似 Java 的 TreeSet,排序规则为 score

两个数据结构:hash、跳跃表

  • hash:保证 field 唯一性,关联 field 和 score。

    image-20220414125343293

  • 跳跃表:给元素 value 排序,根据 score 范围获取元素。

    image-20220414130033985

1.5.2、常用命令

① 查询

  • zrange:查询指定次序范围的元素。

    • WITHSCORES:附带 score 值。
  • zrangebyscore:查询指定 score 范围内的元素(左右包含),按 score 升序排序。

  • zrevrangebyscore:同上,但降序排序(reverse)。

  • zcount:查询指定 score 范围内的元素个数。

  • zrank:查询指定值在集合中的排名(从 0 开始)。

    zrange  [WITHSCORES]
    zrangebyscore key minmax [withscores] [limit offset count]
    zrevrangebyscore key maxmin [withscores] [limit offset count]
    
    zcount 
    zrank 
    

② 增删

  • zadd:添加一个或多个元素及 score。

  • zrem:删除指定元素。

    zadd 
    zrem 
    

③ 递增

zincrby:指定元素的 score 递增指定步长。

zincrby 

1.5.3、应用

排行榜

3、新数据类型

3.1、Bitmaps(位图)

充分使用字节的 8 位二进制数。

  • 计算机中用 8 位二进制数编码表示数值信息(1 byte = 8 bit)
  • Bitmaps 本质是字符串,但支持位操作。
  • 可理解成以 bit 为单位的数组,数组下标称为偏移量
  • 充分利用操作位,可有效提高内存使用率和开发效率

3.1.1、常用命令

  • setbit:设置指定偏移量的值(偏移量,offset 从 0 开始)。

  • getbit:获取指定偏移量的值。

  • bitcount:统计值为 1 的位数目,可指定字节范围。

  • bitop:复合操作(与或非、异或)

    setbit   
    getbit  
    bitcount  [start end]
    bitop and/or/not/xor  [key]
    

案例:某一天用户是否上线

  1. 设置:用户 ID作为偏移量,用户上线则设为 1。
    • 通常,bitmaps 的 Key 为某一天的访问情况。
    • 用户 ID 可能未必从 0 开始,需要经过一定处理。
  2. 获取:根据用户 ID(偏移量)查询,1 表示访问过。
  3. 数量:统计上线用户数。
  4. 复合:如 and,查看两天都有上线的用户。

3.1.2、应用

签到、用户在线状态、用户访问情况

3.2、HyperLogLog(基数统计)

3.2.1、基数问题

基数:集合中不重复元素的个数。

  • 示例{1, 3, 3, 5, 7} 的基数集为 {1, 3, 5},基数为 3。
  • 应用场景

传统方案

特点:精度高,但随着数据的增加,空间占用过多。

  1. MySQL:查询 DISTINCT COUNT。
  2. Redis:hash、set、bitmaps 等数据结构。

HyperLogLog(HLL):基数统计算法

降低一定的精度来平衡存储空间

  • 机制
    • 根据输入元素来计算基数,而不会存储元素。
    • 每个 HLL 的 Key 占用 12KB 内存,可计算接近 2^64 个不同元素的基数。
  • 特点
    • 即使输入元素的数量或容量很大,计算基数所占空间固定且很小。
    • 只计算基数,不存储元素,无法返回输入的元素。

3.2.2、常用命令

HyperLogLog,以下简称 HLL

  • pfadd:添加一个或多个元素。

  • pfcount:计算一个或多个 HLL 的基数。

  • pfmerge:将一个或多个 HLL 合并到另一个 HLL。

    pfadd 
    pfcount  
    pfmerge 
    

案例:记录网站 UV,多次访问只有一个 UV 记录

  • 设置:用户 ID 作为 HLL 的元素。
  • 统计
  • 合并:如合并 7 天的 UV 得到一周的 UV。

3.2.3、应用

UV(UniqueVisitor,独立访客)、独立 IP 数。

3.3、Geospatial(地理)

  • 二维坐标表示地图的经纬度。
  • 提供经纬度设置、查询等操作。

命令

  • geoadd:添加一个或多个地理位置。

  • geopos:获取指一个或多个 Key 的坐标值。

  • geodist:计算两个位置的直线距离,可设置单位。

  • georadius:获取指定 Key 的半径之内的元素,可设置单位。

    geoadd    
    geopos  
    geodist    [m|km|ft|mi ]
    georadius    radius m|km|ft|mi
    

4、发布/订阅

类似

消息通信模式:发送者(pub)在某个频道中发布消息,订阅者( sub )接收消息。

  • 客户端可以订阅任意数量的频道。

  • pub 在频道中发布消息后,sub 自动接收到订阅的消息。

    # sub订阅频道
    subscribe 频道名
    # pub发布信息
    publish channel 频道名