一文带你快速搞懂动态字符串SDS,面试不再懵逼
Posted chenchen0618
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文带你快速搞懂动态字符串SDS,面试不再懵逼相关的知识,希望对你有一定的参考价值。
目录
redis源码分析系列文章
前言
上篇我们已经了解了Redis是什么,在Linux上如何安装,常见的数据类型和API使用,如果有不明白的,可以移步到主页。
Redis是使用C写的,而C中根本不存在string,list,hash,set和zset这些数据类型,那么C是如何将这些数据类型实现出来的呢?我们从该篇开始,就要开始分析源码啦??。
API使用
我们这篇来学习string的底层实现,首先看下API的简单应用,设置str1变量为helloworld,然后我们使用debug object +变量名的方式看下,注意标红的编码为embstr。
如果我们将str2设置为helloworldhelloworldhelloworldhelloworldhell,字符长度为44,再使用下debug object+变量名的方式看下,注意标红的编码为embstr。
但是当我们设置为helloworldhelloworldhelloworldhelloworldhello,字符长度为45,再使用debug object+变量名的方式看下,注意标红的编码为raw。
最后我们将str3设置为整数100,再使用debug object+变量名的方式看下,注意标红的编码为int。
所以Redis的string类型一共有三种存储方式,当字符串长度小于等于44,底层采用embstr;当字符串长度大于44,底层采用raw;当设置是整数,底层则采用int。
embstr和raw的区别
所有类型的数据结构最外层都是RedisObject,这部分会说,先这样大致了解下,因为这篇的重点不在这。如果字符串小于等于44,实际的数据和RedisObject在内存中地址相邻,如下图。
如果字符串大于44,实际的数据和RedisObject在内存中地址不相邻,如下图。
再次强调,这些不重要,以后会讲,现在提下,只是为了能让Redis的String类型有个大致了解,先从整体把握。我们今天要说的其实是实际的数据,即上图指针指向的位置??。
SDSHdr的定义
其实的数据并不是直接存储,也有封装,看下面的代码就知道分为五种,分别是sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64。sdshdr5和另外四种的区别比较明显,sdshrd5其实对内存空间的更加节约。其他四种乍一看都差不多,包括已用长度len,总长度alloc,标记flags(感觉没啥用,要是有知道的小伙伴,欢迎指教),实际数据buf。
SDS具体逻辑图
假设我们设置某个字符串为hello,那么他SDS的可用长度len为8,已用长度len为6,如下图。注意:Redis会根据具体的字符长度,选择相应的sdshdr,但是各个类型都差不多,所以下图加简单画了。
SDS的优势
我们可以看到是对字符数组的再封装,但是为什么呢,直接使用字符数组不是更简单吗?这要从C和Java语言的根本区别说起。
更快速的获取字符串长度
我们都知道Java的字符串有提供length方法,列表有提供size方法,我们可以直接获取大小。但是C却不一样,更偏向底层实现,所以没有直接的方法使用。这样就带来一个问题,如果我们想要获取某个数组的长度,就只能从头开始遍历,当遇到第一个‘ ‘则表示该数组结束。这样的速度太慢了,不能每次因为要获取长度就变量数组。所以设计了SDS数据结构,在原来的字符数组外面增加总长度,和已用长度,这样每次直接获取已用长度即可。复杂度为O(1)。
数据安全,不会截断
如果传统字符串保存图片,视频等二进制文件,中间可能出现‘ ‘,如果按照原来的逻辑,会造成数据丢失。所以可以用已用长度来表示是否字符数组已结束。
SDS关键代码分析
获取常见值(抽象出常见方法)
在sds.h中写了一些常见方法,比如计算sds的长度(即sdshdr的len),计算sds的空闲长度(即sdshdr的可用长度alloc-已用长度len),计算sds的可用长度(即sdshdr的alloc)等等。但是大家有没有疑问,这不是一行代码搞定的事吗,为啥要抽象出方法呢?那么问题在于在上面,我们有将sdshdr分为五种类型,分别是sdshdr5,sdshdr8,sdshdr16,sdshdr32,sdshdr64。那么我们在实际使用的时候,想要区分当前是哪个类型,并取其相应字段或设置相应字段。
创建对象
我们通过sdsnew方法来创建对象,显示通过判断init是否为空来确定初始大小,接着调用方法sdsnew(这边方法名一样,但是参数不一样,其为方法的重载),先根据长度确定类型(上面有提过五种类型,不记得的可以往上翻),然后根据类型分配相应的内存资源,最后追加C语言的结尾符‘ ‘。