Redis的基础数据结构

1、分布式缓存技术的应用

 

缓存 : 就是低速的IO与高速的应用之间的问题缓冲组件放到数据库和应用之间,

使用缓存的方式:

 1、把数据写入数据库之后,然后把相应的数据写入缓存

 2、先去查询数据库里的数据 然后把相应的数据写入缓存

 3、定时刷新,定时任务的调度可以周期性的把相应数据做一个同步

2、redis的存储结构

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

3、数据类型

字符串类型

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

内部数据结构

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

     typedef char *sds;

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

sdshdr8的内存布局

列表类型

     列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获得列表的某一个片段。列表类型内部使用双向链表实现,所以向列表两端添加元素的时间复杂度为O(1), 获取越接近两端的元素速度就越 快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是很快的

ziplist

     ziplist是由一系列特殊编码的连续内存块组成的顺序存储结构,类似于数组,ziplist在内存中是连续存储的,但是不同于数组,为了节省内存 ziplist的每个元素所占的内存大小可以不同(数组中叫元素,ziplist叫节点entry,下文都用“节点”),每个节点可以用来存储一个整数或者一个字符串。
下图是ziplist在内存中的布局

  • zlbytes: ziplist的长度(单位: 字节),是一个32位无符号整数
  • zltail: ziplist最后一个节点的偏移量,反向遍历ziplist或者pop尾部节点的时候有用。
  • zllen: ziplist的节点(entry)个数
  • entry: 节点
  • zlend: 值为0xFF,用于标记ziplist的结尾

内部数据结构

redis3.2之前,List类型的value对象内部以linkedlist或者ziplist来实现, 当list的元素个数和单个元素的长度比较小

的时候,Redis会采用ziplist(压缩列表)来实现来减少内存占用。否则就会采用linkedlist(双向链表)结构。 redis3.2之后,采用的一种叫quicklist的数据结构来存储list,列表的底层都由quicklist实现。

这两种存储方式都有优缺点,双向链表在链表两端进行push和pop操作,在插入节点上复杂度比较低,但是内存开 销比较大; ziplist存储在一段连续的内存上,所以存储效率很高,但是插入和删除都需要频繁申请和释放内存;

quicklist仍然是一个双向链表,只是列表的每个节点都是一个ziplist,其实就是linkedlist和ziplist的结合,quicklist 中每个节点ziplist都能够存储多个数据元素,在源码中的文件为【quicklist.c】,在源码第一行中有解释为:A doubly linked list of ziplists意思为一个由ziplist组成的双向链表;

Hash类型

数据结构

      map提供两种结构来存储,一种是hashtable、另一种是前面讲的ziplist,数据量小的时候用ziplist. 在redis中,哈希表分为三层,分别是,源码地址【dict.h】

dictEntry

    管理一个key-value,同时保留同一个桶中相邻元素的指针,用来维护哈希桶的内部链;

dictht

    实现一个hash表会使用一个buckets存放dictEntry的地址,一般情况下通过hash(key)%len得到的值就是buckets的 索引,这个值决定了我们要将此dictEntry节点放入buckets的哪个索引里,这个buckets实际上就是我们说的hash 表。dict.h的dictht结构中table存放的就是buckets的地址

dict

dictht实际上就是hash表的核心,但是只有一个dictht还不够,比如rehash、遍历hash等操作,所以redis定义了 一个叫dict的结构以支持字典的各种操作,当dictht需要扩容/缩容时,用来管理dictht的迁移,以下是它的数据结 构,源码在

集合类型

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

数据结构

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

有序集合

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

数据结构

      zset类型的数据结构就比较复杂一点,内部是以ziplist或者skiplist+hashtable来实现,这里面最核心的一个结构就 是skiplist,也就是跳跃表