细谈Redis五大数据类型

文章原创于公众号:程序猿周先森。本平台不定时更新,喜欢个人文章,欢迎关注个人微信公众号。
file程序员

上一篇文章有提到,Redis中使用最频繁的有5种数据类型:String、List、Hash、Set、SortSet。上一篇文章只是单纯介绍了下这5种数据类型使用到的指令以及经常使用场景,本篇文章会谈谈5种数据类型的底层数据结构以及各自经常使用的操做命令来分别进行解析。Redis做为目前最流行的Key-Value型内存数据库,不只数据库操做在内存中进行,而且可按期的将数据持久化到磁盘中,因此性能相对普通数据库高不少,而在Redis中,每一个Value实际上都是以一个redisObject结构来表示:
typedef struct redisObject{redis

unsigned type:4;
    unsigned encoding:4;
    void *ptr;
    int refCount;
    unsigned lru:

}
咱们能够看看这几个参数分别的含义:算法

  • type:对象的数据类型,通常状况就是5大数据类型。
  • encode:redisObject对象底层编码实现,主要编码类型有简单动态字符串,链表,字典,跳跃表,整数集合及压缩列表。
  • *ptr:指向底层实现数据结构的指针。
  • refCount:计数器,当引用计数值为0将会释放对象。
  • lru:最后一次访问本对象的时间。

String数据类型数据库

String 数据结构是简单的 Key-Value 类型,是Redis中最经常使用的一种数据类型,Value 能够是string或者数字。String数据类型实际上能够存储字符串、整数、浮点数三种不一样类型的值,Redis是如何作到自动识别字符串、整数、浮点数三种不一样类型的值。Redis是使用C实现的,可是并未使用C中的字符串,实际上Redis本身实现了一个结构体SDS来替代String类型:
struct sdshdr{数组

//记录buf数组中已使用字节的长度
    int len;
    //记录buf数组中剩余空间的长度
    int free;
    //字节数组,用于存储字符串
    char buf[];

};微信

咱们能够看到free参数是用来判断剩余可以使用空间的长度,len表示字符串的长度,buf存储字符串的每个字符以及结尾的'0'。为何Redis要本身实现SDS结构体呢?由于SDS结构体有几个优势:session

  • 因为len保存了当前字符串的实际长度,因此获取长度时间复杂度为O(1)。
  • SDS在拼接以前会对当前字符串的空间进行自动调整和扩展,防止当前字符串数据溢出。
  • 减小内存分配次数,SDS拼接字符串发生时,若是此时的字符串长度len小于1M,则SDS会分配和len大小相同的未使用空间给free,若是此时的字符串长度len大于1M,则SDS会分配和1M的未使用空间给free,当字符串缩短时,缩短的空间会叠加到free中,用于后续的拼接使用。

String数据类型经常使用命令:数据结构

  • 经常使用命令:set、get、decr、incr、mget 等。

String数据类型适用场景:分布式

  • 分布式锁
  • 分布式session:将分布式应用session存储到Redis中
  • 商品秒杀
  • 常规计数:博客数,阅读数

List数据类型函数

List数据结构是用来存储多个有序的字符串,List中的每一个字符串成为元素,List提供了节点重排和节点顺序访问的能力,在Redis中,List能够在两端push和pop元素,还能够获取指定范围的元素列表,获取指定索引下标的元素等,List数据结构主要有zipList(压缩链表)和LinkedList(双向链表)两种实现方式。首先咱们能够先看看LinkedList的结构:

type struct list{

//表头节点
        listNode *head;
        //表尾节点
        listNode *tail;
        //包含的节点总数
        unsigned long len;

};

能够看到每一个LinkedList中都会包含一个表头节点head和一个表尾结点tail,在LinkedList中每一个节点都会有一个prev指向前一个元素,同时还有一个next指向后一个元素,每一个节点的value就是节点的值。从而实现双向链表,理解起来实际上和C中的双向链表有很大程度的类似性。而另外一种实现方式zipList是基于连续内存实现,有点相似于数组方式,可是和数组有点不一致的是zipList的每个entry的大小可能不一致,须要特殊方法去控制解决,可是在执行push,pop操做时会有数据的迁移,时间复杂度为O(n), 因此通常只有在元素较少时才会使用zipList,咱们能够看看zipList的结构:

type struct ziplist{

//整个压缩列表的字节数
        uint32_t zlbytes;
        //记录压缩列表尾节点到头结点的字节数,直接能够求节点的地址
        uint32_t zltail_offset;
        //记录了节点数,有多种类型,默认以下
        uint16_t zllength;
        //节点
        List entryX;

}

zipList中每一个节点都会有如下几个参数信息:

  • previous_entry_length:记录前一个节点的字节长度
  • content:节点所存储的内容,能够是一个字节数组或者整数
  • encoding:记录content属性中所保存的数据类型以及长度

* List数据类型适用场景

在渲染文章列表时可使用List数据类型,通常状况下每一个用户都会有本身发布的文章列表,若是须要展现文章列表,就可使用List数据类型,不但能够有序并且能够按照索引范围去查询文章列表。

Set数据类型

Set数据类型和List数据类型有点相似,也能够用来保存多个元素,但最大的一点区别在于Set数据类型不容许出现重复的元素,而且Set中的元素是无序的,因此没办法和List同样经过索引下标获取元素,可是Set类型支持多个Set集合取交集、并集、差集,因此合理使用Set数据类型,能够在实际项目开发中解决不少问题。Set数据类型有两种数据结构:IntSet和HashTable。首先咱们来看看IntSet的结构:

typedef struct intset {

// 编码方式
    uint32_t enconding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组 
    int8_t contents[];

} intset;

当Set集合中全部元素都为整型时,Redis才会使用IntSet数据结构。有一点须要格外注意的是:IntSet数据结构是有序的。由于为了减轻性能的消耗,Redis在Set集合元素都为整型时,会使用一种基于动态数组的结构体,同时在push元素的时候控制元素的大小顺序,这样就可使用二分查找算法来对元素进行push及pop操做,这样时间复杂度仅为O(logN)。在Set集合中元素存在非整型数据时,Redis这时会自动采用HashTable数据结构来存放数据,在HashTable中,存放的只有key值而没有value值,因此说在HashTable中,键值永远为null。咱们能够看下HashTable的结构:

typedef struct dict{

//类型特定函数
    dictType *type;
    //哈希表 两个,一个用于实时存储,一个用于rehash
    dictht ht[2];
    //rehash索引 数据迁移时使用
    unsigned rehashidx;

}

Set数据类型使用场景:

  • 记录惟一值:好比登陆ip,身份证号
  • 添加标签:能够经过标签的交并集计算用户喜爱程度等数据。

Hash数据类型
在Redis中哈希类型是指键自己又是一种键值对结构,也就是咱们所说的对象,因此Hash数据类型用来存储对象是最合适的数据类型。Hash数据类型的编码能够是zipList或HashTable。当哈希对象保存的全部键值对长度小于64字节而且元素数量少于512时使用zipList,不然使用HashTable。zipList与刚才List数据类型中讲到的zipList实际上基本一致,惟一区别在于Hash存储entry数量成对增长,因此长度必定为2的整数倍。固然,使用zipList刚才已经说过push和pop时间复杂度为O(n),因此只能在数据量少的状况下才容许使用。而HashTable其实有点相似于Java中的HashTable,HashTTable主要依赖于三个结构:dict、dictht、entry。三个结构的关系能够表示为以下这幅图:
file

Hash数据类型适用场景:

  • 存储对象数据。
  • 结合Json描述对象集合。

SortSet数据类型

有序集合是在Set集合的基础上,保留了Set集合中不能存在重复元素的特性,可是不一样的是,SortSet集合中元素是能够排序的,SortSet排序和List排序均可以使用索引下标做为排序依据,因此说SortSet实现了数据有序且键值对惟一的集合,SortSet的数据结构有两种:zipList和skipList + HashTable,zipList都不用多少了,是用于数据量较少的状况,默认排序为元素从小到大。而采用skipList + HashTable的数据结构,skipList会在保证集合有序的状况下优化范围查找的时间复杂性,而HashTable刚才已经提到过它能够优化push和pop元素时的时间复杂性。skipList基于有序链表,能够建立多层索引,实现以空间复杂度来换取时间复杂度的作法,最终实现时间复杂度为O(logN)的元素查询过程,当须要push或者pop元素时,则使用HashTable实现时间复杂度仅为O(1).

SortSet数据类型适用场景

  • 积分排行榜:根据积分排序从小到大
  • 获取某个范围的数据:考试80-100分的数据

欢迎关注公众号:程序员周先森
file

相关文章
相关标签/搜索