# 数据类型
- 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% 。
- 原理:
- 准备一个 N bits 长度的数组,每位初始为 0 。
- 插入一个元素时,计算元素的哈希值,然后将数组中第 HashValue / N 个 bit 位设置为 1 。
- 查询一个元素是否存在时,计算元素的哈希值,然后检查数组中对应的 bit 是否为 1 。
- 如果为 1 ,则该元素可能存在。但也可能不存在,该 bit 是被其它元素设置为 1 的,此时称为误报。
- 如果不为 1 ,则该元素一定不存在。
- 为了减少不同元素设置相同 bit 位的冲突风险,每个元素可用多种哈希算法,计算多种哈希值。
- 假设元素 "hello" 计算出 3 种哈希值,分别对应数组中第 12、45、79 位。如果这 3 个 bit 都为 1 ,则说明该元素可能存在,否则一定不存在。
- 插入布隆过滤器的元素越少、使用的哈希算法越多,误报率越低。
- Redis 使用 Bloom 数据结构时,需要安装 redisbloom.so 模块,步骤如下:
- 执行
wget https://github.com/RedisBloom/RedisBloom/archive/refs/tags/ver2.2.15.tar.gz
下载源代码,解压后执行make
编译,得到./redisbloom.so
文件。 - 在 redis.conf 中增加配置:
loadmodule /path/to/redisbloom.so
- 重启 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 倍。
- Bloom 默认设置为 error_rate=0.01、capacity=100 。