# 数据类型

  • key 只能是 string 类型,value 可以是多种类型。
    • 一般提到 "key 的数据类型" 时,是指 "key 的 value 的数据类型" 。
  • 每个数据库最多可以存储 2^32 个 key ,每个 list、set、zset、hash 最多可以包含 2^32 个元素。

# string

:字符串。

  • string 是基于二进制存储的,可以存储任何类型的值,比如图片、序列化的对象。

  • 一个 string 的存储容量为 512MB 。

    • 虽然 key 也是 string 类型的值,但是 key 越短,使用时越快。
  • 常用作缓存、保存 session、分布式锁、计数器。

  • 命令:

    set     <key> <value>     # 给 key 设置 string 类型的值。时间复杂度为 O(1) ,因为只能处理 1 个 key
    setnx   <key> <value>     # 当该 key 不存在时才执行 set
    mset    <key value>...    # 给多个 key 设置 string 类型的值。处理 n 个 key 时,时间复杂度为 O(n)
    msetnx  <key value>...    # 当指定的所有 key 都不存在时才执行 mset
    
    get     <key>             # 返回 key 的 string 类型值
    wget    <key>...          # 返回多个 key 的 string 类型的值
    strlen  <key>             # 返回 key 的 string 类型值的长度
    append  <key> <value>     # 给 key 附加一个 string 类型值,并返回其 strlen
    
  • 如果 string 类型的值由数字组成(可以为负数),则可以进行以下运算:

    incr    <key>             # 将 key 的值加一
    incrby  <key> <increment> # 将 key 的值加上指定量,并返回 key 的当前值
    
    decr    <key>             # 将 key 的值减一
    decrby  <key> <decrement> # 将 key 的值减去指定量
    
  • 例:

    127.0.0.1:6379> set key1 1
    OK
    127.0.0.1:6379> incrby key1 2
    (integer) 3
    127.0.0.1:6379> get key1
    "3"
    
  • 创建一个 key 之后,它的数据类型就固定了,不能再使用其它数据类型的命令进行操作,否则会报错。如下:

    127.0.0.1:6379> set key1 hello
    OK
    127.0.0.1:6379> lpush key1 a
    (error) WRONGTYPE Operation against a key holding the wrong kind of value
    127.0.0.1:6379> lrange key1 0 10
    (error) WRONGTYPE Operation against a key holding the wrong kind of value
    

# list

:列表,可以包含多个 string 类型的元素。

  • list 属于队列结构,增、删速度快。

  • 列表中的元素从右到左倒序排列,后插入的元素位于列表左端。

  • 命令:

    lpush <key> <value>...  # 在列表左端插入元素
    rpush <key> <value>...  # 在列表右端插入元素
    
    lpop  <key>             # 取出列表左端的一个元素
    rpop  <key>             # 取出列表右端的一个元素
    blpop <key> <timeout>   # 进行 lpop ,并设置过期时间(单位为秒)
    brpop <key> <timeout>   # 进行 rpop ,并设置过期时间(单位为秒)
    
    lpoplpush   <source> <destination>    # 取出源列表左端的一个元素,插入目的列表的左端
    rpoplpush   <source> <destination>
    blpoplpush  <source> <destination> <timeout>
    brpoplpush  <source> <destination> <timeout>
    
    llen    <key>                   # 返回列表的长度
    lindex  <key> <index>           # 返回指定位置的元素(index 从 0 开始编号,必须小于列表长度)
    lset    <key> <index> <value>   # 设置指定位置的元素的值
    lrange  <key> <start> <stop>    # 返回列表的切片 [start, stop) ,stop 的值可以大于列表长度。执行 lrange <key> 0 -1 则返回全部元素
    ltrim   <key> <start> <stop>    # 修剪列表,只保存 [start, stop) 范围内的元素,其余的元素都删除
    
  • 例:

    127.0.0.1:6379> lpush list1 a
    (integer) 1                     # 每次插入后会返回列表当前的长度
    127.0.0.1:6379> lpush list1 b c
    (integer) 3
    127.0.0.1:6379> lrange list1 0 3
    1) "c"
    2) "b"
    3) "a"
    127.0.0.1:6379> blpop list1 1
    1) "list1"
    2) "c"
    

# set

:集合,可以包含多个 string 类型的元素,会自动去掉重复元素。

  • 命令:

    sadd      <key> <member>...   # 往集合中插入元素
    spop      <key> [count]       # 随机取出集合中的 count 个元素
    sismember <key> <member>      # 判断集合中是否包含某个元素
    scard     <key>               # 返回集合中的元素数量
    smembers  <key>               # 返回集合中的所有元素
    
    sinter    <key>...            # 返回多个集合的交集
    sdiff     <key>...            # 返回差集
    sunion    <key>...            # 返回并集
    sinterstore <destination> <key>...  # 执行 sinter ,并将返回值储存在 destination 集合中
    sdiffstore  <destination> <key>...
    sunionstore <destination> <key>...
    
  • 例:

    127.0.0.1:6379> sadd set1 a
    (integer) 1                     # 每次插入后会返回集合当前的长度
    127.0.0.1:6379> sadd set1 b c
    (integer) 2
    127.0.0.1:6379> sadd set1 a
    (integer) 0                     # 如果该元素已存在,则不执行插入,而是返回 0
    127.0.0.1:6379> smembers set1
    1) "c"
    2) "b"
    3) "a"
    

# zset

:有序集合。

  • zset 中的每个元素为 socre member 的格式,按 socre 的值进行排序,按 member 的值进行去重。

    • socre 是 double 类型的值。
    • member 是 string 类型的值。
  • 命令:

    zadd    <key> <score member>...           # 往有序集合中插入元素,如果该 member 已存在则覆盖它的 score
    zcard   <key>                             # 返回有序集合中的元素数量
    zcount  <key> <min> <max>                 # 返回有序集合中,score 在[min, max)范围内的元素数量
    zrange  <key> <start> <stop> [WITHSCORES] # 返回有序集合中,score 在[start, sto)范围内的所有元素
    
  • 例:

    127.0.0.1:6379> zadd zset1 1 a
    (integer) 1
    127.0.0.1:6379> zadd zset1 10 b 100 c
    (integer) 2
    127.0.0.1:6379> zrange zset1 1 100
    1) "b"
    2) "c"
    

# hash

:哈希表,类似于 Python 的字典。

  • 哈希表以键值对的形式存储数据,格式为 field value

  • 哈希表基于哈希映射的关系进行存储,因此增、删、查的时间复杂度是 O(1)。

  • 命令:

    hset    <key> <field value>    # 设置一个名为 key 的哈希表中一个 field 的值
    hsetnx  <key> <field value>    # 当该 field 不存在时才设置它的值
    hmset   <key> <field value>... # 设置多个 field 的值
    
    hget    <key> <field>          # 返回一个 field 的值
    hmget   <key> <field>...       # 返回多个 field 的值
    
    hexists <key> <field>          # 查看某个 field 是否存在
    hdel    <key> <field>...       # 删除哈希表中的 field
    hlen    <key>                  # 返回哈希表中 field 的数量
    hkeys   <key>                  # 返回所有 field
    hvals   <key>                  # 返回所有 value
    hgetall <key>                  # 返回所有 field 和 value
    
  • 例:

    127.0.0.1:6379> hmset hash1 field1 hello field2 world
    OK
    127.0.0.1:6379> hget hash1 field1
    "hello"
    

# HyperLogLog

:用于对基数进行计数,存在一定误差。

  • 基数:一个集合去重后剩下的元素。

# Bloom

  • 布隆过滤器(Bloom filter):一种概率型数据结构,用于快速判断一个元素是否大概率存在于一个集合中。
    • 判断一个大型集合中是否存在某个元素时,使用 Bloom 比 set、hash 数据结构更快,存储体积更小,只是正确率没有 100% 。
  • 原理:
    1. 准备一个 N bits 长度的数组,每位初始为 0 。
    2. 插入一个元素时,计算元素的哈希值,然后将数组中第 HashValue / N 个 bit 位设置为 1 。
    3. 查询一个元素是否存在时,计算元素的哈希值,然后检查数组中对应的 bit 是否为 1 。
      • 如果为 1 ,则该元素可能存在。但也可能不存在,该 bit 是被其它元素设置为 1 的,此时称为误报。
      • 如果不为 1 ,则该元素一定不存在。
      • 为了减少不同元素设置相同 bit 位的冲突风险,每个元素可用多种哈希算法,计算多种哈希值。
      • 假设元素 "hello" 计算出 3 种哈希值,分别对应数组中第 12、45、79 位。如果这 3 个 bit 都为 1 ,则说明该元素可能存在,否则一定不存在。
      • 插入布隆过滤器的元素越少、使用的哈希算法越多,误报率越低。
  • Redis 使用 Bloom 数据结构时,需要安装 redisbloom.so 模块,步骤如下:
    1. 执行 wget https://github.com/RedisBloom/RedisBloom/archive/refs/tags/ver2.2.15.tar.gz 下载源代码,解压后执行 make 编译,得到 ./redisbloom.so 文件。
    2. 在 redis.conf 中增加配置:
      loadmodule /path/to/redisbloom.so
      
    3. 重启 Redis 。
  • 用法示例:
    127.0.0.1:6379> bf.add B1 hello     # 向布隆过滤器 B1 插入一个元素 hello 。如果该 Bloom 不存在,则自动创建
    (integer) 1
    127.0.0.1:6379> bf.add B1 hello     # 重复插入一个元素时,没有修改 bit 位,因此插入失败
    (integer) 0
    127.0.0.1:6379> bf.exists B1 hello  # 判断元素是否存在
    (integer) 1
    127.0.0.1:6379> bf.exists B1 Hello  # 区分大小写
    (integer) 0
    127.0.0.1:6379> bf.reserve B1 0.01 100    # 创建一个 Bloom ,并设置期望的误报率(error_rate)、容量(capacity)。如果该 Bloom 已存在,则报错
    (error) ERR item exists
    127.0.0.1:6379> bf.info B1          # 查询 Bloom 的信息
    1) Capacity                         # 期望容量
    2) (integer) 100
    3) Size                             # 存储体积,单位 bytes
    4) (integer) 296
    5) Number of filters
    6) (integer) 1
    7) Number of items inserted         # 插入的元素数量
    8) (integer) 1
    9) Expansion rate                   # 每次扩容时的倍数,默认为 2
    10) (integer) 2
    127.0.0.1:6379> del B1              # 删除 Bloom
    (integer) 1
    
    • Bloom 默认设置为 error_rate=0.01、capacity=100 。
      • 设置 error_rate=0.01、capacity=1000000 时,存储体积为 1.31 MB 。
    • 当插入 Bloom 的元素数量超过期望容量时,为了避免误报率超过期望,会自动扩容 Bloom ,将 capacity 改为当前 capacity 的大概 Expansion rate 倍。