1:SDS介绍

我们在redis中执行命令

set key name

的时候,key和name都是字符串类型,而且字符串(string)在redis中是会经常用到的类型,那redis是如何保存字符串的呢?我们接下来往下看

众所周知,redis是c写的,在c中使用char来保存字符串,并且用\0作为字符串的结尾,但是redis不是这样保存的,redis是使用一种叫SDS的结构来保存字符串的。结构如下(redis3.2以前)

 struct sdshdr{
int len;
int free;
char buf[];
}

那么问题来了,redis为什么 会用SDS的结构,而不直接用c语言的字符串,我们来看看他们的区别

1:计算字符串长度的区别

对于c来说,计算字符串的长度的方式就是遍历,遇到\0就停止,所以复杂对是O(n),而SDS直接保存了字符串的长度,复杂度是O(1)

2:保证二进制的安全

因为SDS并不是以\0为结尾的标志,自然就保证了二进制的安全

3:内存管理策略(预分配内存和惰性空间释放策略)

redis是一个高速的缓存数据库,需要频繁的对字符串进行操作,如果内存分配错误,会导致很严重的后果,就算内存分配没问题,频繁的内存分配也是非常耗费时间的,所以这些都是应该去避免的

惰性空间释放策略

在SDS中首先用到了惰性空间释放策略,惰性空间释放用于优化SDS的字符串缩短操作。

当要缩短SDS保存的字符串时,程序并不立即使用内存充分配来回收缩短后多出来的字节,而是使用表头的free成员将这些字节记录起来,并等待将来使用。

源码如下

void sdsclear(sds s) {  //重置sds的buf空间,懒惰释放
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
sh->free += sh->len; //表头free成员+已使用空间的长度len = 新的free
sh->len = 0; //已使用空间变为0
sh->buf[0] = '\0'; //字符串置空
}
预分配内存

扩容策略是字符串在长度小于 1M 之前,扩容空间采用加倍策略,也就是保留 100% 的冗余空间。当长度超过1M 之后,为了避免加倍后的冗余空间过大而导致浪费,每次扩容只会多分配 1M大小的冗余空间。

4:兼容c语言函数库 (字符串后面会自动加上\0)

3.2版本以后的SDS结构

前面的len和free以及char这种结构看起来很好,但是是存在一定的问题的

 struct sdshdr{
int len;
int free;
char buf[];
}

len和free都是int类型,都是4byte也就是32bit,能表示42亿左右的范围,大大的造成了空间的浪费,所以在3.2以后对SDS有一定的更改,更改如下

typedef char *sds;

/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
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 */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
.........

sdshdr5表示的是用5个bit位来表示数据的长度,sdshdr8就是表示用8个bit位来表示数据的长度,以此类推

sdshdr5的内存分配如图



当需要存储的数据长度超过31,就需要用sdshdr8来表示

sdshdr8的内存分配如图

其余的sdshdr16以上的都是以此类推,判断方式源码如下

static inline char sdsReqType(size_t string_size) {
if (string_size < 1<<5) //2^5-1
return SDS_TYPE_5;
if (string_size < 1<<8) //2^8-1
return SDS_TYPE_8;
if (string_size < 1<<16) //2^16-1
return SDS_TYPE_16;
#if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32) //2^32-1
return SDS_TYPE_32;
return SDS_TYPE_64;
#else
return SDS_TYPE_32;
#endif
}

关注我的技术公众号,每周都有优质技术文章推送。

微信扫一扫下方二维码即可关注:

最新文章

  1. 第一章 管理程序流(In .net4.5) 之 实现多线程和异步处理
  2. md RAID
  3. butterknife简化android开发
  4. bzoj2011: [Ceoi2010]Mp3 Player
  5. 2017广东工业大学程序设计竞赛决赛-tmk买礼物
  6. Tomcat修改service.xml性能调优 增加最大并发连接数
  7. CCS中cmd文件的编写
  8. Kali学习笔记22:缓冲区溢出漏洞利用实验
  9. python 脚本之 获取远程主机的hostname
  10. 用groovy脚本进行每日工作的自动化【groovy】
  11. nvm 安装使用
  12. xshell替代工具finalShell
  13. jupyter notebook远程配置
  14. eclipse 代码模板
  15. python3 判断字符串是否为IP
  16. PHP之Composer类库依赖管理神器
  17. Lightoj Halloween Costumes
  18. mac python2.7.10 升级到 3.6
  19. setw()函数使用
  20. maven指定项目的构建、打包和tomcat插件的pom.xml配置

热门文章

  1. Javascript关键字,条件语句,函数及函数相关知识
  2. OsgEarth开发笔记(三):Osg3.6.3+OsgEarth3.1+vs2019x64开发环境搭建(下)
  3. D - D (最短路解决源点到多点,多点到源点的和(有向图))
  4. poj2778 DNA Sequence(AC自动机+矩阵快速幂)
  5. 牛客编程巅峰赛S1第5场 - 青铜&amp;白银 A.凯撒密码(字符串)
  6. Codeforces Round #655 (Div. 2) C. Omkar and Baseball (思维)
  7. Gitlab 快速部署及日常维护 (一)
  8. SpringSecurity认证流程
  9. woj1019 Curriculum Schedule 输入输出 woj1020 Adjacent Difference 排序
  10. spark mllib als 参数