当前位置: 首页 > news >正文

redis源码分析之底层数据结构(一)-动态字符串sds

1.绪论

我们知道redis是由c语言实现的,c语言中是自带字符串的,但是为什么redis还要再实现自己的动态字符串呢,这种动态字符串的底层数据结构是怎样的呢?接下来我们带着这些问题来看一看redis中的动态字符串sds。

2.sds的组成

struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {//字符数组的长度uint16_t len; /* used *///整个sds字符串的大小uint16_t alloc; /* excluding the header and null terminator *///表示是5种sds字符串中的哪一种unsigned char flags; /* 3 lsb of type, 5 unused bits *///真正存储数据的地方char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};

可以看出redis中sds是一个动态数组,它由长度+sds占据内存大小+sds的类型+加一个数组组成。如果用图表示如下:

可以看出redis的sds和java中的ArrayList是类似的。

3.sds的优点

为什么redis需要重新实现一个字符串呢?主要有如下的几点考虑:

3.1.常数时间复杂度获取字符串长度

普通字符串以'\0'结尾,而sds存取了整个字符串占据多少个字符。所以普通字符串需要用o(n)的复杂度获取到字符串长度,而sds以o(1)的复杂度获取到字符串长度;

3.2.存储特殊符号\0

sds能够存储特殊符号\0',当时c语言原生的字符串以'\0'结尾,不能存储\0,保证二进制安全;

3.3.内存动态分配,防止杜绝缓冲区溢出

c语言原生字符串,内存一但分配,大小便固定,比如在调用'append key'命令的时候,会实现字符串拼接的功能,如果超出缓冲器大小,会超出分配内存大小而报错;
但是sds实现了动态扩展的功能,在拼接前,会检查内存是否够用,如果不够用,便会进行动态扩容,而如果数组的剩余空间过多,便会进行缩容。

3.3.1 动态扩容

当新的字符串占用空间超出分配内存空间时,会进行动态分配,并且会提前考虑预分配一部分空间,防止内存的频繁分配问题。

3.3.2 动态缩容 

当已使用内存小于分配内存的部分比例时,会进行动态缩容,并且采用惰性释放的策略,不使用的数据并不会立即清除,而是等待有新的字符串写入的时候进行覆盖。

3.4.节约内存

在高版本的redis中,将存储字符数值分成了5类,分别是sdshdr5 、sdshdr8 、sdshdr16 、sdshdr32 、sdshdr64 ,redis会根据存储的字符内容来判断采用哪个中字符串尽显存储数据。比如如果用户写入的字符串只包含abcd这种英文字母,每个字符串用一个字节便能存储。sds便会考虑采用sdshdr8来进行存储.

4.总结

可以看出redis的sds其实就相当于java中的ArrayList,都具有动态扩容,缩容等功能。

5.参考

[1] 黄建宏 redis设计与实现

[2] https://juejin.cn/book/7144917657089736743/section/7144917738698326019

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Git】取消追踪多个文件或目录
  • Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules。Android studio纯java代码报错
  • 使用Spring Boot和自定义缓存注解优化应用性能
  • Linux内存管理--系列文章柒——硬件架构
  • ELK集群搭建
  • 28个常用的损失函数介绍以及Python代码实现总结
  • React -- useState状态更新异步特性——导致获取值为旧值的问题
  • 前端工程化(01):10款自动化构建工具初识。
  • [GHCTF 2024 新生赛]ezzz_unserialize
  • 攻防世界 Web_python_template_injection(flask模版注入)
  • 网络安全应急响应信息收集利器-Eagle_Eye
  • Java中Collection集合和Map集合详解(进阶三)
  • sql注入之宽字节注入
  • WEB攻防-通用漏洞SQL注入-ACCESS一般注入与偏移注入
  • 【Scrapy】深入了解 Scrapy 中间件中的 process_spider_output 方法
  • [数据结构]链表的实现在PHP中
  • 2017 前端面试准备 - 收藏集 - 掘金
  • Apache的基本使用
  • es6--symbol
  • Fabric架构演变之路
  • Javascript基础之Array数组API
  • Java新版本的开发已正式进入轨道,版本号18.3
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • node-glob通配符
  • node学习系列之简单文件上传
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • QQ浏览器x5内核的兼容性问题
  • 蓝海存储开关机注意事项总结
  • 如何在 Tornado 中实现 Middleware
  • 用jQuery怎么做到前后端分离
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 再次简单明了总结flex布局,一看就懂...
  • ​比特币大跌的 2 个原因
  • ​卜东波研究员:高观点下的少儿计算思维
  • # 计算机视觉入门
  • #LLM入门|Prompt#2.3_对查询任务进行分类|意图分析_Classification
  • (02)Unity使用在线AI大模型(调用Python)
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (2)STL算法之元素计数
  • (26)4.7 字符函数和字符串函数
  • (第30天)二叉树阶段总结
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)springboot 基于HTML5的个人网页的网站设计与实现 毕业设计 031623
  • (几何:六边形面积)编写程序,提示用户输入六边形的边长,然后显示它的面积。
  • (四)React组件、useState、组件样式
  • (转)memcache、redis缓存
  • (转)Unity3DUnity3D在android下调试
  • (转)我也是一只IT小小鸟
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .NET 给NuGet包添加Readme
  • .NET 设计一套高性能的弱事件机制
  • .net6 core Worker Service项目,使用Exchange Web Services (EWS) 分页获取电子邮件收件箱列表,邮件信息字段
  • .NET使用存储过程实现对数据库的增删改查