Redis 支持五种数据类型:
- string
- hash
- list
- set
- zset
# String
string 是最基本的类型。string 二进制安全,即可以包含任何数据。
string 一个键最大能存储 512MB。
string 类型的数据结构存储方式有三种 int
、 raw
、 embstr
:
# int
Redis 中规定假如存储的是整数型值,比如 set num 123
这样的类型,就会使用 int
的存储方式进行存储,在 redisObject
的 ptr属性
中就会保存该值。
# SDS
假如存储的字符串是一个字符串值并且长度大于 32 个字节就会使用 SDS(simple dynamic string)方式进行存储,并且 encoding
设置为 raw
;若是字符串长度小于等于 32 个字节就会将 encoding
改为 embstr
来保存字符串。
SDS 称为简单动态字符串,对于 SDS 中的定义在 Redis 的源码中有的三个属性 int len
、 int free
、 char buf[]
。
len
保存了字符串的长度, free
表示 buf数组
中未使用的字节数量, buf数组
则是保存字符串的每一个字符元素。
因此当你在 Redis 中存储一个字符串 "Hello_World"
时,SDS 的形式的 redisObject
结构图如下图所示:
# Hash
Redis hash 是一个键值 (key=>value) 对集合。
Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
每个 hash 可以存储 键值对。
Hash 对象的实现方式有两种分别是 ziplist
、 hashtable
,其中 hashtable
的存储方式 key
是 String 类型的, value
也是以 key value
的形式进行存储。
# ziplist
压缩列表(ziplist)是一组连续内存块组成的顺序的数据结构,压缩列表能够节省空间,压缩列表中使用多个节点来存储数据。
压缩列表是列表键和哈希键底层实现的原理之一,压缩列表并不是以某种压缩算法进行压缩存储数据,而是它表示一组连续的内存空间的使用,节省空间,压缩列表的内存结构图如下:
其中:
zlbytes
:4 个字节的大小,记录压缩列表占用内存的字节数。zltail
:4 个字节大小,记录表尾节点距离起始地址的偏移量,用于快速定位到尾节点的地址。zllen
:2 个字节的大小,记录压缩列表中的节点数。entry
:表示列表中的每一个节点。zlend
:表示压缩列表的特殊结束符号'0xFF'
每一个 entry
节点又有三部分组成,分别是 previous_entry_length
、 encoding
、 content
。
previous_entry_length
表示前一个节点entry
的长度,可用于计算前一个节点的其实地址,因为他们的地址是连续的。encoding
:这里保存的是content
的内容类型和长度。content
:content
保存的是每一个节点的内容。
# hashtable
hashtable 是一种字典类型的数据结构,用于存储键值对,其中键是字符串类型,值可以是字符串、列表、集合或有序集合。hashtable 的底层实现是一个数组,每个数组元素是一个链表,用于解决哈希冲突。当 hashtable 中的键值对数量变化时,会进行扩展或收缩操作,重新分配数组的大小和哈希值。hashtable 是哈希键和集合键的底层实现之一,当这些键的元素数量和长度较大时,Redis 会使用 hashtable 作为其存储方式。
# List
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部或者尾部。
列表最多可存储 元素。
Redis 中的列表在 3.2 之前的版本是使用 ziplist
和 linkedlist
进行实现的。在 3.2 之后的版本就是引入了 quicklist
。
# linkedlist
linkedlist 是一个双向链表,他和普通的链表一样都是由指向前后节点的指针。插入、修改、更新的时间复杂度是 O (1),查询的时间复杂度是 O (n)。其特性为
- 每一个节点都有指向前一个节点和后一个节点的指针。
- 头节点和尾节点的
prev
和next
指针指向为null
,所以链表是无环的。 - 链表有自己长度的信息,获取长度的时间复杂度为 O (1)。
# quicklist
quicklist 实际上是 zipList 和 linkedList 的混合体,它将 linkedList 按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。
prev
、next
指向该节点的前后节点。zl
指向该节点对应的 ziplist 结构。sz
代表整个 ziplist 结构的大小。encoding
代表采用的编码方式:1
代表是原生的,2
代表使用LZF
进行压缩。container
为 quickListNode 节点zl
指向的容器类型:1
代表none
,2
代表使用ziplist
存储数据。recompress
代表这个节点之前是否是压缩节点,若是,则在使用压缩节点前先进行解压缩,使用后需要重新压缩,此外为1
,代表是压缩节点。attempted_compress
测试时使用。extra
为预留。
# Set
Redis 的 Set 是 string 类型的无序集合。
集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O (1)。
集合中最大的成员数为 。
Redis 中列表和集合都可以用来存储字符串,但是 Set 是不可重复的集合,而 List 列表可以存储相同的字符串。
Set 的底层实现是 hashtable
和 intset
。
# intset
inset 也叫做整数集合,用于保存整数值的数据结构类型,它可以保存 int16_t
、 int32_t
或者 int64_t
的整数值。
在整数集合中,有三个属性值 encoding、length、contents[]
,分别表示编码方式、整数集合的长度、以及元素内容, length
就是记录 contents
里面的大小。
在整数集合新增元素的时候,若是超出了原集合的长度大小,就会对集合进行升级,具体的升级过程如下:
- 首先扩展底层数组的大小,并且数组的类型为新元素的类型。
- 然后将原来的数组中的元素转为新元素的类型,并放到扩展后数组对应的位置。
- 整数集合升级后就不会再降级,编码会一直保持升级后的状态。
# Zset
Redis zset 和 set 一样也是 string 类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
zset 的成员是唯一的,但分数(score)却可以重复。
zset 的底层实现是 ziplist
和 skiplist
。
# skipset
skiplist 也叫做跳跃表,跳跃表是一种有序的数据结构,它通过每一个节点维持多个指向其它节点的指针,从而达到快速访问的目的。
skiplist 由如下几个特点:
- 有很多层组成,由上到下节点数逐渐密集,最上层的节点最稀疏,跨度也最大。
- 每一层都是一个有序链表,只扫包含两个节点,头节点和尾节点。
- 每一层的每一个每一个节点都含有指向同一层下一个节点和下一层同一个位置节点的指针。
- 如果一个节点在某一层出现,那么该以下的所有链表同一个位置都会出现该节点。