面试官:有一种数据类型,Redis 要存两次,为什么?

阅读本文大概需要 9 分钟 。
来自:////
前言
在 Redis 中,有一种数据类型,当在存储的时候会同时采用两种数据结构来进行分别存储,那么 Redis 为什么要这么做呢?这么做会造成同一份数据占用两倍空间吗?
五种基本类型之集合对象
Redis 中的集合对象是一个包含字符串类型元素的无序集合,集合中元素唯一不可重复 。
集合对象的底层数据结构有两种: 和。内部通过编码来进行区分:
编码
(整数集合)可以保存类型为,,的整数值,并且保证集合中没有重复元素 。
数据结构定义如下(源码inset.h内):
typedef struct intset {uint32_t encoding;//编码方式uint32_t length;//当前集合中的元素数量int8_t contents[];//集合中具体的元素} intset;
下图就是一个的集合对象存储简图:
在内部的记录了当前整数集合的数据存储类型,主要有三种:
此时[]内的每个元素都是一个类型的整数值,范围是:-32768 ~ 32767(-2 的 15 次方 ~ 2 的 15 次方 - 1) 。
此时[]内的每个元素都是一个类型的整数值,范围是:- ~ (-2 的 31 次方 ~ 2 的 31 次方 - 1) 。
此时[]内的每个元素都是一个类型的整数值,范围是:- ~ (-2 的 63 次方 ~ 2 的 63 次方 - 1) 。
[]
[]虽然结构的定义上写的是类型,但是实际存储类型是由上面的来决定的 。最新 Redis 面试题整理好了,点击Java面试库小程序在线刷题 。
整数集合的升级
假如一开始整数集合中的元素都是 16 位的,采用类型来存储,此时需要再存储一个 32 位的整数,那么就需要对原先的整数集合进行升级,升级之后才能将 32 位的整数存储到整数集合内 。这就涉及到了整数集合的类型升级,升级过程主要有 4 个步骤:
PS:和字符串对象的编码一样,整数集合的类型一旦发生升级,将会保持编码,无法降级 。
升级示例
1.假如我们有一个集合存储的是,内部存储了 3 个元素:
2.这时候需要插入一个整数 50000,发现存储不下去,而 50000 是一个类型整数,所以需要申请新空间,申请空间大小为4 * 32 - 48=80 。
3.现在新的数组内要放置 4 个元素,原来的数组排在第 3,所以需要将升级后的 3 移动到 64-95 位 。
4.继续将升级后的 2 移动到 32-63 位 。
5.继续将升级后的 1 移动到 0-31 位 。
6.然后会将 50000 放到 96-127 位 。
7.最后会修改和属性,修改之后就完成了本次的升级 。
编码
结构在前面讲述哈希对象的时候进行过详细分析,想详细了解的可以点击这里 。
和编码转换
当一个集合满足以下两个条件时,Redis 会选择使用编码:
一旦集合中的元素不满足上面两个条件,则会选择使用编码 。
集合对象常用命令
了解了操作集合对象的常用命令,我们就可以来验证下前面提到的哈希对象的类型和编码了,在测试之前为了防止其他 key 值的干扰,我们先执行命令清空 Redis 数据库 。
依次执行如下命令:
sadd num 1 2 3//设置 3 个整数的集合,会使用 intset 编码type num //查看类型object encoding num//查看编码sadd name 1 2 3 test//设置 3 个整数和 1 个字符串的集合,会使用 hashtable 编码type name //查看类型object encoding name //查看编码
得到如下效果:
可以看到,当设置的元素里面只有整数时,集合使用的就是编码,当设置的元素中含有非整数时,使用的就是编码 。
五种基本类型之有序集合对象
Redis 中的有序集合和集合的区别是有序集合中的每个元素都会关联一个类型的分数,然后按照分数从小到大的顺序进行排列 。换句话说,有序集合的顺序是由我们自己设值的时候通过分数来确定的 。