分布式专题(十二)Redis数据结构

目录

存储结构

数据类型

字符串类型(String)

列表类型(list)

哈希类型(hash)

集合类型(set)

有序集合(sorted-set)


缓存大致可以分为两类,一种是应用内缓存,比如Map(简单的数据结构),以及EH Cache(Java第三方库),另一种 就是缓存组件,比如Memached,Redis;Redis(remote dictionary server)是一个基于KEY-VALUE的高性能的 存储系统,通过提供多种键值数据类型来适应不同场景下的缓存与存储需求 

存储结构

大家一定对字典类型的数据结构非常熟悉,比如map ,通过key-value的方式存储的结构。 redis的全称是remote dictionary server(远程字典服务器),它以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容。数据结构如下

数据类型

字符串类型(String)

字符串类型是redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用它存储用户的 邮箱、json化的对象甚至是图片。一个字符类型键允许存储的最大容量是512M

内部数据结构

在Redis内部,String类型通过 intSDS(simple dynamic string)作为结构存储,int用来存放整型数据,sds存放字节/字符串和浮点型数据。在C的标准字符串结构下进行了封装,用来提升基本操作的性能,同时也充分利用已有的C的标准库,简化实现逻辑。我们可以在redis的源码中【sds.h】中看到sds的结构如下:

typedef char *sds;

redis3.2分支引入了五种sdshdr类型,目的是为了满足不同长度字符串可以使用不同大小的Header,从而节省内存,每次在创建一个sds时根据sds的实际长度判断应该选择什么类型的sdshdr,不同类型的sdshdr占用的内存空间不同。这样细分一下可以省去很多不必要的内存开销,下面是3.2sdshdr定义

// 8表示字符串最大长度是2^8-1 (长度为255)
struct __attribute__ ((__packed__)) sdshdr8 {
 
    //表示当前sds的长度(单位是字节)
    uint8_t len;

    //表示已为sds分配的内存大小(单位是字节)
    uint8_t alloc; 

    //用一个字节表示当前sdshdr的类型,因为有sdshdr有五种类型,所以至少需要3位来表示000:sdshdr5,001:sdshdr8,010:sdshdr16,011:sdshdr32,100:sdshdr64。高5位 用不到所以都为0
    unsigned char flags; 

    //sds实际存放的位置
    char buf[];

};

列表类型(list)

列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获得列表的某一个片段。

列表类型内部使用双向链表实现,所以向列表两端添加元素的时间复杂度为O(1), 获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是很快的

内部数据结构

redis3.2 之前, List 类型的 value 对象内部以 linkedlist 或者 ziplist 来实现 , list 的元素个数和单个元素的长度达到以下两个要求时,Redis 会采用 ziplist (压缩列表)来实现来减少内存占用。否则就会采用 linkedlist (双向链表)结构。
 
  • 列表对象保存的所有字符串元素的长度都小于64字节
  • 列表对象保存的元素数量小于512个
这两种存储方式都有优缺点,linkedlist在链表两端进行push pop 操作,在插入节点上复杂度比较低,但是内存开销比较大;ziplist 存储在一段连续的内存上,所以存储效率很高,但是插入和删除都需要频繁申请和释放内存;
 
redis3.2 之后,采用的一种叫 quicklist 的数据结构来存储 list ,列表的底层都由 quicklist 实现。
 
quicklist 仍然是一个双向链表,只是列表的每个节点都是一个 ziplist ,其实就是 linkedlist ziplist 的结合, quicklist中每个节点ziplist 都能够存储多个数据元素,在源码中的文件为【 quicklist.c 】,在源码第一行中有解释为: A doubly linked list of ziplists意思为一个由 ziplist 组成的双向链表,如下图所示:

哈希类型(hash)

内部数据结构

  • 一种是ziplist,上面已经提到过。当存储的数据超过配置的阀值时就是转用hashtable的结构。这种转换比较消耗性能,所以应该尽量避免这种转换操作。同时满足以下两个条件时才会使用这种结构:
    • 当键的个数小于hash-max-ziplist-entries(默认512)
    • 当所有值都小于hash-max-ziplist-value(默认64)
  • 另一种就是hashtable。这种结构的时间复杂度为O(1),但是会消耗比较多的内存空间

集合类型(set)

集合类型中,每个元素都是不同的,也就是不能有重复数据,同时集合类型中的数据是无序的。一个集合类型键可 以存储至多232-1个 。集合类型和列表类型的最大的区别是有序性和唯一性 集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在。由于集合类型在redis内部是使用的值 为空的散列表(hash table),所以这些操作的时间复杂度都是O(1).

内部数据结构

Set 在的底层数据结构以 intset 或者 hashtable 来存储。当 set 中只包含整数型的元素时,采用 intset 来存储,否则,采用hashtable 存储,但是对于 set 来说,该 hashtable value 值用于为 NULL 通过 key来存储元素
 

有序集合(sorted-set)

有序集合类型,顾名思义,和前面讲的集合类型的区别就是多了有序的功能
在集合类型的基础上,有序集合类型为集合中的每个元素都关联了一个分数,这使得我们不仅可以完成插入、删除 和判断元素是否存在等集合类型支持的操作,还能获得分数最高(或最低)的前N个元素、获得指定分数范围内的元 素等与分数有关的操作。虽然集合中每个元素都是不同的,但是他们的分数却可以相同

内部数据结构

  • 一种是ziplist结构

          与上面的hash中的ziplist类似,member和score顺序存放并按score的顺序排列

  • 另一种是skiplist与dict的结合

         skiplist是一种跳跃表结构,用于有序集合中快速查找,大多数情况下它的效率与平衡树差不多,但比平衡树实现简单。redis的作者对普通的跳跃表进行了修改,包括添加span\tail\backward指针、score的值可重复这些设计,从而实现排序功能和反向遍历的功能。

一般跳跃表的实现,主要包含以下几个部分:

  • 表头(head):指向头节点
  • 表尾(tail):指向尾节点
  • 节点(node):实际保存的元素节点,每个节点可以有多层,层数是在创建此节点的时候随机生成的一个数值,而且每一层都是一个指向后面某个节点的指针。
  • 层(level):目前表内节点的最大层数
  • 长度(length):节点的数量。

跳跃表的遍历总是从高层开始,然后随着元素值范围的缩小,慢慢降低到低层。

跳跃表的实现原理可以参考:https://blog.csdn.net/Acceptedxukai/article/details/17333673

前面也说了,有序列表是使用skiplist和dict结合实现的,skiplist用来保障有序性和访问查找性能,dict就用来存储元素信息,并且dict的访问时间复杂度为O(1)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值