Redis学习笔记(1)- 数据类型

一、Redis基础数据类型

  Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

1、String 字符串

  String类型是 Redis 最基本的数据类型,String 类型是二进制安全的,意思是 Redis的 String 可以包含任何数据,比如jpg图片或者序列化的对象。Redis 的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采⽤预分配冗余空间的⽅式来减少内存的频繁分配,当字符串⻓度⼩于 1M时,扩容都是加倍现有的空间,如果超过 1M,扩容时⼀次只会多扩1M 的空间,String类型的值最大能存储 512MB。

2、Hash 字典

  Hash 是一个键值(key=>value)对集合,是一个 String 类型的 field 和 value 的映射表,Hash 特别适合用于存储对象。
  Hash的结构为数组 +链表⼆维结构。第⼀维 hash 的数组位置碰撞时,就会将碰撞的元素使⽤链表串接起来。Redis 的字典的值只能是字符串。当字典很大的时候,会进行rehash,Redis 为了⾼性能,不能堵塞服务,采⽤了渐进式 rehash 策略。
  渐进式 rehash 保留新旧两个 hash 结构,查询时会同时查询两个 hash 结构,然后在后续的定时任务中以及hash 操作指令中,循序渐进地将旧 hash 的内容⼀点点迁移到新的hash 结构中。当搬迁完成了,就会使⽤新的hash结构取⽽代之。当 hash 移除了最后⼀个元素之后,该数据结构⾃动被删除,内存被回收。

3、Hash 字典

  List是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。List相当于 Java 语⾔⾥⾯的 LinkedList,数据结构形式为链表,插⼊和删除操作⾮常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为 O(n)。当列表弹出了最后⼀个元素之后,该数据结构⾃动被删除,内存被回收。插入元素后,各元素的相对位置确定,遍历的结果也与之保持一致。链表元素可以重复。

4、Set 集合

  Set 是 string 类型的无序且不重复的集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。Redis 的集合相当于 Java 语⾔⾥⾯的 HashSet,它内部的键值对是⽆序的唯⼀的。它的内部实现相当于⼀个特殊的字典,字典中所有的value 都是⼀个值NULL。当集合中最后⼀个元素移除之后,数据结构⾃动删除,内存被回收。

5、zset (sorted set:有序集合)

  zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。zset 类似于 Java 的 SortedSet 和HashMap的结合体,它的内部实现⽤的是⼀种叫做「跳跃列表」的数据结构。zset 中最后⼀个 value 被移除后,数据结构⾃动删除,内存被回收。

二、基础数据类型的内部编码

  数据类型是Redis数据结构的表现,内部编码是数据类型的内部实现。每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样Redis会在合适的场景选择合适的内部编码。每种数据结构都有两种以上的内部编码实现,例如string数据结构就包含了raw、int和embstr三种内部编码。同时,有些内部编码可以作为多种外部数据结构的内部实现,例如ziplist就是hash、list和zset共有的内部编码。
  Redis这样外部结构和内部编码分离的设计方式的优势:第一,可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发开发出优秀的内部编码,无需改动外部数据结构和命令。第二,多种内部编码实现可以在不同场景下发挥各自的优势。例如ziplist比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为linkedlist。
Redis学习笔记(1)- 数据类型_第1张图片

1、查看数据类型、内部编码

  查看数据类型的方式:type [key],如下图示:
Redis学习笔记(1)- 数据类型_第2张图片
  查看内部编码的方式:object encoding [key],如下图所示:
Redis学习笔记(1)- 数据类型_第3张图片

2、String 的内部编码

  字符串类型的内部编码有3种,Redis会根据当前值的类型和长度决定使用内部编码实现。

  • int:8个字节的长整型,即保存long型的64位有符号整数
  • embstr:小于等于44个字节的字符串。
  • raw:大于44个字节的字符串。

注:在3.2版本之前,据说是按照39个字节作为分界的(未实际验证)。
Redis学习笔记(1)- 数据类型_第4张图片

3、Hash的内部编码

  Hash类型的内部编码有两种:ziplist(压缩列表)和 (哈希表)。

  • ziplist(压缩列表):当Hash类型元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64个字节)时,Redis会使用ziplist作为哈希的内部实现ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。压缩列表的缺点:每次插入或删除一个元素时,都需要进行频繁的调用realloc()函数进行内存的扩展或减小,然后进行数据”搬移”,甚至可能引发连锁更新,造成严重效率的损失。
  • hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现。因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

Redis学习笔记(1)- 数据类型_第5张图片

上面例子只演示了value大于hash-max-ziplist-value配置的情况,如果 元素个数大于hash-max-ziplist-entries配置,也会使用hashtable的内部编码。

4、List的内部编码

  列表类型的内部编码有两种:

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)。同时所有值都小于hash-max-ziplist-value配置(默认64个字节)时,Redis会使用ziplist作为哈希的内部实现。和Hash类型一样,满足条件就是用ziplist,否则使用linkedlist。
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。
  • quicklist(快速列表):在Redis 3.2版本中新加的内部编码,是以一个zpilist为节点的linkedlist,结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。quicklist宏观上是一个双向链表,因此,它具有一个双向链表的优点,进行插入或删除操作时非常方便,虽然复杂度为O(n),但是不需要内存的复制,提高了效率,而且访问两端元素复杂度为O(1)。双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大。首先,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。在quicklist表头结构中,有两个成员是fill和compress,其中” : “是位域运算符,表示fill占int类型32位中的16位,compress也占16位。fill和compress的配置文件是redis.conf。

  1、fill成员对应的配置:list-max-ziplist-size -2

当数字为负数,表示以下含义:

  • -1 每个quicklistNode节点的ziplist字节大小不能超过4kb。(建议)
  • -2 每个quicklistNode节点的ziplist字节大小不能超过8kb。(默认配置)
  • -3 每个quicklistNode节点的ziplist字节大小不能超过16kb。(一般不建议)
  • -4 每个quicklistNode节点的ziplist字节大小不能超过32kb。(不建议)
  • -5 每个quicklistNode节点的ziplist字节大小不能超过64kb。(正常工作量不建议)
  • 当数字为正数,表示:ziplist结构所最多包含的entry个数。最大值为 215。

  compress成员对应的配置:list-compress-depth 0

后面的数字有以下含义:

  • 0 表示不压缩。(默认)
  • 1 表示quicklist列表的两端各有1个节点不压缩,中间的节点压缩。
  • 2 表示quicklist列表的两端各有2个节点不压缩,中间的节点压缩。
  • 3 表示quicklist列表的两端各有3个节点不压缩,中间的节点压缩。
  • 以此类推,最大为 216。
5、Set的内部编码

  集合类型的内部编码有两种:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会选用intset来作为集合内部实现,从而减少内存的使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。当某个元素不为整数时,内部编码也会变为hashtable。
    Redis学习笔记(1)- 数据类型_第6张图片
6、ZSet的内部编码

  有序集合类型的内部编码有两种:

  • ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个)。同时每个元素的值小于zset-max-ziplist-value配置(默认64个字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存使用。
  • skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时zip的读写效率会下降。
    Redis学习笔记(1)- 数据类型_第7张图片

参考:
1、《面试官看了赞不绝口的Redis笔记》、
2、《Redis的五种数据结构的内部编码》

你可能感兴趣的:(Redis,redis,数据结构,内部编码,list,string)