Redis数据类型及编码
说到Redis的数据类型,我们大概会很快想到Redis的5种常见的数据类型:字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted Set),以及他们的特点和运用场景及常用命令。不过在讲五大数据类型之前,我们有必要看一下redis中的全局操作命令,下面列出了一些常用的操作及其时间复杂度和使用场景。
全局命令中大多都是针对key的属性设置和修改,除了全局操作以外,redis的五个常用数据类型也有自己的单独对的操作命令,我们下面就来一个个看一下,顺便把这些数据类型的内部原理也大概了解一下。
要讲内部原理,首先我们得看看redis中有多少种编码方式:
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
* is set to one of this fields for this object. */
#define OBJ_ENCODING_RAW 0 /* Raw representation */
#define OBJ_ENCODING_INT 1 /* Encoded as integer */
#define OBJ_ENCODING_HT 2 /* Encoded as hashtable */
#define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ // 已废弃
#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6 /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
不懂这些编码方式不要紧,后面都会讲到。下面我们就从五个数据类型出发,依次讲解。
(一)String 字符串
string是redis最基本的数据类型,一个key对应一个value。string类型是二进制安全的,意思是redis的string可以包含任何数据,比如图片或者序列化的对象 等等。string类型是redis最基本的数据类型,一个键最大能存储512MB的数据。String 数据结构是简单的 key-value 类型。
string类型一共有三种编码方式,分别是int,raw, embstr。
1.int编码
字符串对象保存的值是一个整数值,并且这个整数值在 long 的范围内,那么 redis 用整数值来保存这个信息,字符串编码设置为int。
2.raw编码
字符串对象保存的是一个字符串,长度大于 32 个字节,就会使用SDS(简单动态字符串)数据结构来保存这个字符串值,并且将字符串对象的编码设置为raw。
3.embstr编码
字符串对象保存的是一个字符串,长度小于 32 个字节,会使用embstr来保存,embstr编码是对SDS的一个优化,采用连续的空间保存,即将SDS的值和字符串对象的值放在一块连续的内存空间上。主要用于为使用较小的字符串对象时提高效率。另外,embstr编码是只读的,只要发生修改操作,就会将编码转换成raw再进行操作。
PS:若字符串保存的为浮点数,那么浮点数将先被转化为字符串,再根据上面的三种情况来选择编码方式。浮点数在进行操作时时,需要从字符串转换成浮点数进行计算,然后再转换成字符串进行保存。
(二)Hash 哈希
hash类型很像一个关系型数据库的数据表,hash的Key是一个唯一值,Value部分是一个 hashmap的结构。hash数据类型在存储对象时具有比 string 类型更灵活、更快的优势,具体的说,使用 string 类型存储,必然需要转换和解析 json 格式的字符串,即便不需要转换,在内存开销方面,还是 hash 更占优势。hash的模型基本上是这样:
哈希对象的编码有ziplist和hashtable。
1.ziplist编码
键值对的键和值的长度都小于 64 字节,且键值对个数小于 512时,使用ziplist编码,底层数据结构使用ziplist,用两个连续的ziplist节点来表示哈希对象中的一个键值对。
2.hashtable编码
键值对的键或值的长度大于 64 字节,或键值对个数大于512时使用hashtable编码,底层数据结构使用hashtable。哈希在结构上和hashtable非常相似,因此哈希对象中的每一个键值对都是hashtable中的一个键值对。
(三)List 列表
redis中的列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),就像一个双向队列。
list类型经常会被用于消息队列的服务,以完成多程序之间的消息交换。在较老版本中(好像是3.2以下),list一共使用了两种数据结构:压缩链表和双向链表。当元素数量较少的时候,使用压缩列表,当元素数量增多,就使用双向链表。
但是在3.2之后,一个新的数据结构——快速列表(quicklist)出现了,这个数据结构现在已经成为了所有列表的底层实现。
(四)Set 集合
set 就是一个集合,集合的概念就是一堆不重复值的组合。利用redis提供的set数据类型,可以存储一些集合性的数据。redis的set是string类型的无序集合。集合最大的优势在于可以进行交集并集差集操作。Set可包含的最大元素数量是4294967295。
集合对象的编码有intset和hashtable。
1.intset编码
集合中的所有元素都是整数,且数量不大于 512 个的时候,使用 intset 编码。intset 编码底层使用intset数据结构。
2.hashtable编码
元素不符合全部为整数值且元素个数小于 512时,集合对象使用的编码方式为 hashtable。hashtable的每一个键都是一个字符串对象,其中保存了集合里的一个元素,hashtable的值全部被设置为NULL。
(五)Sorted Set 有序集合
和set相比,sorted set是将set中的元素增加了一个权重参数score,使得集合中的元素能够按 score进行有序排列。sorted set 和set一样也是string类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。set的成员是唯一的,但分数(score)却可以重复。sorted set是插入有序的,即自动排序。
有序集合对象的编码有ziplist和skiplist。
1.ziplist编码
当元素数量少于128且所有元素成员的长度小于64字节时使用ziplist编码,有序集合对象的实现数据结构为ziplist, 每个集合的元素 (key-value), 使用两个紧挨着的ziplist的节点来表示,第一个节点保存集合元素的成员 (member), 第二个节点保存集合元素的分数(score)。在压缩列表的内部,集合元素按照分值从小到大进行排序。
2.skiplist编码
当元素数量大于128且所有元素成员的长度大于64字节时使用skiplist编码,内部使用zset来实现数据的保存。
最后,我们总结一下五种数据类型和所用的编码:
2020年4月28日