Redis五大数据类型详解

关于Redis的五大数据类型,它们分别为:String、List、Hash、Set、SortSet。本文将会从它的底层数据结构常用操作命令、一些特点实际应用这几个方面进行解析。对于数据结构的解析,本文只会从大的方面来解析,不会介绍详细的代码实现。

String

1.实现结构

  String是Redis中最常用的一种数据类型,也是Redis中最简单的一种数据类型。首先,表面上它是字符串,但其实他可以灵活的表示字符串、整数、浮点数3种值。Redis会自动的识别这3种值。那么,String的底层数据机构又是怎样的呢?由于Redis是使用c语言实现的,而c语言中没有String这一数据类型,那么就需要自己实现一个类似于String的结构体。它的名字就叫做SDS(simple dynamic string),下面是它的代码结构。

1 typedef struct sdshdr {
2     // buf中已经占用的字符长度
3     unsigned int len;
4     // buf中剩余可用的字符长度
5     unsigned int free;
6     // 数据空间
7     char buf[];
8 }

如果有了解过Java集合框架类的朋友都知道,这种结构与集合中动态数组结构类似,那么就会涉及到一系列的扩容判断和操作,但这些具体的做法在这里不深入讲解。不过有一点比较重要的就是String的value值最大可以存放512MB的数据,所以有时候它不仅仅可以存放字符,还可以存放字节数据。

2.实际应用

  在讲实际应用之前,要声明的是Redis是基于单线程IO多路复用的架构实现的NoSql,意味着它的操作都是串行化的,所以在命令操作上不会出现线程安全问题,基于这个特性可以有很多应用。

  1. 分布式锁。利用Redis的串行化特性,可以轻松的实现分布式锁,其中用到的命令有:setnx key value , expire key time  ,del key ,其中第一个setnx是指在key不存在时能赋值成功,expire 来设置key的存活时间来防止程序异常而没有及时del到key值的情况。但是程序也有可能在expire没有执行时就已经挂掉的时候,这是可以来一个增强版set key value  NX EX time。这里的NX就是表示if not exist,而Ex表示时间单位秒,Px代表毫秒。
  2. 分布式session。这里仅仅是利用Redis的数据库功能,把分布式应用的session抽取到Redis中,普通的get、set,命令即可完成。
  3. 商品秒杀实现。把需要销售的商品提前放入Redis,通过redis的incrdecr命令安全的增加和减少库存。
  4. 限时验证。 expire key time ,判断exists key在短信验证时,当redis中存在数据则不允许再次请求验证发送。

List

1.实现结构

  首先,List的主要存取操作有lpush、lpop、rpush、rpop,有点像是双向队列。List的实现是灵活多样的,它分别有ziplist(压缩链表)、LinkedList(双向链表)两种实现方式。

  1.ziplist

  如下图所示,它是基于连续内存实现(类似数组)。当然,它的每一个entry的大小可能不是一致的,这就需要特殊的控制手段去解决,所以才叫压缩表。那么数组有的特性它都会有,比如在lpush、lpop的时候就会有数据的搬移,时间复杂度是O(n)。所以,一般在数据元素较少时使用ziplist结构实现。

  2.LinkedList

  则与我们日常所学的双向链表相差无异,同样也保留则头尾指针、数据长度等数据,这里就不再详细说明,需要了解的去读一读Java的LInkedList源码也不错。

2.实际应用

  1. 消息队列。使用lpush,brpop两个命令可以模拟一个消息队列。其中brpop key time为阻塞式弹出,当队列中为空时会阻塞当前操作,该操作需要添加超时参数,单位为秒。
  2. 有限集合。使用ltrim key start end操作可以获取一个固定位置的数据,可以快速实现一个有限的集合。

Hash

1.实现结构

  首先,Hash的特性我们可以想象为Java集合中的HashMap,一个hash中可以有多个field:value(键值对)。关于hash的实现同样有两种情况。一种是基于ZipList,一种是基于HashTable实现。

  1.ZipList

  这里的的ZipList与List当中的ZipList其实是相差无几的,唯一的特点就是Hash存储的时候,它的entry数量是成对增加的,同时也是成对存在的,所以它的长度一定是2的整数倍。filed值放在前面,value放在后面的形式存放。当然采用ZipList操作时,它的查找删改查的时间复杂度就会变为O(n),所以ZipList适合在数据较少的情况下使用。

2.HashTable

虽然说Hash与Java中的HashMap功能类似,但在HashTable这个结构上还是有一定的不同点的。要想了解HashTable的实现,需要了解三个结构。它们分别是:dictdicthtentry。entry和前面list中提到的类似,下面列出前面两个结构的定义:

 1 // 哈希表(字典)数据结构,Redis 的所有键值对都会存储在这里。其中包含两个哈希表。
 2 typedef struct dict {
 3     // 哈希表的类型,包括哈希函数,比较函数,键值的内存释放函数
 4     dictType *type;
 5     // 存储一些额外的数据
 6     void *privdata;
 7     // 两个哈希表
 8     dictht ht[2];
 9     // 哈希表重置下标,指定的是哈希数组的数组下标
10     int rehashidx; /* rehashing not in progress if rehashidx == -1 */
11     // 绑定到哈希表的迭代器个数
12     int iterators; /* number of iterators currently running */
13 } dict;
14
15 typedef struct dictht {
16     //槽位数组
17     dictEntry **table;
18     //槽位数组长度
19     unsigned long size;
20     //用于计算索引的掩码,可以理解为hash函数
21     unsigned long sizemask;
22     //真正存储的键值对数量
23     unsigned long used;
24 } dictht;

关系可以总结为下面这幅图:

2.实际应用

  1. 存放对象Object。你可能会发现,从宏观上来说,这种一个key , field1 : value1 ,field2 : value2 一个键值对应多个字段field的格式非常适合用于描写一个对象。所以,hash一般会用于描述一个对象,但其实我们在实际中也有可能会用一个Json格式的字符串来描述一个对象。那么这两种方法都可行的情况下,会有什么优缺点呢?利用hash描述一个对象:可以做到序列化开销小,可以单独修改某一个字段而不用读出全部数据,但是使用比较复杂。而使用json描述对象,使用简单但需要耗费额外的序列化开销。需要使用什么形式,具体情况需要具体分析。
  2. 结合Json描述对象的集合。例如,在商城应用中,可以利用Hash的key来描述一个用户的id,而field用于描述用户的购物车列表中的一个物品的详细信息。

Set

Set是一个不允许重复的,无顺序的数据集合。值得注意的是,这里说的无顺序其实还是有一点歧义的,那么到底是怎么回事呢?接下来的博文就会有提到这个差异。

1.实现结构

  1.IntSet。

  这里的IntSet是一种在满足特定情况下所使用的数据结构。这种情况就是当全部value都为整型时,redis会使用IntSet这种结构。在这个情况下它是有序的。这是为什么呢?先从结构开始说起,为了平衡空间的性能的消耗,Redis在数据都为整型的时候使用了一种基于动态数组的结构体,同时在存放元素时保正元素的大小顺序,这样就可以使用二分查找以时间复杂度O(logn)来完成增删改查的操作。这样就节省了很多空间。至于增和删操作,同样会涉及到数组的数据搬移操作。下面为它的结构体代码:

1 typedef struct intset {
2     // 编码方式
3   uint32_t enconding;
4   // 集合包含的元素数量
5   uint32_t length;
6   // 保存元素的数组
7   int8_t contents[];
8 } intset;

  2.HashTable

  当存在非整型数据的时候,Redis会自动把IntSet转换为HashTable的结构存放数据,但HashTable不能转换为IntSet。这里的HashTable与上面Hash结构提到的HashTable没有太大的差别。唯一的差别就在于Set存放在HashTable中只有Key值,没有value值,所以在HashTable的Entry中,Enrty的value永远为null。

2.实际应用

  1.  记录唯一的事物。如ip值,身份证等。
  2. 随机用户抽奖。通过srandmember key 随机返回一个set中的数据。
  3. 用户标签。当用户在使用某个产品的时候,后台可能会记录该用户对某个东西的喜好,从而在该标签中记录该用户。同时,可以利用sinter key1 key2、sunion、sdiff返回标签中的交集、并集、差集。这样就可以轻松得出用户的共同喜好、所有喜好、非共同喜好等数据。

SortSet

SortSet是一个实现了数据有序且唯一的键值对集合。其中,Entry的键为string类型,值为整型或浮点型,表示权值score。其中SortSet的顺序就是通过Score的值来确定的。

1.实现结构

SortSet的实现结构同样有两种,一种是ZipList结构实现,适用于较少数据的情况。另一种是SkipList+HashTable的形式,使用与数据较多的情况,其中SkipList是在保证有序的情况下优化范围查找的时间复杂度,而HashTable则是优化增删改查的时间复杂度。

  1.ZipList

SortSet的ZipList和Hash中的数据结构类似,同样也是存放键值对,但是它维护了基于Score的有序性(默认从小到大),这里就不再赘述。

  2.SkipList+HashTable

  首先来说明主要的SkipList(跳表),跳表是一种基于有序链表,通过建立多层索引,以空间换时间的方式实现平均查找效率为O(logn)复杂度的一种数据结构。下面给出一个跳表的基本形式图:

可以看到在根据数据的权值Score进行查找的时候,从最顶层的索引开始查找。当找到数据在某个范围后,在往下一层的索引查找,然后就这样一路缩小查找的范围。看上去是不是有点像二分查找?至于跳表的具体介绍和实现,可以参考这篇文章:为什么Redis要用跳表实现有序集合?

上面可以看到,基于跳表的实现的有序集合可以完成增删改查实现O(logn)的时间复杂度,那么有没有更加快的方式来实现O(1)的时间复杂度呢?通常说起O(1)的时间复杂度都会想起HashTable这个数据结构。Redis就利用HashTable+SkipList的组合数据结构,HashTable来实现增删改查的时间复杂度为O(1)的同时SkipList保证数据的有序性,可以方便的获取一个范围的数据。

至于HashTable的实现与前面谈到的一致,下面用一张图来说明两个数据结构结合是什么样子的。

上图中,每一个节点可以看成一个跳表的节点同时也是HashTable中的一个节点

  1. 在看跳表的时候,我们需要忽略hnext指针,每个节点通过双向链表来保证有序性。
  2. 在看HashTable的时候,可以忽略prev指针和next指针。看上去就是一个用拉链法解决冲突的HashTable,而hnext就是指向下一节点的指针。

这样,当我们需要增删改查的时候,利用HashTable的特性实现时间复杂度为1的操作,当我们需要基于权值Score进行范围查找的时候可以通过SkipList进行时间复杂度为O(logn)的查找。

2.实际应用

  1. 排行榜。使用zrange key start end。根据热度、积分、评论等可以衡量的权值Score进行排行,其中score排序为从小到大,用ZRERANGE实现从大到小排序。
  2. 获取某个权值范围的用户。例如在应用中获取积分为80到100的用户,可以使用ZRANGEBYSCORE key 80 100 WITHSCORES来输出score在80到100间的用户。

总结

太多东西记不住?来张思维导图帮你记忆一下。

原文地址:https://www.cnblogs.com/hill1126/p/11523329.html

时间: 2024-08-28 07:55:45

Redis五大数据类型详解的相关文章

  redis字符串内型详解

redis字符串内型详解 字符串 Redis中的字符串是一个字节序列.Redis中的字符串是二进制安全的,这意味着它们的长度不由任何特殊的终止字符决定.因此,可以在一个字符串中存储高达512兆字节的任何内容. 示例 redis 172.16.10.1:6379> set name "baidu.com"  OK  redis 172.16.10.1:6379> get name  "baidu.com"Shell 在上面的示例中,set和get是Redi

redis服务简介 && redis.conf配置文件详解

#一.redis服务简介 redis是一个key-value存储系统. 和Memcached类似,它支持存储的value类型相对更多(memcached不支持value类型,只支持key),包括string(字符串).list(链表).set(集合)和zset(有序集 合).这些数据类型都支持push/pop.add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的.在此基础上,redis 支持各种不同方式的排序.与memcached一样,为了保证效率,数据都是缓存在内存中

Redis常见配置文件详解

Redis常见配置文件详解 # vi redis.conf   daemonize yes #是否以后台进程运行 pidfile /var/run/redis/redis-server.pid    #pid文件位置 port 6379#监听端口 bind 127.0.0.1   #绑定地址,如外网需要连接,设置0.0.0.0 timeout 300     #连接超时时间,单位秒 loglevel notice  #日志级别,分别有: # debug :适用于开发和测试 # verbose :

Redis高可用详解:持久化技术及方案选择

Redis高可用详解:持久化技术及方案选择 Java架构师那些事 关注 0.3 2018.08.23 22:55 字数 9774 阅读 542评论 0喜欢 9 前言 本文将先说明上述几种技术分别解决了Redis高可用的什么问题,然后详细介绍Redis的持久化技术,主要是RDB和AOF两种持久化方案.在介绍RDB和AOF方案时,不仅介绍其作用及操作方法,同时还会介绍持久化实现的一些原理细节及需要注意的问题.最后,介绍在实际使用中持久化方案的选择以及经常遇到的问题等内容. 一.Redis高可用概述

【转】Redis内部数据结构详解——ziplist

本文是<Redis内部数据结构详解>系列的第四篇.在本文中,我们首先介绍一个新的Redis内部数据结构--ziplist,然后在文章后半部分我们会讨论一下在robj, dict和ziplist的基础上,Redis对外暴露的hash结构是怎样构建起来的. 我们在讨论中还会涉及到两个Redis配置(在redis.conf中的ADVANCED CONFIG部分): hash-max-ziplist-entries 512 hash-max-ziplist-value 64 本文的后半部分会对这两个配

PHP100-第三讲 PHP5.4 语法、常量、变量、数据类型详解

内容摘要: ①PHP5.4 的基本语法与写作格式 ②PHP5.4 的变量与变量数据类型 ③PHP5.4 的系统常量与自定义常量 PHP5.4 的基本语法与写作格式: 任何程序语言都有自己的语言风格,PHP语言也有自己独特的风格,虽然也继承了许多Perl和C的语言特色.但经过多年的发展PHP已经成为了一个成熟 的编程语言,所以我们还需要认真的学习PHP的独特语法.PHP一个很大的特色就是与HTML标签语言进行混编,这种模式是今后很长一段学习过程中所用到 的格式,因此我们先来通过一个例子来认识一下P

MySQL 数据类型 详解

MySQL 数据类型 详解 MySQL 的数值数据类型可以大致划分为两个类别,一个是整数,另一个是浮点数或小数.许多不同的子类型对这些类别中的每一个都是可用的,每个子类型支持不同大小的数据,并且 MySQL 允许我们指定数值字段中的值是否有正负之分或者用零填补. 1.整型 MySQL数据类型 含义(有符号) tinyint(m) 1个字节 范围(-128~127) smallint(m) 2个字节 范围(-32768~32767) mediumint(m) 3个字节 范围(-8388608~83

Mysql数据类型详解

MySQL数据类型包括 1.整数类型,浮点数类型和定点数类型 2.时间和日期类型 3.字符串类型 4.二进制类型 整数类型 标准的SQL中支持INTEGER,SMALLINT这两类整数类型,MySQL除了这两个还有其他的,见下表 类型 大小 范围(有符号) 范围(无符号) 默认宽度 TINYINT 1 字节 (-128,127) (0,255) 4 SMALLINT 2 字节 (-32 768,32 767) (0,65 535) 6 MEDIUMINT 3 字节 (-8 388 608,8 3

MySQL 数据类型 详解 (转载)

数值类型 MySQL 的数值数据类型可以大致划分为两个类别,一个是整数,另一个是浮点数或小数.许多不同的子类型对这些类别中的每一个都是可用的,每个子类型支持不同大小的数据,并且 MySQL 允许我们指定数值字段中的值是否有正负之分或者用零填补. 表列出了各种数值类型以及它们的允许范围和占用的内存空间. 类型 大小 范围(有符号) 范围(无符号) 用途 TINYINT 1 字节 (-128,127) (0,255) 小整数值 SMALLINT 2 字节 (-32 768,32 767) (0,65