热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Redis9种数据结构简述

目录一、简介二、Redis内部编码三、5种最基本数据结构1.String(字符串)1.1内部编码1.2应用1.3数据结构1.3.1什么是简单动态字符串(SDS)1.3.2SDS的数

目录

  • 一、简介
  • 二、Redis 内部编码
  • 三、5种最基本数据结构
    • 1. String(字符串)
      • 1.1 内部编码
      • 1.2 应用
      • 1.3 数据结构
        • 1.3.1 什么是简单动态字符串(SDS)
        • 1.3.2 SDS 的数据结构
        • 1.3.3 SDS 与 C语言字符串的区别
          • 1.3.3.1 获取字符串长度
          • 1.3.3.2 杜绝缓冲区溢出
          • 1.3.3.3 减少修改字符串时带来的内存重分配次数
            • 1.3.3.3.1 空间预分配
            • 1.3.3.3.2 惰性空间释放
          • 1.3.3.4 获取字符串长度
          • 1.3.3.5 兼容部分 C 字符串函数
          • 1.3.3.5 总结
      • 1.4 命令使用

参考1:Redis学习(一)简单动态字符串(SDS)

一、简介

redis 9种数据结构由5种最基本的数据结构(String、List、Hash、Set、Sorted Set(zset))bitmap、geohash、hyperloglog、streams 组成。

二、Redis 内部编码

我们常说的 String、List、Hash、Set、Sorted Set(zset)只是对外的编码,实际上每种数据结构都有自己底层的内部编码,而且不止一种,这样就可以在合适的场景下选择合适的内部编码。

内部编码如图(图片纠正:intset编码,而不是inset编码):

《Redis 9 种数据结构简述》

三、5种最基本数据结构

Redis有5种数据结构,分别为:string(字符串)list(列表)set(集合)hash(哈希)zset(有序集合)

Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。不同类型的数据结构的差异就在于 value 的结构不一样。

1. String(字符串)

1.1 内部编码

String 3种内部编码:int、embstr、raw

  • int编码:当一个key的value是整型时,Redis就将其编码为int类型(另外还有一个条件:把这个value当作字符串来看,它的长度不能超过20,保存的是可以用 long 类型表示的整数值)。这种编码类型为了节省内存。Redis默认会缓存10000个整型值(#define OBJ_SHARED_INTEGERS 10000),这就意味着,如果有10个不同的KEY,其value都是10000以内的值,事实上全部都是共享同一个对象。
  • embstr编码:保存长度小于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。
  • raw编码:保存长度大于44字节的字符串(redis3.2版本之前是39字节,之后是44字节)。

int 编码是用来保存整数值,raw 编码是用来保存长字符串,而embstr是用来保存短字符串。其实 embstr 编码是专门用来保存短字符串的一种优化编码,raw 和 embstr 的区别如下图:
《Redis 9 种数据结构简述》
embstr与raw都使用redisObject和sds保存数据,区别在于,embstr的使用只分配一次内存空间(因此redisObject和sds是连续的),而raw需要分配两次内存空间(分别为redisObject和sds分配空间)。因此与raw相比,embstr的好处在于创建时少分配一次空间,删除时少释放一次空间,以及对象的所有数据连在一起,寻找方便。而embstr的坏处也很明显,如果字符串的长度增加需要重新分配内存时,整个redisObject和sds都需要重新分配空间,因此redis中的embstr实现为只读。

PS:Redis中对于浮点数类型也是作为字符串保存的,在需要的时候再将其转换成浮点数类型。

编码的转换:

  • 当 int 编码保存的值不再是整数,或大小超过了long的范围时,自动转化为raw。
  • 对于 embstr 编码,由于 Redis 没有对其编写任何的修改程序(embstr 是只读的),在对embstr对象进行修改时,都会先转化为raw再进行修改,因此,只要是修改embstr对象,修改后的对象一定是raw的,无论是否达到了44个字节。

1.2 应用

string 结构使用非常广泛,最常见的就是缓存用户信息。将用户信息结构体使用JSON序列化成字符串,存入 redis 中。获取时再将 value 反序列化成目标对象。

因为string 类型是二进制安全的,可以用来存放图片,视频等内容,另外由于Redis的高性能读写功能,而string类型的value也可以是数字,可以用作计数器(INCR,DECR),比如分布式环境中统计系统的在线人数,秒杀等。

1.3 数据结构

stringredis 最简单的数据结构,由一个 key – value 组成。

Redis 的字符串是简单动态字符串(SDS),是可以修改的字符串,内部结构实现上类似与 java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M。

1.3.1 什么是简单动态字符串(SDS)

简单动态字符串(SDS),即:simple dynamic string。
Redis 没有直接使用 C 语言传统的字符串表示,而是自己构建了一种简单动态字符串的抽象类型,并将 SDS 用作 Redis 的默认字符串表示。

1.3.2 SDS 的数据结构

struct sdshdr {
// 记录buf数组中已使用字节的数量
// 等于SDS所保存字符串的长度
int len;

// 记录buf数组中未使用字节的数量
int free;

// 字节数组,用于保存字符串
char buf[];
};

SDS 内存块结构
《Redis 9 种数据结构简述》

  • free = 0:表示这个 SDS 没有分配任何未使用空间;
  • len = 4:表示这个 SDS 保存了一个四字节长的字符串;
  • buf:是一个 char 类型的数组,最后一个字节和C语言字符串一样,保存了空字符 ‘\0’。

SDS 遵循 C 语言字符串以空字符结尾的惯例,保存空字符的 1 字节不计算在 SDS 的 len 属性里面,并且为空字符分配额外的 1 字节空间,以及添加空字符到字符串末尾等操作,都是由 SDS 函数自动完成的。

1.3.3 SDS 与 C语言字符串的区别

1.3.3.1 获取字符串长度

C语言字符串
因为C 语言字符串本身不记录长度信息,所以为了获取字符串长度,程序必须遍历整个字符串,直到遇到 \0 结束符为止,所以它获取长度的复杂度为:O(N)。

SDS
从 SDS 的数据结构中可知道,它用 len 字段保存了字符串的长度,所以获取一个 SDS 长度的复杂度为:O(1)。

1.3.3.2 杜绝缓冲区溢出

C语言字符串
strcat 函数可以将 src 字符串中的内容拼接到 dest 字符串的末尾:
char *strcat(char *dest, const char *src)

假设某个程序中有两个在内存中紧邻着的 C字符串 s1 和 s2,其中 s1 保存了字符串 Redis,而 s2 则保存了字符串 MongoDB,如图所示:
《Redis 9 种数据结构简述》
那么在执行 strcat(s1, ‘Cluster’) 时,如果没有为 s1 分配足够的空间,那么在执行 strcat 方法后,s1 的数据将溢出到 s2 所在的空间中,导致 s2 保存的内容被意外地修改。如图:

《Redis 9 种数据结构简述》

SDS
当 SDS API 需要对 SDS 进行修改时,API 会先检查 SDS 的空间是否满足修改所需的要求,如果不满足的话,API 会自动将 SDS 的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以不会出现C语言字符串中的缓冲区溢出问题。

1.3.3.3 减少修改字符串时带来的内存重分配次数

C语言字符串
因为 C 字符串并不记录自身长度,且末尾总是包含 \0 的结束符。所以,每次增长或者缩短一个 C 字符串,程序都要对保存这个 C 字符串的数组进行一次内存重分配操作。

  • 如果程序执行的是增长字符串的操作,那么在执行之前,需要先通过内存重分配来扩展底层数组的大小,不然会产生缓冲区溢出的问题。
  • 如果程序执行的是缩短字符串的操作,那么在执行之后,需要通过内存重分配来释放字符串的空闲空间,不然会产生内存泄漏的问题。

SDS
为了避免 C 字符串存在的问题,SDS 通过未使用空间解除了字符串长度和底层数组长度之间的关联:在 SDS 中,buf 数组的长度不一定就是字符数量加一,数组里面可以包含未使用的字节,而这些字节的数量就由 free 属性记录。
通过未使用空间,SDS 实现了空间预分配惰性空间释放两种优化策略。

1.3.3.3.1 空间预分配

空间预分配用于优化 SDS 的字符串增长操作:当 SDS API 对一个 SDS 进行修改,并且需要对 SDS 进行空间扩展的时候,程序不仅会为 SDS 分配修改所必须的空间,还会为 SDS 分配额外的未使用空间。
预分配的空间大小由以下公式决定:

  • 如果对 SDS 进行修改之后,SDS 的长度小于 1MB。那么程序会分配和 len 属性同样大小的未使用空间。如:修改后,SDS len = 10 字节 <1 MB,那么程序会额外分配多 10 字节。所以最终结果为:10 + 10 + 1 = 21 字节。
  • 如果对 SDS 进行修改之后,SDS 的长度大于 1MB,那么程序将分配 1MB 的未使用空间。

通过空间预分配策略,Redis 可以减少连续执行字符串增长操作所需要的的内存重分配次数。

1.3.3.3.2 惰性空间释放

惰性空间释放用于优化 SDS 的字符串缩短操作:当 SDS API 需要缩短 SDS 保存的字符串时,程序不会立即使用内存重分配来回收缩短后多出来的字节,而是使用 free 属性将这个字节的数量记录起来,并等待将来使用。

1.3.3.4 获取字符串长度

C语言字符串
C 字符串中的字符必须符合某种编码(比如:ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串的结尾。有着这个限制,使得 C 字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

SDS
SDS API 都是二进制安全的,所有的 SDS API 都会以处理二进制的方式来处理 SDS 存在在 buf 数组里的数据。

1.3.3.5 兼容部分 C 字符串函数

虽然 SDS API 都是二进制安全的,但它们一样遵循 C 字符串以空字符结尾的惯例:这些 API 总会将 SDS 保存的数据的末尾设置为空字符,并且总会在为 buf 数组分配空间时多分配一个字节来容纳这个空字符,为的就是让 SDS 可以重用一部分库定义的函数。

1.3.3.5 总结

《Redis 9 种数据结构简述》

1.4 命令使用

如果 value 值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是 signed long 的最大最小值,超过了这个值,Redis 会报错。

> set name code # 设置,成功返回 ok
> get name # 获取,不存在返回 nil
> mset name1 code1 name2 code2 # 批量设置
> mget name1 name2 # 批量获取
> exists name # 是否存在,1:存在;0:不存在
> del name # 删除,1:成功
> expire name 5 # 设置过期时间
> setex name 5 code # 等价于 set + expire
> setnx name code # 如果name不存在,则创建,存在,则创建失败返回 0
> incr name # 对name++
> incrby name 5 # 对name + n

推荐阅读
  • ### 优化后的摘要本学习指南旨在帮助读者全面掌握 Bootstrap 前端框架的核心知识点与实战技巧。内容涵盖基础入门、核心功能和高级应用。第一章通过一个简单的“Hello World”示例,介绍 Bootstrap 的基本用法和快速上手方法。第二章深入探讨 Bootstrap 与 JSP 集成的细节,揭示两者结合的优势和应用场景。第三章则进一步讲解 Bootstrap 的高级特性,如响应式设计和组件定制,为开发者提供全方位的技术支持。 ... [详细]
  • 秒建一个后台管理系统?用这5个开源免费的Java项目就够了
    秒建一个后台管理系统?用这5个开源免费的Java项目就够了 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • 本文探讨了如何在 Java 中将多参数方法通过 Lambda 表达式传递给一个接受 List 的 Function。具体分析了 `OrderUtil` 类中的 `runInBatches` 方法及其使用场景。 ... [详细]
  • oracle c3p0 dword 60,web_day10 dbcp c3p0 dbutils
    createdatabasemydbcharactersetutf8;alertdatabasemydbcharactersetutf8;1.自定义连接池为了不去经常创建连接和释放 ... [详细]
  • MySQL 5.7 学习指南:SQLyog 中的主键、列属性和数据类型
    本文介绍了 MySQL 5.7 中主键(Primary Key)和自增(Auto-Increment)的概念,以及如何在 SQLyog 中设置这些属性。同时,还探讨了数据类型的分类和选择,以及列属性的设置方法。 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 属性类 `Properties` 是 `Hashtable` 类的子类,用于存储键值对形式的数据。该类在 Java 中广泛应用于配置文件的读取与写入,支持字符串类型的键和值。通过 `Properties` 类,开发者可以方便地进行配置信息的管理,确保应用程序的灵活性和可维护性。此外,`Properties` 类还提供了加载和保存属性文件的方法,使其在实际开发中具有较高的实用价值。 ... [详细]
  • 在Java编程中,初始化List集合有多种高效的方法。本文介绍了六种常见的技术,包括使用常规方式、Arrays.asList、Collections.addAll、Java 8的Stream API、双重大括号初始化以及使用List.of。每种方法都有其特定的应用场景和优缺点,开发者可以根据实际需求选择最合适的方式。例如,常规方式通过直接创建ArrayList对象并逐个添加元素,适用于需要动态修改列表的情况;而List.of则提供了一种简洁的不可变列表初始化方式,适合于固定数据集的场景。 ... [详细]
  • 深入解析 Synchronized 锁的升级机制及其在并发编程中的应用
    深入解析 Synchronized 锁的升级机制及其在并发编程中的应用 ... [详细]
  • Flowable 流程图路径与节点展示:已执行节点高亮红色标记,增强可视化效果
    在Flowable流程图中,通常仅显示当前节点,而路径则需自行获取。特别是在多次驳回的情况下,节点可能会出现混乱。本文重点探讨了如何准确地展示流程图效果,包括已结束的流程和正在执行的流程。具体实现方法包括生成带有高亮红色标记的图片,以增强可视化效果,确保用户能够清晰地了解每个节点的状态。 ... [详细]
  • 本文探讨了如何通过编程手段在Linux系统中禁用硬件预取功能。基于Intel® Core™微架构的应用性能优化需求,文章详细介绍了相关配置方法和代码实现,旨在帮助开发人员有效控制硬件预取行为,提升应用程序的运行效率。 ... [详细]
  • 在C++程序中,文档A的每一行包含一个结构体数据,其中某些字段可能包含不同数量的数字。需要将这些结构体数据逐行读取并存储到向量中,随后不仅在控制台上显示,还要输出到新创建的文档B中。希望得到指导,感谢! ... [详细]
author-avatar
康师傅摸-你丶擦_489
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有