数据结构
Redis无论什么数据类型,存储的时候都是以键值对key-value形势存储,并且所有的key都是String类型,本文讨论的数据类型是value的数据类型。
Set集合
概述:Set集合是一种无序不重复的数据结构,查询效率高,具备去重功能,支持集合的交集、并集、差集运算,其应用场景丰富。
Set常用命令:
命令 | 描述 |
---|---|
sadd key member1 memeber2 … | 将一个或者多个成员添加到键为key的集合中 |
scard key | 统计键为key的集合中元素的数量 |
sismember key member | 判断member 是否在键为key的集合中,存在返回1,否则返回0 |
smembers key | 查询键为key的集合中的所有元素 |
spop key | 随机移出键为key的集合中的一个元素 |
smove src des member | 将元素member从src集合中移动到des集合中 |
srem key member1 member2 … | 移出键为key的集合中的多个元素 |
sinter key1 key2 … | 求多个集合的交集 |
sinterstore des key1 key2 … | 将多个集合的交集保存到新的集合des中 |
sunion key1 key2 … | 求多个集合的并集 |
sunionstore des key1 key2 … | 将多个集合的并集保存到新的集合des中 |
sdiff key1 key2 … | 求第一个集合与后面多个集合的差集 |
sdiffstore des key1 key2 … | 将第一个集合与后面多个集合的差集保存到新的集合des中 |
注意观察上面所有的命令都是以 S 开头,表示操作的是Set集合。
Set类型底层数据结构:
- intSet:是一种紧凑型的数组结构存,储纯数字的情况下才会用到,且元素数量不能超过512个。
- hashTable:hash表结构,和Redis hash类型的hashTable一样,不满足intSet的条件会使用hashTable。
- listpack:Redis 7.2版本之后加入了listpack数据结构。
intSet结构定义如下:
typedef struct intset {
//编码方式
uint32_t encoding;
//集合包含的元素数量
uint32_t length;
//保存元素的数组
int8_t contents[];
} intse
- encoding:编码方式,共有 int16_t、int32_t、int64_t 三种类型。
- length:集合中元素的数量。
- contents[]:保存元素的数组。
inSet、listpack和hashtable的相互转换(针对存储字符串的情况讨论)?
- 当前编码为set,没有超过阀值,使用listpack超过阀值则使用hashTable。
- 当前编码为listpack,没有超过阀值就使用listpack,超过阀值就使用hashTable。
- 当前编码是hashTable,直接插入,不进行编码转换。
intSet、listpack的最大元素数量可以在redis.conf配置:
set-max-intset-entries 512
#从listpack转换为hashTable需要满足这两个条件
set-max-listpack-entries 128
set_max_listpack_value 64
为什么加入了listpack?
因为hashTable的空间开销高,哈希碰撞概率高,listpack结构可以提高内存使用率和操作效率。
什么是intSet的升级操作?
如果我们的intSet使用的是int16_t类型的编码,此时新增一个元素类型int32_t,此时整数集合就要升级,先把contents [] 扩展为int32_t,然后再把新元素加入到intSet中,升级过程中是有序的,且是在原来的数组上操作。
intSet的升级操作有什么好处?
可以节约内存资源,当我们不知道元素是多少位的时候,如果直接使用int64_t 的编码,那肯定是最简单的,但是如果保存的数据中没有int64_t的元素,那就会造成资源浪费,因此有升级操作后,是可以节约内存资源的,升级操作不可逆的,是不支持降级操作的。
set的应用场景:
- 去重操作,利用Set的唯一性,统计网站的UV(独立访客),IP(独立IP)。
- 好友关系,利用Set的交集、并集、差集特性。
- 打标签,可以把有相同爱好的用户利用一个标签归类到一起。
- 点赞,点踩场景。
如有不正确的地方请各位指出纠正。