百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Redis底层数据结构有了解过吗(redis底层原理该如何回答)

mhr18 2024-11-10 09:46 23 浏览 0 评论

Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢?

将一些底层的数据结构及实现原理记录下来。本文将介绍 Redis 中最基础的 字符串 的实现方法。 它是Redis的字符串键的主要实现方法.
颤抖吗?骚年

定义

Redis 是使用 C 语言实现的,但是 Redis 中使用的字符串却不是直接用的 C 语言中字符串的定义,而是自己实现了一个数据结构,叫做 SDS(simple dynamic String), 即简单动态字符串。

Redis 中 SDS 数据结构的定义为:

 struct sdshdr{
    int len;
    int free;
    char buf[];
}
  • len=5, 说明当前存储的字符串长度为 5.
  • free=0, 说明这个结构体实例中,所有分配的空间长度已经被使用完毕。
  • buf 属性是一个 char 类型的数组,保存了实际的字符串信息。

带有 free 空间的 SDS 示例:

可以看到 len 属性和 buf 属性的已使用部分都和第一个示例相同,但是 free 属性为 5, 同时 buf 属性的除了保存了真实的字符串内容之外,还有 5 个空的未使用空间 ('\0'结束字符不在长度中计算).

优劣

Redis 为什么要这么做呢,或者说使用 SDS 来作为字符串的具体实现结构,有什么好处呢?

那么就不得不提 C 语言本来的字符串了。

C 语言的字符串定义,是使用和字符串相等长度的字符数组来存储字符串,并且在后面额外加一个字符来存储空字符'\0'.

这种实现方式的优点就是,简单且直观。但是众所周知,Redis 是一个性能极强的内存数据库,这种实现方式并不能满足 Redis 的性能要求,当然,同时也有一部分的功能性要求无法满足。

后面讲述的每一条优点,都是相对于 C 语言字符串而言的,具体的特性再具体分析。

高性能获取字符串长度

从 C 语言字符串的结构图中,我们可以看到,如果我们想获取一个字符串的长度,那么唯一的办法就是遍历整个字符串。遍历操作需要 O(N) 的时间复杂度。

而 SDS 记录了字符串的长度,也就是 len属性,我们只需要直接访问该属性,就可以拿到当前 SDS 的长度。访问属性操作的时间复杂度是 O(1).

Redis 字符串数据结构的 求长度的命令 STRLEN. 内部即应用了这一特性。无论你的 string 中存储了多长的字符串,当你想求出它的长度时,可以随意的执行 STRLEN, 而不用担心对 Redis 服务器的性能造成压力。

杜绝缓冲区溢出

C 语言的的字符串拼接函数,strcat(*desc, const char *src), 会将第二个参数的值直接连接在第一个字符串后面,然而如果第一个字符串的空间本就不足,那么此时就会产生缓冲区溢出。

SDS 记录了字符串的长度,同时在 API 实现上杜绝了这一个问题,当需要对 SDS 进行拼接时,SDS 会首先检查剩余的未使用空间是否足够,如果不足,会首先扩展未使用空间,然后进行字符串拼接。

因此,SDS 通过记录使用长度及未使用空间长度,以及封装 API, 完美的杜绝了在拼接字符串时容易造成缓冲区溢出的问题。

减少修改字符串产生的内存分配次数,提高修改字符串性能

上面提到,C 语言的字符串实现,是一个长度永远等于 字符串内容长度+1 的字节数组。那么也就意味着,当字符串发生修改,它所占用的内存空间必须要发生更改。

  • 字符串变长。需要首先扩展当前字符串的字节数组,来容纳新的内容。
  • 字符串变短。在修改完字符串后,需要释放掉空余出来的内存空间。

内存分配是比较底层的实现,其中实现比较复杂,且可能执行系统调用,通常情况下比较耗时,Redis 怎么进行对应的优化呢?

  • 空间预分配

SDS 在进行修改之后,会对接下来可能需要的空间进行预分配。这也就是 free 属性存在的意义,记录当前预分配了多少空间。

分配策略:

  1. 如果当前 SDS 的长度小于 1M, 那么分配等于已占用空间的未使用空间,即让 free 等于 len.
  2. 如果当前 SDS 的长度大于 1M, 那么分配 1M 的 free 空间。

在 SDS 修改时,会先查看 free属性的值,来确定是否需要进行空间扩展,如果不需要就直接进行拼接了。

通过预分配策略,SDS 连续增长 N 次,所需要的内存分配次数从绝对 N 次,变成了最多 N 次。

  • 惰性释放内存

当 SDS 进行了缩短操作,那么多余的空间不着急进行释放,暂时留着以备下次进行增长时使用。

听起来预分配和惰性释放是不是很简单的道理?本质上也是使用空间换取时间的操作。而且可能发现了其中的一个问题,那就是在内存紧张的机器上,这样浪费真的好吗?

这个问题,Redis 当然考虑到了,SDS 也提供了对应的 API, 在需要的时候,会自己释放掉多余的未使用空间。

二进制安全

Redis 的字符串是二进制安全的这个特性,但是它为什么可以做到二进制安全呢?

C 语言的字符串不是二进制安全的,因为它使用空间符'\0'来判断一个字符串的结尾。也就是说,假如你的字符串是 wed\0aa\0 嘘嘘、0, 那么你就不能使用 C 语言的字符串,因为它识别到第一个空字符'\0'的时候就结束识别了,它认为这次的字符串值是'wed\0'.

而二进制中的数据,我们谁也说不好,如果我们存储一段音频序列化后的数据,中间肯定会有无数个空字符,这时候怎么 C 语言的字符串就无能为力了。

而 SDS 可以,虽然 SDS 中也会在字符串的末尾储存一个空字符,但是它并不以这个空字符为判断条件,SDS 判断字符串的长度时使用 len属性的,截取 字节数组 buf 中的前 len 个字符即可。

因此,在 SDS 中,可以存储任意格式的二进制数据,也就是我们常说的,Redis 的字符串是二进制安全的。

兼容部分 C 语言的库函数

上面提到,SDS 使用 len 属性的长度来判断字符串的结尾,但是,却依然遵循了 C 语言的惯例,在字符串结尾的地方填充了一个空字符'\0'.

这样做可以在处理一些纯文本的字符串时,可以方便的沿用一些 C 语言的库函数,而不是自己重新为 SDS 进行开发库函数。

总结

Redis 中使用字符串的大多数场景(键的字符串,字符串数据结构的实际值存储等等)下,都不使用 C 语言的字符串,而是使用 SDS. 简单动态字符串。

它的实现方式是:一个字节数组 buf, 一个当前字符串长度的记录属性 len, 一个当前未使用空间长度属性 free. 字节数组的长度不要求绝对等于字符串值的真实长度,会有一定的缓冲。

相对于 C 语言的字符串,SDS 的优势如下:

(此处已添加小程序,请到今日头条客户端查看)

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: