redis数据类型(5种)和底层实现

Redis的特点

要用好Redis,首先要明白它的特点:

  • 读写速度快。redis官网测试读写能到10万左右每秒。速度快的原因这里简单说一下,第一是因为数据存储在内存中,我们知道机器访问内存的速度是远远大于访问磁盘的,其次是Redis采用单线程的架构,避免了上下文的切换和多线程带来的竞争,也就不存在加锁释放锁的操作,减少了CPU的消耗,第三点是采用了非阻塞IO多路复用机制
  • 数据结构丰富。 Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构。这也是这篇文章要讲的。
  • 支持持久化。Redis提供了RDB和AOF两种持久化策略,能最大限度地保证Redis服务器宕机重启后数据不会丢失。
  • 支持高可用。可以使用主从复制,并且提供哨兵机制,保证服务器的高可用。
  • 客户端语言多。因为Redis受到社区和各大公司的广泛认可,所以客户端语言涵盖了所有的主流编程语言,比如Java,C,C++,PHP,NodeJS等等。

Redis的数据结构

下面我们就学习Redis的数据结构,也是使用Redis要知道的最基础的知识。

Redis是一个Key-Value型的内存数据库,它所有的key都是字符串,而value常见的数据类型有五种:string,list,set,zset,hash。

Redis的这些数据结构,在底层都是使用redisObject来进行表示。redisObject中有三个重要的属性,分别是type、 encoding 和 ptr

type表示保存的value的类型。通常有以下几种,也就是常见的五种数据结构:

  • 字符串 REDIS_STRING
  • 列表 REDIS_LIST
  • 集合 REDIS_SET
  • 有序集合 REDIS_ZSET
  • 字典 REDIS_HASH

string(字符串)

这是redis中最常用的数据类型,字符串对象的 encoding 有三种,分别是:int、raw、embstr

常用的命令有常用命令: set、get、decr、incr、mget 等。

我们知道Redis是用C语言开发的,但是底层存储不是使用C语言的字符串类型,而是自己开发了一种数据类型SDS进行存储,SDS即Simple Dynamic String ,是一种动态字符串。

hash(字典)

哈希对象的编码有两种,分别是:ziplist、hashtable

当哈希对象保存的键值对数量小于 512,并且所有键值对的长度都小于 64 字节时,使用ziplist(压缩列表)存储;否则使用 hashtable 存储。

Redis中的hashtable跟Java中的HashMap类似,都是通过"数组+链表"的实现方式解决部分的哈希冲突。

list(链表)

列表对象的编码有两种,分别是:ziplist、linkedlist。当列表的长度小于 512,并且所有元素的长度都小于 64 字节时,使用ziplist存储;否则使用 linkedlist 存储。

Redis中的linkedlist类似于Java中的LinkedList,是一个链表,底层的实现原理也和LinkedList类似。这意味着list的插入和删除操作效率会比较快,时间复杂度是O(1)。

set(集合)

set类型的特点很简单,无序,不重复,跟Java的HashSet类似。它的编码有两种,分别是intset和hashtable。如果value可以转成整数值,并且长度不超过512的话就使用intset存储,否则采用hashtable。

hashtable在前面讲hash类型时已经讲过,这里的set集合采用的hashtable几乎一样,只是哈希表的value都是NULL。这个不难理解,比如用Java中的HashMap实现一个HashSet,我们只用HashMap的key就是了。

zset(有序集合)

zset是Redis中比较有特色的数据类型,它和set一样是不可重复的,区别在于多了score值,用来代表排序的权重。也就是当你需要一个有序的,不可重复的集合列表时,就可以考虑使用这种数据类型。

zset的编码有两种,分别是:ziplist、skiplist。当zset的长度小于 128,并且所有元素的长度都小于 64 字节时,使用ziplist存储;否则使用 skiplist 存储。

这里要讲一下skiplist,也就是跳跃表。它的底层实现比较复杂,这里简单地提一下。

跳表是在双向链表之上加多层索引构成的,相对于双向链表,支持快速查找,更新,删除,所以适用于需求灵活的场景。

查找某一个数据时,先在索引里面查找出一个大的范围,然后再下降到原始链表中精确查找。

因为加一层索引后,查找一个结点需要遍历的次数减少了,所以查找效率大大提升。【空间换时间】

针对链表长度比较大的时候,构建索引查找效率的提升就会非常明显。

在这里插入图片描述

跳表的特点

(1) 跳表结合了链表和类似二分查找的思想;

(2) 有很多层结构,由原始链表和一些通过“跳跃”生成的链表组成;

(3) 每一层都是一个有序的链表;

(4) 最底层(Level 1)的链表包含所有元素,越上层“跳跃”的越高,元素(索引)越少;

(5) 查找时从顶层向下,不断缩小搜索范围;

(6) 上层链表是下层链表的子序列;

(7) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

跳表的搜索

在这里插入图片描述

现在访问节点7 经过的节点为1->6->7

链表是有序的,但不能使用二分查找("类似"二分法)。类似二叉搜索树,我们把一些结点提取出来,作为索引。在实际开发中,原始链表中存储的可能是很大的对象,而索引结点只需要存储关键值和几个指针,其额外占用的空间可以被忽略掉。

类似"二分法)。类似二叉搜索树,我们把一些结点提取出来,作为索引。在实际开发中,原始链表中存储的可能是很大的对象,而索引结点只需要存储关键值和几个指针,其额外占用的空间可以被忽略掉。

注意:每隔两个节点往上提升一层建立索引只是理想情况,实际上是通过随机层数K(丢硬币决定 K,随机算法)来实现。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐