Redis 有哪些数据类型?

Redis 的数据类型可谓是 Redis 的精华所在,同样的数据类型,例如字符串存储不同的值对应的实际存储结构也是不同,当你存储的 int 值是实际的存储结构也是 int,如果是短字符串(小于 44 字节)实际存储的结构为 embstr,长字符串对应的实际存储结构是 raw,这样设计的目的是为了更好的节约内存。

我们本文的面试题是 Redis 有哪些数据类型?

典型回答

Redis 最常用的数据类型有 5 种:String(字符串类型)、Hash(字典类型)、List(列表类型)、Set(集合类型)、ZSet(有序集合类型)。

1.字符串类型

字符串类型(Simple Dynamic Strings 简称 SDS),译为:简单动态字符串,它是以键值对 key-value 的形式进行存储的,根据 key 来存储和获取 value 值,它的使用相对来说比较简单,但在实际项目中应用非常广泛。

字符串的使用如下:

127.0.0.1:6379> set k1 v1 # 添加数据 
OK
127.0.0.1:6379> get k1 # 查询数据
"v1"
127.0.0.1:6379> strlen k1 # 查询字符串的长度
(integer) 5

我们也可以在存储字符串时设置键值的过期时间,如下代码所示:

127.0.0.1:6379> set k1 v1 ex 1000 # 设置 k1 1000s 后过期(删除)
OK

我们还可以使用 SDS 来存储 int 类型的值,并且可以使用 incr 指令和 decr 指令来操作存储的值 +1 或者 -1,具体实现代码如下:

127.0.0.1:6379> get k1 # 查询 k1=3
"3"
127.0.0.1:6379> incr k1 # 执行 +1 操作
(integer) 4
127.0.0.1:6379> get k1 # 查询 k1=4
"4"
127.0.0.1:6379> decr k1 # 执行 -1 操作
(integer) 3
127.0.0.1:6379> get k1 # 查询 k1=3
"3"

字符串的常见使用场景:

  • 存放用户(登录)信息;
  • 存放文章详情和列表信息;
  • 存放和累计网页的统计信息(存储 int 值)。

……

2.字典类型

字典类型 (Hash) 又被成为散列类型或者是哈希表类型,它是将一个键值 (key) 和一个特殊的“哈希表”关联起来,这个“哈希表”表包含两列数据:字段和值。例如我们使用字典类型来存储一篇文章的详情信息,存储结构如下图所示: 哈希表存储结构.png 同理我们也可以使用字典类型来存储用户信息,并且使用字典类型来存储此类信息就无需手动序列化和反序列化数据了,所以使用起来更加的方便和高效。

字典类型的使用如下:

127.0.0.1:6379> hset myhash key1 value1 # 添加数据
(integer) 1
127.0.0.1:6379> hget myhash key1 # 查询数据
"value1"

字典类型的数据结构,如下图所示:

Redis-HashType-02.png

通常情况下字典类型会使用数组的方式来存储相关的数据,但发生哈希冲突时才会使用链表的结构来存储数据。

3.列表类型

列表类型 (List) 是一个使用链表结构存储的有序结构,它的元素插入会按照先后顺序存储到链表结构中,因此它的元素操作 (插入和删除) 时间复杂度为 O(1),所以相对来说速度还是比较快的,但它的查询时间复杂度为 O(n),因此查询可能会比较慢。

列表类型的使用如下:

127.0.0.1:6379> lpush list 1 2 3 # 添加数据
(integer) 3
127.0.0.1:6379> lpop list # 获取并删除列表的第一个元素
1

列表的典型使用场景有以下两个:

  • 消息队列:列表类型可以使用 rpush 实现先进先出的功能,同时又可以使用 lpop 轻松的弹出(查询并删除)第一个元素,所以列表类型可以用来实现消息队列;
  • 文章列表:对于博客站点来说,当用户和文章都越来越多时,为了加快程序的响应速度,我们可以把用户自己的文章存入到 List 中,因为 List 是有序的结构,所以这样又可以完美的实现分页功能,从而加速了程序的响应速度。

4.集合类型

集合类型 (Set) 是一个无序并唯一的键值集合。

集合类型的使用如下:

127.0.0.1:6379> sadd myset v1 v2 v3 # 添加数据
(integer) 3
127.0.0.1:6379> smembers myset # 查询集合中的所有数据
1) "v1"
2) "v3"
3) "v2"

集合类型的经典使用场景如下:

  • 微博关注我的人和我关注的人都适合用集合存储,可以保证人员不会重复;
  • 中奖人信息也适合用集合类型存储,这样可以保证一个人不会重复中奖。

集合类型(Set)和列表类型(List)的区别如下:

  • 列表可以存储重复元素,集合只能存储非重复元素;
  • 列表是按照元素的先后顺序存储元素的,而集合则是无序方式存储元素的。

5.有序集合类型

有序集合类型 (Sorted Set) 相比于集合类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。有序集合的存储元素值也是不能重复的,但分值是可以重复的。

当我们把学生的成绩存储在有序集合中时,它的存储结构如下图所示:

学生存储值.png

有序集合类型的使用如下:

127.0.0.1:6379> zadd zset1 3 golang 4 sql 1 redis # 添加数据
(integer) 3
127.0.0.1:6379> zrange zset 0 -1 # 查询所有数据
1) "redis"
2) "mysql"
3) "java"

有序集合的经典使用场景如下:

  • 学生成绩排名;
  • 粉丝列表,根据关注的先后时间排序。

考点分析

关于 Redis 数据类型的这个问题,对于大多数人既熟悉又陌生,熟悉的是每天都在使用 Redis 存取数据,陌生的是对于 Redis 的数据类型知之甚少,因为对于普通的开发工作使用字符串类型就可以搞定了。但是善用 Redis 的数据类型可以到达意想不到的效果,不但可以提高程序的运行速度又可以减少业务代码,可谓一举两得。

例如我们经常会把用户的登录信息存储在 Redis 中,但通常的做法是先将用户登录实体类转为 JSON 字符串存储在 Redis 中,然后读取时先查询数据再反序列化为 User 对象,这个过程看似没什么问题,但我们可以有更优的解决方案来处理此问题,比如我们可以使用 Hash 存储用户的信息,这样就无需序列化的过程了,并且读取之后无需反序列化,直接使用 Map 来接收就可以了,这样既提高了程序的运行速度有省去了序列化和反序列化的业务代码。

与此知识点相关的面试题还有以下几个:

  • 有序列表的实际存储结构是什么?
  • 除了五种基本的数据类型之外,还有什么数据类型?

知识扩展

有序列表的内部实现

有序集合是由 ziplist (压缩列表) 或 skiplist (跳跃表) 组成的。

ziplist 介绍

当数据比较少时,有序集合使用的是 ziplist 存储的,如下代码所示:

127.0.0.1:6379> zadd myzset 1 db 2 redis 3 mysql
(integer) 3
127.0.0.1:6379> object encoding myzset
"ziplist"

从结果可以看出,有序集合把 myset 键值对存储在 ziplist 结构中了。 有序集合使用 ziplist 格式存储必须满足以下两个条件:

  • 有序集合保存的元素个数要小于 128 个;
  • 有序集合保存的所有元素成员的长度都必须小于 64 字节。

如果不能满足以上两个条件中的任意一个,有序集合将会使用 skiplist 结构进行存储。 接下来我们来测试以下,当有序集合中某个元素长度大于 64 字节时会发生什么情况? 代码如下:

127.0.0.1:6379> zadd zmaxleng 1.0 redis
(integer) 1
127.0.0.1:6379> object encoding zmaxleng
"ziplist"
127.0.0.1:6379> zadd zmaxleng 2.0 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(integer) 1
127.0.0.1:6379> object encoding zmaxleng
"skiplist"

通过以上代码可以看出,当有序集合保存的所有元素成员的长度大于 64 字节时,有序集合就会从 ziplist 转换成为 skiplist。

小贴士:可以通过配置文件中的 zset-max-ziplist-entries(默认 128)和 zset-max-ziplist-value(默认 64)来设置有序集合使用 ziplist 存储的临界值。

skiplist 介绍

skiplist 数据编码底层是使用 zset 结构实现的,而 zset 结构中包含了一个字典和一个跳跃表,源码如下:

typedef struct zset {dict *dict;zskiplist *zsl;
} zset;

跳跃表的结构如下图所示: 有序集合-跳跃表.png

根据以上图片展示,当我们在跳跃表中查询值 32 时,执行流程如下:

  • 从最上层开始找,1 比 32 小,在当前层移动到下一个节点进行比较;
  • 7 比 32 小,当前层移动下一个节点比较,由于下一个节点指向 Null,所以以 7 为目标,移动到下一层继续向后比较;
  • 18 小于 32,继续向后移动查找,对比 77 大于 32,以 18 为目标,移动到下一层继续向后比较;
  • 对比 32 等于 32,值被顺利找到。

从上面的流程可以看出,跳跃表会想从最上层开始找起,依次向后查找,如果本层的节点大于要找的值,或者本层的节点为 Null 时,以上一个节点为目标,往下移一层继续向后查找并循环此流程,直到找到该节点并返回,如果对比到最后一个元素仍未找到,则返回 Null。

高级数据类型

除了有 5 大基本数据类型外,还有 GEO(地理位置类型)、HyperLogLog(统计类型)、Stream(流类型)。

GEO(地理位置类型)是 Redis 3.2 版本中新增的数据类型,用于存储和查询地理位置的,使用它我们可以实现查询附近的人或查询附近的商家等功能(这部分的内容会在后面的章节单独讲解)。

Stream(流类型)是 Redis 5.0 版本中新增的数据类型,因为使用 Stream 可以实现消息消费确认的功能,使用“xack key group-key ID”命令,所以此类型的出现给 Redis 更好的实现消息队列提供了很大的帮助。

HyperLogLog(统计类型)是本文介绍的重点,HyperLogLog (下文简称为 HLL) 是 Redis 2.8.9 版本添加的数据结构,它用于高性能的基数 (去重) 统计功能,它的缺点就是存在极低的误差率。

HLL 具有以下几个特点:

  • 能够使用极少的内存来统计巨量的数据,它只需要 12K 空间就能统计 2^64 的数据;
  • 统计存在一定的误差,误差率整体较低,标准误差为 0.81%;
  • 误差可以被设置辅助计算因子进行降低。

HLL 的命令只有 3 个,但都非常的实用,下面分别来看。

1.添加元素

127.0.0.1:6379> pfadd key "redis"
(integer) 1
127.0.0.1:6379> pfadd key "java" "sql"
(integer) 1

相关语法: pfadd key element [element ...] 此命令支持添加一个或多个元素至 HLL 结构中。

2.统计不重复的元素

127.0.0.1:6379> pfadd key "redis"
(integer) 1
127.0.0.1:6379> pfadd key "sql"
(integer) 1
127.0.0.1:6379> pfadd key "redis"
(integer) 0
127.0.0.1:6379> pfcount key
(integer) 2

从 pfcount 的结果可以看出,在 HLL 结构中键值为 key 的元素, 有 2 个不重复的值:redis 和 sql,可以看出结果还是挺准的。 相关语法: pfcount key [key ...]
此命令支持统计一个或多个 HLL 结构。

3.合并一个或多个 HLL 至新结构

新增 k 和 k2 合并至新结构 k3 中,代码如下:

127.0.0.1:6379> pfadd k "java" "sql"
(integer) 1
127.0.0.1:6379> pfadd k2 "redis" "sql"
(integer) 1
127.0.0.1:6379> pfmerge k3 k k2
OK
127.0.0.1:6379> pfcount k3
(integer) 3

相关语法:pfmerge destkey sourcekey [sourcekey ...] ** pfmerge 使用场景:当我们需要合并两个或多个同类页面的访问数据时,我们可以使用 pfmerge 来操作。

总结

本文我们介绍了 Redis 的 5 大基础数据类型的概念以及简单的使用:String(字符串类型)、Hash(字典类型)、List(列表类型)、Set(集合类型)、ZSet(有序集合类型),还深入的介绍了 ZSet 的底层数据存储结构:ziplist (压缩列表) 或 skiplist (跳跃表)。除此之外我们还介绍了 Redis 中的提前 3 个高级的数据类型:GEO(地理位置类型)用于实现查询附近的人、HyperLogLog(统计类型)用于高效的实现数据的去重统计(存在一定的误差)、Stream(流类型)主要应用于消息队列的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545332.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

导出/入数据库

导出/入数据库1、以SQL文件的方式1.1导出1.2 导入2、以mdf和ldf数据库文件的方式2.1导出2.1.1 脱机2.1.2 到数据库的数据路径,拷贝出mdf,ldf文件2.1.3 将原数据库设置为online状态即可正常使用2.2导入数据库(切记导入之前要先将控制权限打开)…

arm中clz指令_JavaScript中带有示例的Math.clz32()方法

arm中clz指令JavaScript | Math.clz32()方法 (JavaScript | Math.clz32() Method) Math.clz32() is a function in math library of JavaScript that is used to find the number of leading zeroes in the 32-bit representation of the number. The method will return the n…

Oracle 创建用户 scott 例

在OracleXE中创建scott用户1、打开SQL*Plus,以 sys用户登录数据库 connect / as sysdba2、依次执行下面命令 --DROP USER scott CASCADE; CREATE USER scott IDENTIFIED BY tiger; GRANT connect,resource TO scott; GRANT CREATE DATABASE LINK, CREATE MATERIALIZ…

第八章Transact-SQL程序设计

第八章Transact-SQL程序设计8.1_变量8.1.1_局部变量8.1.2_全局变量8.2_流程控制语句8.2.1_IF...ELSE语句8.2.2_while循环语句8.1_变量 8.1.1_局部变量 局部变量的声明定义: Declare Variable_name Datatype[, Variable_name Datatype]…--举例: decla…

Redis 如何处理已经过期的数据?

上一篇我们讲了 Redis 内存用完之后的内存淘汰策略,它主要是用来出来异常情况下的数据清理,而本文讲的是 Redis 的键值过期之后的数据处理,讲的是正常情况下的数据清理,但面试者常常会把两个概念搞混,以至于和期望的工作失之交臂。我们本文的职责之一就是帮读者朋友搞清楚…

如何删除多余系统引导项

我们很多人都装过双系统,但是有时候装的当中却不想装了或者装不成功,生成的多余系统引导项怎么删除呢?下面分享下我的经验:win7(XP)下如何删除多余的系统引导项。关键词:删除多余系统引导项&…

动态规划编程面试_面试的前25大动态编程问题

动态规划编程面试Dynamic programming is one of the most asked paradigms in any product-based company interviews. You can expect DP in online assessments also if you are in touch with any product-based company. For beginner its, of course, a Tough nut to cra…

第九章存储过程

第九章存储过程9.1_游标的使用9.1.1_游标简介及使用流程9.1.2_游标的声明9.1.3_使用游标读取数据9.1.4_举例说明9.2_存储过程9.2.1_存储过程简介9.2.2_存储过程定义及执行9.2.3_重写存储过程9.2.6_删除存储过程9.2.5_举例说明9.1_游标的使用 9.1.1_游标简介及使用流程 使用游…

Oracle笔记:用户、权限及exp/imp数据

--模式(方案)逻辑概念:一个数据对象的集合,每一个用户--都有一个与之同名的模式,用于存放此用户名下的所有数据对象。select * from user_objectsselect * from dba_users;--创建用户1、给用户创建自己的数据表空间cre…

Redis 内存用完会怎样?

在某些极端情况下,软件为了能正常运行会做一些保护性的措施,比如运行内存超过最大值之后的处理,以及键值过期之后的处理等,都属于此类问题,而专业而全面的回答这些问题恰好是一个工程师所具备的优秀品质。 我们本文的面试题是 Redis 内存用完之后会怎么? 典型回答 Red…

Linux学习十七、正规表达式练习题

情境模拟题一:透过 grep 搜寻特殊字串,并配合数据流重导向来处理大量的文件搜寻问题。目标:正确的使用正规表示法;前提:需要了解数据流重导向,以及透过子命令 $(command) 来处理档名的搜寻; 我们…

ntp symmetric_Python使用示例设置symmetric_difference()方法

ntp symmetric设置symmetric_difference()方法 (Set symmetric_difference() Method) symmetric_difference() method is used to get the list of all elements which are not common in both sets, the method is called with this set (set1) and another set (set2) is sup…

第十章触发器的创建与管理

第十章触发器的创建与管理10.1_触发器简介10.2_触发器的创建、修改、删除10.2.1_触发器的创建10.2.2_触发器的修改10.2.3_触发器的删除10.2.4_触发器的创建、修改、删除举例10.3_instead of触发器10.3.1_instead of触发器简介及举例 有疑问10.4_inserted、deleted表10.5_注意事…

英语笔记:词组句子:0812

Among 表示多人之间的一种共同关系 Within 在……之内 On 在……之上 Towards 向、对于 Sponsor 发起、主办 Spread 扩散、蔓延 Speed 快行、急走 Spur 激励、刺激 Entrance 入口 Chance 机会 Route 路线 Less likely 较少可能 Shrink 缩短 Tighten 绷紧 Limit 限…

如何在海量数据中查询一个值是否存在?

一般面试中考察的题目通常是由三类组成的,基础面试题、进阶面试题、开放性面试题,而本文的题目则属于一个开放性的面试题,但对于 Redis 这种以数据为核心的缓存中间件来说,实现在海量数据中查询一个值是否存在还是相对比较容易的。 因为是海量数据,所以我们就无法将每个键…

Hapoxy+keepalived实现双主高可用负载均衡

在测试了NginxKeepalived的负载均衡后,也对HaproxyKeepalived双主模式做了测试,双主的模式充分利用了服务器资源,这样不会导致服务器浪费。 这里举例说明: 默认情况下,第一台负载均衡器主要分发 www.breaklinux的请求…

c语言中的printf函数_C语言中的printf()函数与示例

c语言中的printf函数C语言中的printf()函数 (printf() function in C) The printf() function is defined in the <stdio.h> header file. 在<stdio.h>头文件中定义了printf()函数 。 Prototype: 原型&#xff1a; int printf(const char* str, . . .);Parameter…

第一章数据库绪论

第一章数据库绪论1.1_数据库系统概述1.1.1_数据库的四个基本概念1.1.2_数据库系统的特点1.2_数据库模型1.2.1_两类数据模型1.2.2_概念模型1.2.3_数据模型的组成要素1.2.4_常用的数据模型1.3_数据库系统的结构1.3.2_数据库系统的三级模式结构1.3.3_数据库的二级映像功能与数据独…

如何保证 Redis 消息队列中的数据不丢失?

Redis 最常见的业务场景就是缓存读取与存储,而随着时间的推移,有人开始将它作为消息队列来使用了,并且随着 Redis 版本的发展,在 Redis.2.0.0 中新增了发布订阅模式(Pub/Sub)代表着官方开始正式支持消息队列的功能了,直到今天为止还有部分公司在实现轻量级的消息队列时,…

英语笔记:写作:Limiting the use of disposable plastic bags

Limiting the use of disposable plastic bags 限制使用一次性塑料袋 Recently,limiting the use of disposable plastic bags has been brought to popularattention in china. No one denies that they once gained great popularity in ourdaily life. However, what would…