《Redis设计与实现》读书笔记(一)——简单动态字符串(SDS)
《Redis设计与实现》读书笔记(一) ——简单动态字符串(SDS)
(原创内容,转载请注明来源,谢谢)
前言:《Redis设计与实现》,是一本分析redis源码,讲述redis各数据类型结构与实现方式、各操作方式的具体实现等内容。本系列是我对该书学习过程中的读书笔记。
一、概述
Redis中的字符串,是在redis中最为常用的内容,除了redis的字符串数据结构,另外redis其他的数据结构中的子成份,大多也是用字符串的形式存储。
redis的字符串不是直接用c语言的字符串,而是用了一种称为简单动态字符串(SDS)的抽象类型,并将其作为默认字符串。
二、SDS定义
structsdshdr{
int len;
int free;
char buf[];
}
该结构体共三个变量,包括len——sds的长度、free——buf数组中未使用的字节数量、buf——字节数组(保存字符串使用)。
例如,字符串“Redis”的存储方式如下图:
其中,free是0表示申请的内存空间已经都用上,len是5表示长度是5(不计算表示字符串结束的符号 ,这一点和c语言一样),buf里面存储了具体的字符串内容。
三、SDS与C语言字符串的比较
SDS采用结构体的方式存储字符串,而不是采用c语言的开辟一个连续的存储空间的方式。
1、字符串长度计算
C语言如果要获取字符串的长度,需要从第一个字符开始,遍历整个字符串,直到遍历到 符号,时间复杂度是O(N),即字符串的长度。
而redis由于已经存储了字符串的长度,因此,时间复杂度是O(1)。
这样,避免了获取大字符串长度时时间的缓慢。
2、缓冲区
C语言给字符串开辟一个存储空间,如果对此存储空间的使用超过开辟的空间,会导致内存溢出。例如使用字符串拼接等方式时,就很容易出现此问题。而如果每次拼接之前都要计算每个字符串的长度,时间上又要耗费很久。
redis的SDS中内置一个sdscat函数,也是用于字符串的拼接。但是在执行操作之前,其会先检查空间是否足够,通过比较当前字符串的free与即将拼接字符串的len的大小,就知道是否可以拼接。如果free的值不够,会再申请内存空间,避免溢出。
3、修改字符串时的内存重分配
C语言的字符串长度和底层数组之间存在关联,因此字符串长度增加时需要再分配存储空间,避免溢出;字符串长度减少时,需要释放存储空间,避免内存泄漏。
由于redis中,字符串频繁修改是很经常发生的事情,redis的一个应用场景就是变量频繁修改的场景。为了避免C语言的不断重分配空间,redis进行了改进。
redis的sds,主要是通过free字段,来进行判断。通过未使用空间大小,实现了空间预分配和惰性空间释放。
1)空间预分配
空间预分配用于优化字符串的增长操作,实现方式为:当需要增长字符串时,sds不仅会分配足够的空间用于增长,还会预分配未使用空间。
分配的规则是,如果增长字符串后,新的字符串比1MB小,则额外申请字符串当前所占空间的大小作为free值;如果增长后,字符串长度超过1MB,则额外申请1MB大小。
例如,字符串增长后,大小是50kb,则额外申请50kb作为未使用空间。如果字符串增长后的大小是20mb,则额外申请1mb作为未使用空间。以上两种情况都为将 计算在内,因此,实际上还会需要1字节作为 存放的空间。
上述机制,避免了redis字符串增长情况下频繁申请空间的情况。每次字符串增长之前,sds会先检查空间是否足够,如果足够则直接使用预分配的空间,否则按照上述机制申请使用空间。该机制,使得字符串增长n次,需要申请空间的次数,从必定为n次的情况,降为最多n次的情况。
2)懒惰空间释放
懒惰空间释放用于优化sds字符串缩短的操作,实现方式为:当需要缩短sds的长度时,并不立即释放空间,而是使用free来保存剩余可用长度,并等待将来使用。当有剩余空间,而有有增长字符串操作时,则又会调用空间预分配机制。
当redis内存空间不足时,会自动释放sds中未使用的空间,因此也不需要担心内存泄漏问题。
4、二进制安全
C语言的字符必须符合某种编码,例如ascii,且字符串除了末尾之外,不能有空格,否则会被当作是另一个字符串。这些限制使得c语言的字符串只能保存不含空格的文本,不能保存图片、视频等二进制数据,也不能保存包含空格的文本。
而保存图片、大段文本等内容,也是redis的常用场景。因此,redis也对此进行优化。因此,sds是二进制安全的,写入的是什么内容,返回的也是什么内容,并没有限制。
redis的sds,用buf保存字符串,保存的就是一系列的二进制数据。因为,sds考虑字符串长度,是通过len属性,而不是通过 来判断。
5、C语言字符串函数
redis兼容c语言对于字符串末尾采用 进行处理,这样使得其可以复用部分c语言字符串函数的代码,实现代码的精简性。
6、总结——C语言字符串和SDS字符串比较
C字符串 |
Redis SDS |
---|---|
获取字符串长度时间复杂的O(n) |
获取字符串长度时间复杂的O(1) |
字符串长度增加可能造成缓冲区溢出 |
字符串长度增加不会造成缓冲区溢出 |
修改字符串长度n次,必然n次内存重分配 |
修改字符串长度n次,最多n次内存重分配 |
只保存不含空格文本 |
保存任意二进制数据和文本数据 |
可以使用<string.h>库所有函数 |
可以使用部分<string.h>库的函数 |
四、总结
redis只有部分情况下使用c语言的字符串形式用作字符串,如给用户返回的信息、报错信息、提示信息等,只有不会被改动的字符串,才会直接使用c语言的字符串形式。否则,大部分情况下,redis都是使用sds作为字符串的存储方式。
——written by linhxx 2017.08.28
- Google官方网页载入速度检测工具PageSpeed Insights 使用教程
- ASP.NET 路由
- Kafka定时清除过期数据
- 腾讯高级副总裁郭凯天:打造腾讯智库分析互联网产业前沿问题
- Google Chrome 浏览器 开发者工具 使用教程
- 反向代理(Reverse Proxy)及 IIS 7 应用请求路由模块
- 2014腾讯“大数据连接的未来”高峰论坛在京召开
- 工作流、业务流程管理和SOA
- 面向对象设计的SOLID原则
- 用psake来简化自动化脚本的构建
- TESLA V100如何让质疑GPU的流言“失声”
- Web 前端性能优化相关内容解析
- Service Broker 无法工作的问题修复
- .NET代码快速转换成powershell代码
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- (建议收藏)Java基础知识笔记二(详细)
- Android 手机如何拍摄RAW图
- 「干货」基本数据类型和引用数据类型的区别
- int 和 integer :装箱和拆箱的过程,会用到什么方法,你觉得这个会对性能有影响吗,原因是什么(百度一面)
- 数组:这个循环可以转懵很多人!
- 企业远程办公视频会议系统EasyRTC-SFU下侧边栏边框超限问题如何解决?
- 编写高质量可维护的代码:数据建模
- 新版企业远程办公视频通话系统EasyRTC-SFU,如何解决用户登录信息更新不及时的问题?
- 服务应用突然宕机了?别怕,Dubbo 帮你自动搞定服务隔离!
- 33.Python字符串方法find以及与序列解包的技巧结合
- 代码审计从0到1 —— Centreon One-click To RCE
- 一文带你深扒ClassLoader内核,揭开它的神秘面纱!
- 小知识:OGG的TRANLOGOPTIONS MINEFROMACTIVEDG参数
- oracle转postgreSQL修改点
- 重学数据结构(三、队列)