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

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

typedef struct redisObject{

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结构体有几个优点:

  • 由于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。三个结构的关系可以表示为如下这幅图:

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分的数据

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

细谈Redis五大数据类型的更多相关文章

  1. Redis五大数据类型的常用操作

    在上一篇博文<centos安装redis>中,已经详细介绍了如何在centos上安装redis,今天主要介绍下Redis五大数据类型及其五大数据类型的相关操作. Redis支持五种数据类型 ...

  2. 一文搞定Redis五大数据类型及应用场景

    本文学习知识点 redis五大数据类型数据类型:string.hash.list.set.sorted_set 五大类型各自的应用场景 @TOC 1. string类型 1-1 string类型数据的 ...

  3. redis 五大数据类型使用

    redis 五大数据类型使用 字符串str 单个值 127.0.0.1:6379> set name pp # 设置键值[O(1)] OK 127.0.0.1:6379> setex na ...

  4. 《Redis入门指南(第二版)》读书思考总结之Redis五大数据类型

    热身:系统级命令 1. 获得符合规则的键名列表 KEYS pattern 模式匹配 产品的缓存:product+"."+....;  => keys product* 订单的 ...

  5. Redis五大数据类型详解

    关于Redis的五大数据类型,它们分别为:String.List.Hash.Set.SortSet.本文将会从它的底层数据结构.常用操作命令.一些特点和实际应用这几个方面进行解析.对于数据结构的解析, ...

  6. redis五大数据类型以及常用操作命令

    Redis的五大数据类型 String(字符串) string是redis最基本的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value.string类型是二进制安全的.意 ...

  7. 四:Redis五大数据类型

    Redis的五大数据类型 1.string(字符串) string是Redis最基本的类型,你可以理解成与menmcached一模一样的类型,一个key对应一个value string类型是二进制安全 ...

  8. Redis五大数据类型

    首先说明下,Redis是:单线程+多路IO复用技术!!! string set  >  key  +  zset          list hash 常用的几个命令: >keys * 查 ...

  9. 【转】细谈Redis和Memcached的区别

    Redis的作者Salvatore Sanfilippo曾经对这两种基于内存的数据存储系统进行过比较: Redis支持服务器端的数据操作:Redis相比Memcached来说,拥有更多的数据结构和并支 ...

随机推荐

  1. 从零开始实现ASP.NET Core MVC的插件式开发(五) - 插件的删除和升级

    标题:从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除 作者:Lamond Lu 地址:https://www.cnb ...

  2. go 学习笔记之是否支持以及如何实现继承

    熟悉面向对象的小伙伴们可能会知道封装,继承和多态是最主要的特性,为什么前辈们会如此看重这三种特性,真的那么重要吗? 什么是封装 什么是封装,封装有什么好处以及怎么实现封装? 相信大多数小伙伴们都有自己 ...

  3. java 正则 替换中文为空

    //中文替换为"" public String replaceChineseToNULL(String s){ String reg = "[\u4e00-\u9fa5] ...

  4. Spark应用监控解决方案--使用Prometheus和Grafana监控Spark应用

    Spark任务启动后,我们通常都是通过跳板机去Spark UI界面查看对应任务的信息,一旦任务多了之后,这将会是让人头疼的问题.如果能将所有任务信息集中起来监控,那将会是很完美的事情. 通过Spark ...

  5. python 04 列表

    1.列表——list [ ] 有序.可变.支持索引查看. 存储数据,支持大多数数据类型:字符串,数字,布尔值.列表.集合.元组.字典等. 1.1 定义: lst(勿用list)  lst = [&qu ...

  6. spss分析存在共性线后,接下来是怎么分析?

    在进行线性回归分析时,容易出现自变量(解释变量)之间彼此相关,这种情况被称作多重共线性问题. 适度的多重共线性不成问题,但当出现严重共线性问题时,可能导致分析结果不稳定,出现回归系数的符号与实际情况完 ...

  7. Elasticsearch(5)--- 基本命令(集群相关命令、索引CRUD命令、文档CRUD命令)

    Elasticsearch(5)--- 基本命令 这篇博客的命令分为ES集群相关命令,索引CRUD命令,文档CRUD命令.这里不包括Query查询命令,它单独写一篇博客. 一.ES集群相关命令 ES集 ...

  8. POJ-2153Colored Sticks解题报告+欧拉回路,字典树,并查集;

    传送门:http://poj.org/problem?id=2513 题意:给你许多木棍,木棍两端都有颜色,问能不能首尾相接,要求颜色相同. 参考:https://www.cnblogs.com/ku ...

  9. POJ-2387 Til the Cows Come Home ( 最短路 )

    题目链接: http://poj.org/problem?id=2387 Description Bessie is out in the field and wants to get back to ...

  10. python数据结构——线性表

    线性表 线性表可以看作是一种线性结构(可以分为顺序线性结构,离散线性结构) 1. 线性表的种类: 顺序表 元素存储在一大块连续存储的地址中,首元素存入存储区的起始位置,其余元素顺序存放. (元素之间的 ...